Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

9365
557
421924


[Linux][Kernel] __init 매크로 (1) 부록

코드 리뷰를 하다 보면 함수 앞에 __init 코드를 종종 볼 수 있습니다. 예를 들면 lockup_detector_init란 함수 옆에 보이는 __init 구문이죠.
void __init lockup_detector_init(void)
{
set_sample_period();

구글링으로 __init 매크로를 검색하면 __init 매크로를 함수에 추가하면 커널이 부팅할 때 한번 호출된다는 정보를 알 수 있죠. 여기서 멈추지 말고 조금 더 __init 매크로에 대해 분석해볼까요? 이 과정에서 조금 더 유익한 디버깅 정보를 얻을 수 있거든요.

그럼 다음 코드를 한번 같이 볼까요? 이번에는 init_workqueues 함수입니다. 역시 init_workqueues 함수 앞에 __init 매크로가 붙어 있습니다. 참고로 init_workqueues 함수는 이름과 같이 워크큐 초기화를 담당하고 있습니다.
[kernel/workqueue.c]
static int __init init_workqueues(void)
{
int std_nice[NR_STD_WORKER_POOLS] = { 0, HIGHPRI_NICE_LEVEL };
int i, cpu;

예전에 C 코드를 보다가 조금 이라도 매크로에 의문이 생기면 전처리 파일을 열어 보는게 효율적이라고 했죠? 이번에도 해당 파일 [kernel/workqueue.c]에 대응하는 전처리 파일을 함께 볼까요?

[kernel/workqueue.c] 파일을 컴파일하면 [kernel/.tmp_workqueue.i] 이름으로 전처리 파일이 생성됩니다. 이점 유념하시고 이 파일을 열어 보면 다음과 같은 init_workqueues 함수가 보일 겁니다.
static int __attribute__ ((__section__(".init.text"))) __attribute__((__cold__)) __attribute__((no_instrument_function)) init_workqueues(void)
{
 int std_nice[NR_STD_WORKER_POOLS] = { 0, HIGHPRI_NICE_LEVEL };
 int i, cpu;

위 전처리 코드를 보면 __init이란 매크로가 다음 코드로 치환되는 것을 알 수 있습니다. 
평소 C 코드에서 볼 수 없는 이상한 암호 같은 코드가 보이네요.
__init = __attribute__ ((__section__(".init.text"))) __attribute__((__cold__)) __attribute__((no_instrument_function))

그럼 어느 코드에서 __init이란 매크로를 설정하는지 알아볼까요? 코드를 검색하니 __init이란 매크로는 [include/linux/init.h] 파일에 다음과 같이 정의돼 있군요. 
#define __init          __section(.init.text) __cold notrace __latent_entropy

매크로에서 매크로를 다시 호출하는 구조네요. 그런데 여기부터 머리가 아파집니다. 이상한 어셈블리 명령어 같은 문자가 보이네요. 하지만 여기서 분석을 멈추면 남는 게 아무것도 없어요. 사실 생소해 보이는 것뿐이지 코드를 차근차근 알아가면 그리 어렵지도 않거든요. 낯선 코드라도 조금 더 집중하면서 코드의 의미를 살펴볼까요? 


코드 리뷰
__section(.init.text) 분석
include/linux/compiler.h 파일을 열어보면 다음과 같이 # 매크로를 써서 __section(S)을 구현했는데요. 
# define __section(S) __attribute__ ((__section__(#S)))
 
__section(.init.text) 이 코드에서 입력이 .init.text이므로 #S 대신 .init.text가 치환되면 다음 코드가 되겠죠. 잘 이해가 안 가는 분은 이번 장에서 다룬 # 매크로 기법에 대한 내용을 다시 읽어주세요. 여기서 __attribute__란 지시자는 컴파일러에게 함수 컴파일 속성을 알려줍니다.
 __attribute__ ((__section__(.init.text)))

그럼, __attribute__ ((__section__(.init.text))) 은 어떤 의미일까요? 이는 .init.text란 섹션에 코드를 위치시키라는 의미입니다. 섹션이란 비슷한 역할을 수행하는 코드 묶음인데요. 비슷한 속성의 코드나 변수들을 특정 섹션에 위치시키는 경우가 많습니다. 

그럼 __init이란 매크로가 붙는 함수들은 모두  __attribute__ ((__section__(.init.text))) 속성이므로 .init.text 섹션에 위치합니다. 그럼 init_workqueues 함수가 정말 .init.text 섹션에 있는지 확인해야겠죠. 이 정보는 조금 있다가 확인해볼게요.

__cold 분석
__cold 매크로는 [include/linux/compiler-gcc.h] 파일에 다음과 같이 선언됐습니다.
__cold는 __attribute__((__cold__))로 치환하는 매크로이군요.  
#define __cold                 __attribute__((__cold__))
 
__cold__는 자주 호출될 가능성이 낮은 함수에 붙는 속성입니다. 보통 부팅이나 리부팅 될 때만 호출되는 함수에 붙는 속성이죠. 이런 속성을 지정하면 GCC 컴파일러는 이 함수의 처리 속도보다 사이즈에 조금 더 초점을 맞춰서 최적화를 합니다.

참고로, __init 매크로가 붙는 함수들은 커널이 부팅할 때 한 번만 호출된다는 점 유념해주세요.

notrace 분석 
include/linux/compiler.h 파일에 notrace 매크로가 선언돼 있습니다.
#define notrace __attribute__((no_instrument_function))

__attribute__((no_instrument_function)) 속성은 컴파일러가 이 함수에 대한 어셈블리 코드를 생성할 때 프로파일링 정보를 추가하지 말라는 의미입니다. 왜냐면 __init 매크로가 붙은 함수들은 부팅할 때 딱 한 번 호출되니 프로파일링을 할 필요는 없거든요.

여기까지 코드 리뷰를 마무리하고 실제 해당 코드가 어떻게 동작하는지 같이 알아볼게요.

"이 포스팅이 유익하다고 생각되시면 댓글로 응원해주시면 감사하겠습니다.  
혹시 글을 읽고 궁금점이 있으면 댓글로 질문 남겨주세요. 상세한 답글 올려 드리겠습니다!"



# Reference: For more information on 'Linux Kernel';

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2






핑백

덧글

댓글 입력 영역