Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

3440
557
422296


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

리눅스 커널 코드를 읽다가 current란 매크로를 보신 적이 있나요? 아직 없다고요? 그럼 조금 코드를 읽다 보면 만나게 될 겁니다. 왜냐면 리눅스 커널 코드 구석구석 이 매크로를 쓰고 있거든요. 

그럼 current 매크로는 어떤 동작을 할까요? current란 매크로는 현재 구동 중인 프로세스의 태스크 디스크립터 정보를 담고 있습니다. 그럼 current 매크로가 포함된 코드를 열어 볼까요?

다음 파일 디스크립터를 할당하는 get_unused_fd_flags 함수를 잠깐 볼게요.
1 int get_unused_fd_flags(unsigned flags)
2 {
3 return __alloc_fd(current->files, 0, rlimit(RLIMIT_NOFILE), flags);
4 }
5 EXPORT_SYMBOL(get_unused_fd_flags);

여기서 잠깐 get_unused_fd_flags 함수가 어떻게 호출되는지 잠깐 알아볼까요? 유저 공간에서 다음과 같이 open 함수를 호출하면 “/dev/devices” 파일에 대한 핸들을 가져온다고 알고 있습니다. 파일 입출력 프로그램에서 많이 쓰이죠.
int main(int argc, char **argv) 
{
int fd;
fd = open(“/dev/devices”, O_RDONLY);

if (fd == -1)
         printf ("fd open Error.... \n");
else
         printf ("fd open success...!!! fd is : %d \n" , fd);
 
close(fd);
}

그런데 위 코드에서 open이란 함수를 유저 공간에서 호출하면 커널 입장에서 어떤 동작을 하죠? open에 해당하는 시스템 콜이 실행됩니다. 이 후 커널 공간에서는 (struct file *)란 자료구조인 파일 디스크립터를 생성합니다. 이 파일 디스크립터가 유저 공간에서 선언된 fd에 대응하는 거죠.  get_unused_fd_flags 함수는 이 파일 디스크립터를 할당하는 동작을 수행합니다. 이번 장은 매크로에 대한 내용을 다루므로 파일 디스크립터에 대한 자세한 내용은 가상 파일 시스템 챕터를 참고하세요.

그럼 다시 get_unused_fd_flags  함수 분석으로 돌아갈게요. 
3 return __alloc_fd(current->files, 0, rlimit(RLIMIT_NOFILE), flags);

3번째 줄 코드를 보면 current 변수로 current->files 멤버에 접근해서 __alloc_fd 함수 첫 번째 파라미터로 전달합니다. 코드를 조금 눈여겨보면 조금 이상한 점이 보이지 않나요? 난감하게도 어느 코드에도 current를 지역변수나 전역 변수로 선언한 흔적이 없습니다.

이번에는 __put_task_struct란 함수 코드 조각을 같이 볼게요.
1 void __put_task_struct(struct task_struct *tsk)
2 {
3 WARN_ON(!tsk->exit_state);
4 WARN_ON(atomic_read(&tsk->usage));
5 WARN_ON(tsk == current);
 
5번째 줄을 보면 __put_task_struct 함수로 전달된 struct task_struct 타입 tsk 파라미터와 current가 같은지 체크를 합니다. 이번도 마찬가지입니다. 함수 내 current란 변수를 선언한 흔적이 없습니다. 그런데 갑자기 current란 변수가 나타났습니다.

5번째 줄 코드에서 WARN_ON이란 매크로가 보이네요. 이 매크로는 WARN_ON 매크로 내 조건 코드가 true면 WARN 코드를 실행합니다.  

위에서 2가지 예시는 빙산의 일각에 불과합니다. current란 변수는 리눅스 커널 코드에서 굉장히 많이 볼 수 있거든요. 그래서 이 코드를 정확히 이해해야 커널 코드를 읽는 데 문제가 없겠죠.

그럼 current 코드에 대해서 알아볼게요. current는 현재 구동 중인 프로세스의 태스크 디스크립터입니다. 구조체는 struct task_struct이고 포인터형 변수입니다. 그래서 current->comm, current->files 형태로 각 멤버에 접근합니다. 

여기서 태스크 디스크립터란 어려운 용어가 나오는데요. 태스크 디스크립터를 잠깐 소개할게요.
임베디드 시스템에서 TCB(Task Control Block)란 용어 들어오신 적 있나요? 프로세스의 상세 정보(이름, 메모리 정보, 실행 시간)을 담고 있는 자료 구조입니다.

리눅스 커널에서 TCB를 태스크 디스크립터라고 하며 이 자료 구조는 include/linux/sched.h 해더 파일을 보시면 됩니다.
struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK
/*
 * For reasons of header soup (see current_thread_info()), this
 * must be the first element of task_struct.
 */
struct thread_info thread_info;
#endif
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
void *stack;

상세 내용은 커널 자료 구조 챕터에서 더 살펴보니 참고하세요.

이제 다시 current 코드로 돌아갈게요. 이 변수는 struct task_struct 구조체이며 포인터형이라고 했죠? 그럼 다음과 같이 선언됐다고 봐도 좋겠죠?
struct task_struct *current;



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

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

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





핑백

덧글

댓글 입력 영역