[리눅스 커널 구조 원리] #8. 스레드 구조체 초기화

문연수·2025년 3월 24일
0

 이전 글에서 설명한 것처럼 프로세스를 처음 생성할 때에는 copy_process() 함수를 호출한다. 이 과정에서 dup_task_struct() 함수를 호출하게 되는데, 이 함수가 태스크 디스크립터와 프로세스가 실행될 스택 공간을 새로 만들게 된다.

1. dup_task_struct()

dup_task_struct() 함수가 수행하는 핵심적인 기능은 다음과 같다:

  • alloc_task_struct_node() 함수: 슬랩 할당자로 태스크 디스크립터인 task_struct 구조체를 할당 받음
  • alloc_thread_stack_node() 함수: 프로세스 스택 공간을 할당받음

 책에서는 슬럽(slub)을 사용한다고 했으나 실제 구현은 슬랩(slab)으로 되어있다. 단순히 커널 config 의 차이일 수도 있다. 필자는 할당자 별 차이를 잘 모르기 때문에 자세한 설명은 생략하려 한다. 그냥 그런다보다 한다.

alloc_thread_stack_node() 함수에서는 stack 을 할당하는데 코드 자체만 보면 따로 스택을 동적할당하는 코드는 보이지 않고 대신 cached_stacks 이라는 곳에서 vm_struct 를 받아오고 이를 가져오는 것 같다. 책에서 설명하는 것처럼 stack 의 크기는 THREAD_SIZE 정해져 있는 것 같고 이는 0x4000 이다.

 책에서는 setup_thread_stack() 으로 태스크 디스크립터의 주소를 thread_info 구조체의 task 필드에 저장한다고 나와 있는데 지금은 setup_thread_stack() 함수가 비어있다. 그런데 당연히 그럴 수 밖에 없다. 왜냐하면 CONFIG_THREAD_INFO_IN_TASK 가 켜져 있으면 thread_infotask_struct 에 built-in 되기 때문이다.

2. alloc_task_struct_node() 함수

 여기에서는 다시 슬럽이 아니라 슬랩이라고 설명하는 걸 보면 오탈자 같다. kmem_cache_alloc_node() 함수를 호출해서 task_struct 구조체를 할당 받는다. 여기에서 task_strut_cachep 라는 슬랩 캐시를 사용하는데 이는 다음과 같은 역할을 수행한다:

  • task_struct 구조체 크기만큼 메모리를 미리 확보해 놓고 대기
  • task_struct 구조체 할당 요청이 오면 이미 할당해 놓은 task_struct 구조체의 시작 주소를 반환

 이래서 리눅스 커널의 한계(limit)이 존재하는 것 같다. 사용자 입장에선 불편할 수 있지만 그 불편함을 감수할 만큼의 독보적인 장점이 있는 구현인 것 같다. 탁월한 설계다. 다만 이걸 이미 빌드된 커널에서 config 한 줄 바꿔서 변경하는 것은 불가능하겠다.

3. alloc_thread_stack_node() 함수

 책에서는 alloc_thread_stack_node() 함수가 단 두 구문(statement)로 구현되어 있는데 필자의 코드에서는 완전히 다른 구현으로 동작한다. 이건 CONFIG_VMAP_STACK 여부에 따라 그 구현이 달라지게 되는데 책의 구현은 CONFIG_VMAP_STACK 이 꺼진 상태이다.

https://www.kernel.org/doc/Documentation/vm/vmalloced-kernel-stacks.rst

 관련된 내용은 여기에 있는 것 같은데... 너무 길어서 읽고 싶지 않다.. 흑흑... 그래도 Introduction 만 보자면 Kernel Stack Overflow 문제가 디버깅을 어렵게 만들기 때문에 이 문제를 해결하기 위해, 가상으로 매핑한 커널 스택에 페이지 가드(?)를 붙여서 오버 플로우를 캐칭하겠다는 것이 메인 아이디어인 것 같다.

profile
2000.11.30

0개의 댓글

관련 채용 정보