리눅스 커널은 C 언어
표준만으로 작성되어 있지 않다. 인라인 어셈블리
, 다양한 GCC 확장 속성(함수, 자료형, 레이블, etc.)
등을 통해 구현되어 있다. 위 사진은 arch/x86/include/asm/vdso/gettimeofday.c
의 파일 내용(커널 버전 5.12.2) 이다. 보면 알겠지만, 분명 C 언어
로 작성되어 있지만 이질감이 느껴진다. 특히 57
행의 __attribute__
와 69
행의 __always_inline
은 확실하게 비표준
이다. 이러한 속성(Attributes
) 들은 모두 GCC
에서 제공해주는 기능으로 컴파일러에게 코드에 대한 약간에 힌트를 준다. 이러한 힌트를 바탕으로 컴파일러는 더 좋은 결과물을 만들어 낼 수 있다.
리눅스 커널은 속성 지정자(attribute specifier
)를 제공하며 다음의 형태를 가진다: __attribute__((attribute-list))
. 이는 마치 지정자(specifier)
와 같이 동작한다. 대표적인 C 표준 지정자로 extern
, unsigned
등이 있다. attribute-list
에는 다음과 같은:
- Type 속성
- Function 속성
- Label 속성
- Enumerator 속성
- Statement 속성
종류가 있다.
리눅스 커널에서는 이러한 다양한 속성의 지정자를 미리 매크로로 정의해두었다:
이는 include/linux/compiler_attributes.h
헤더파일의 내용 중의 일부이다. 이 밖에도 다양한 매크로가 있기 떄문에 한번쯤 살펴 보는 것을 추천한다.
Type 속성
을 통해 데이터 타입에 속성을 부여할 수 있다.
// 일반적인 변수의 선언 int x = 0; // __attribute__ 를 이용한 속성 지정: 16 바이트 단위로 정렬 int y __attribute__((aligned(16))) = 0;
위 예제는 일반적인 C
의 Basic type
이였으나, 구조체(User-defined data type
)에도 적용이 가능하다:
struct user_defined { int data_type[10]; } __attribute__((aligned(16)));
이렇게 16 바이트 단위로 정렬하라는 속성을 주는 것이 가능하다. 구조체의 aligning
은 undefined
이기 때문에 이러한 속성은 undefined behavior (UB)
를 없애고, 컴파일러의 동작 방식을 구체적으로 명시해주는 장점이 있다.
Function 속성
은 함수에 속성을 부여한다.
void __f(void) { /* do something */ } void f(void) __attribute__((weak, alias("__f")));
위 명세(alias
)는 함수에 대한 별칭을 생성하며 이는 반드시 지정되어야 한다.
weak
속성은 함수를 전역대신 약한 기호로 방출한다. 보통 사용자 코드에서 재정의 될 수 있는 라이브러리 함수에 대해 선언 되어진다.
Label 속성
은 레이블(분기 위치)에 대해 속성을 부여한다.
대표적으로 unused
, hot
, cold
등이 존재한다.
unused
: 이 특징은 프로그램이 생성한 코드가 사용하지 않는 레이블을 포함할 수도 있다는 것을 알리며, 이는 -Wall
과 함께 컴파일 되어진다. 사람이 작성한 코드에 사용하는 것은 부적합할지라도, #ifdef
조건을 포함하는 레이블로 분기하는 코드의 경우 유용할 수 있다.hot
: 레이블에 붙은 hot
속성은 컴파일러에게 다른 레이블의 경로보다 해당 레이블의 경로를 따라갈 가능성이 높은 것을 알리는데 사용되어진다.cold
: 레이블에 붙은 cold
속성은 다른 레이블에 비해 더 실행될 가능성이 적다는 것을 알린다. Enumerator 속성
은 열거형에 대한 속성을 지정한다. 여기에는 현재(현재 필자의 GCC 버전인 9.3.0 을 기준으로) deprecated
속성 밖에 없다.
enum E { oldval __attribute__((deprecated)), newval };
deprecated
속성은 소스 파일 내에서, 해당 속성이 지정된 열거형 사용 시 경고를 발생 시킨다. 이후 버전 프로그램에서 제거되어야 하는 열거형을 나타낼 때 유용하다.
Statement 속성
은 구문에 대해 속성을 부여한다. Enumerator 속성
과 마찬가지로 아직 fallthrough
속성 밖에 없다.
switch (cond) { case 1: bar(1); __attribute__((fallthrough)); case 2: ...
말 그대로 fallthrough (빠져버리는) 구문이다. 이 속성이 지정되면 컴파일러에게 다른 case
레이블로 빠져버릴 수 있다는 것과, switch 문
이 의도적으로 빠질 수 있게 설계되어 있으므로, -Wimplicit-fallthrough
경고가 필요 없다는 것을 알린다.
그 밖에도 다양한 속성들이 있지만 내용이 너무 방대하여 설명과 속성들을 일부 생략하였다. 자세한 내용은 필자가 아래에 적어둔 출처
를 통해서 그 내용을 확인하라.
[사이트] https://gcc.gnu.org/onlinedocs/gcc-9.3.0/gcc/C-Extensions.html#C-Extensions
[책] 리눅스 커널 소스 해설: 기초입문 (정재준 저)
[책] C: A Reference Manual (Fifth Edition) (Samuel P. Harbison III, Guy L. Steele Jr. 저)
[책] C Programming: A Modern Approach (Second Edition) (K.N.KING 저)