#Software Engineering Aspects
#Local vs Global Symbols
- 링커의 관점에서, 개별 .o 파일의 기호는 전역적이거나 로컬이다
- Assembly level: default값은 local이다. 그렇지 않으면 .globl로 state해야함
- C level: default값은 global이다. 로컬로 만들려면 static으로 state해야함
- 참고: 로컬/글로벌 변수와 로컬/글로벌 변수의 다르게 사용가능. 여기서 "local"은 컴파일 유닛에 대한 로컬, 즉 .c 파일(헤더 포함)을 의미한다
- 서로 다른 컴파일 단위의 로컬 기호가 분리되고 서로 충돌하거나 다른 단위의 글로벌 기호와 충돌하지 않는다
#Conflict Resolution Rules for Global Symbols
만약 2개 이상의 모듈이 동일한 이름의 전역 기호를 정의하면 어떻게 될까?
- 답: 기호가 "strong"으로 간주되는지 "weak"으로 간주되는지에 따라 다르다
- strong + strong → 충돌 "잘못 정의됨"
- strong + weak → weak definition은 무시됨
- weak + weak → 둘중에 하나의 weak definition이 사용됨
- 보면서 느꼈을 수도있겠지만 이러한 규칙들은 굉장히 이상하다
(포트란의 공통 블록 탓).다행스럽게도, 기호를 "weak" 으로 만드는 일반적인 단 한 가지 경우가 있다:- 초기화되지 않은 전역 변수 정의 (예: int x; 또는 struct struct type obj)
- 따라서 서로 다른 컴파일 유닛에서 동일한 전역 변수를 여러 번 정의할 수 있고 링커가 다른 방향으로 회전할 수 있다.
#Understanding Definitions and Declarations in C
#Effect of Definitions and Declarations in a Header File
#Best Practices - Variables
- 가능한 경우 전역 변수를 사용하지 않는게 좋다. 그러나 변수를 사용해야 하는 경우:
- 정적 여부에 관계없이 헤더 파일에 전역 변수 정의 하면안됨
- 대신 정확히 하나의 헤더 파일(외부 파일 포함)에 선언하고 이들을 정의할 하나의 .c 파일을 선택하는게 좋다(이 파일들은 모듈이 소유한다고 하는 것과 동일한 기본 이름을 갖는 경우가 많다)
- 변수에 대한 초기 값이 제공되었는지 여부에 관계없이 이 작업 수행
- C++는 One definition rule까지 필요로한다
- 둘 이상의 .c 파일에 사용되지 않는 한 .c 파일에 정적 전역 변수를 정의하지 않는게 좋다.
- 여러 파일이 동일한 이름을 정의하면 strong 정의가 충돌하고 weak 정의는 strong/weak 조합과 동일한 복사본을 자동으로 참조한다
- 대신 정적으로 설정한다 – 캡슐화 극대화
#Best Practices - Functions
- 둘 이상의 .c 파일에 사용되지 않는 경우 static 파일을 만들고 .c 파일에 보관한다
- 둘 이상의 .c 파일에 사용되는 경우 헤더 파일에 프로토타입 선언을 배치하는게 좋다. -W missing-protype을 사용하여 이 선언을 적용한다.
- “implicit declaration” 경고 무시 하지 않기
- file.c의 함수에 적합한 파일 이름 선택.
- 컴파일러가 헤더 파일에 인라인할 작은 함수 정의
#Best Practices - Inline Functions
- 인라인: 컴파일러는 호출 사이트에 함수 본문을 삽입하여 프로시저 호출 오버헤드를 방지하고 최적화를 가능하게 한다
- 컴파일러가 함수의 소스 코드에 액세스할 수 있어야 함. 따라서 헤더 파일에 대한 정의가 필요하다. 과도한 사용은 컴파일 시간을 증가시킨다.
- 컴파일러는 선택된 최적화 수준과 휴리스틱을 기반으로 인라인 여부를 결정한다.
- 그렇다면 어떤 modifier를 사용해야할까?
- 옵션 1: 정적 또는 정적 인라인. 인라인 추가는 좋은 방법이지만 컴파일러가 실제로 인라인으로 이동하거나 강제하지는 않는다
- 옵션 2: (C99 이후부터 사용가능) 일반 인라인. 헤더 파일에서 인라인으로, 외부 인라인 선언을 추가할 컴파일 유닛을 정확히 한 개 선택한다.
- 옵션 2는 컴파일러가 인라인하지 않을 경우 여러 복사본을 피할 수 있지만 더 복잡하며 헤더 전용 라이브러리를 허용하지 않는다는 장점이 있다.
#Conclusion
- 이 포스트에서는 .c 소스 및 .h 헤더 파일에 선언 및 정의를 배치하기 위한 모범 사례를 알아봄
- 링커 오류 및 취약한 관행 방지/디버깅
- 새로운 대안: 전체 프로그램 최적화 기법
- 링크-시간 최적화(LTO): 컴파일러는 .o 파일에 중간 표현을 저장하며, 최적화 및 코드 생성은 전체 프로그램에서 링크 시간에 수행됨
- 여러 파일의 소스 코드 연결(소위 "통합 빌드/ unity build"라고 함)
'Computer Science > Computer Systems' 카테고리의 다른 글
[Lecture 8] 멀티쓰레딩 II - Basic Locking / Managing Shared State (0) | 2022.10.08 |
---|---|
[Lecture 6] Linking and Loading - Part III (0) | 2022.09.22 |
[Lecture 6] Linking and Loading - Part I (0) | 2022.09.22 |
[P1] The Customizable Shell (0) | 2022.09.20 |
[Lecture 4] Implementing Job Control Shells (0) | 2022.09.16 |