Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

18113
1478
166888


[리눅스커널] 프로세스: 태스크 디스크립터(task_struct 구조체) - 프로세스 간의 관계 4. 프로세스(Process) 관리

커널에서 프로세스는 다양한 방식으로 서로 연결돼 있습니다. 이번 절에서는 태스크 디스크립터에서 프로세스 사이의 관계를 나타내는 다음과 같은 필드에 대해 알아보겠습니다.

struct task_struct  *real_parent
struct task_struct *parent
struct list_head children
struct list_head sibling

이전 절에서도 살펴봤듯이 유저 공간에서 생성한 프로세스의 부모는 대부분 init이고 커널 공간에서 생성한 커널 스레드(프로세스)의 부모는 kthreadd 프로세스입니다. 태스크 디스크립터에서는 프로세스의 부모와 자식 관계를 real_parent와 parent 필드로 알 수 있습니다.

struct task_struct  *real_parent

자신을 생성한 부모 프로세스의 태스크 디스크립터 주소를 저장합니다.

struct task_struct *parent

부모 프로세스의 태스크 디스크립터 주소를 담고 있습니다.

그런데 *real_parent와 *parent 필드의 차이점은 무엇인가요? 일반적인 상황에서 자신을 생성한 부모 프로세스가 종료되지 않고 실행 중이면 real_parent와 parent가 같습니다. 그런데 부모 프로세스가 소멸될 수 있습니다. 프로세스 계층 구조에서 지정한 부모 프로세스가 없을 경우 init 프로세스를 부모 프로세스로 변경합니다. 이 조건에서는 *real_parent와 *parent 필드가 다릅니다. 다음 그림을 보면서 처리 과정을 더 자세히 살펴보겠습니다.

그림 4.12 프로세스가 종료될 때 새로운 부모 프로세스가 지정되는 실행 흐름

부모 프로세스가 종료되면 그림 4.12와 같이 do_exit() 함수에서 화살표 방향으로 함수를 호출합니다. 함수 이름과 같이 forget_original_parent() 함수와 find_new_reaper() 함수에서 새로운 부모 프로세스를 지정합니다.

이어서 프로세스 간의 관계를 저장하는 children과 sibling 필드를 봅시다.

struct list_head children

부모 프로세스가 자식 프로세스를 생성할 때 children 연결 리스트에 자식 프로세스를 등록합니다.

struct list_head sibling

같은 부모 프로세스로 생성된 프로세스 연결 리스트 주소를 저장합니다. 필드명에서 알 수 있듯이 형제 관계의 프로세스들이 등록된 연결 리스트입니다.

이번에는 children과 sibling 필드가 어떤 방식으로 연결됐는지 구체적으로 알아봅시다. 이를 위해 라즈베리 파이에서 터미널을 열고 ‘ps axjf’ 명령어를 입력해 부모와 자식 프로세스 관계를 확인해 봅시다.

root@raspberrypi:/home/pi# ps axjf
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
    0     2     0     0 ?           -1 S        0   0:00 [kthreadd]
    2     4     0     0 ?           -1 I<       0   0:00  \_ [kworker/0:0H]
    2     6     0     0 ?           -1 I<       0   0:00  \_ [mm_percpu_wq]
    2     7     0     0 ?           -1 S        0   0:00  \_ [ksoftirqd/0]

출력 결과, kworker/0:0H, mm_percpu_wq, ksoftirqd/0 프로세스들의 부모 프로세스는 kthreadd임을 알 수 있습니다. 각 프로세스의 PPID(Parent Process PID) 항목을 보면 모두 2인데, 커널 스레드를 생성하는 kthreadd 프로세스의 pid가 2입니다.


리눅스를 탑재한 대부분의 시스템(안드로이드, 라즈베리 파이)에서 kthreadd 프로세스의 PID는 2입니다.


부모 프로세스인 kthreadd 입장에서 태스크 디스크립터는 다음 그림과 같이 구성돼 있습니다.

그림 4.13 task_struct 구조체의 sibling 필드로 연결된 프로세스 리스트

태스크 디스크립터 관점에서 이 구조를 살펴봅시다. “kthreadd” 프로세스 태스크 디스크립터의 children 필드는 연결 리스트입니다. 연결 리스트 헤드에 등록된 자식 프로세스의 task_struct 구조체의 sibling 필드 주소를 저장합니다.

“kthreadd” 프로세스의 자식 프로세스인 “kworker/0:0H” 입장에서 “mm_percpu_wq”와 “ksoftirqd/0” 프로세스는 자신의 sibling 연결 리스트로 이어져 있습니다. 같은 부모 프로세스에서 생성된 프로세스이기 때문입니다.

#프로세스

프로세스 소개 
프로세스 확인하기  
프로세스는 어떻게 생성할까?  
유저 레벨 프로세스 실행 실습  
커널 스레드  
커널 내부 프로세스의 생성 과정   
프로세스의 종료 과정 분석  
태스크 디스크립터(task_struct 구조체)  
스레드 정보: thread_info 구조체  
프로세스의 태스크 디스크립터에 접근하는 매크로 함수  
프로세스 디버깅  
   * glibc의 fork() 함수를 gdb로 디버깅하기  


"혹시 궁금한 점이 있으면 댓글로 질문 남겨주세요. 아는 한 성실히 답변 올려드리겠습니다!" 

Thanks,
Austin Kim(austindh.kim@gmail.com)

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

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

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




 
 

핑백

덧글

댓글 입력 영역