1. 태스크 디스크립터: 프로세스 사이간의 관계
1.1 프로세스의 부모-자식 관계
유저 공간에서 생성한 모든 프로세스의 부모 프로세스는 init 이고 커널 공간에서 생성한 커널 스레드(프로세스)의 부모 프로세스는 kthreadd라고 했습니다. 태스크 디스크립터에서는 프로세스의 부모와 자식 관계를 상세히 알 수 있습니다.
struct task_struct *real_parent;
프로세스를 생성한 부모 프로세스의 태스크 디스크립터 주소를 저장합니다.
struct task_struct *parent;
부모 프로세스를 의미합니다. real_parent 란 멤버는 해당 프로세스를 생성해준 프로세스를 의미합니다. 그런데 자식 프로세스 입장에서 부모 프로세스가 소멸된 경우 부모 프로세스를 다른 프로세스로 지정합니다. 프로세스 계층 구조에서 지정한 부모 프로세스가 없을 경우 init 프로세스를 부모 프로세스로 변경합니다.
커널에서 프로세스는 다양한 방식으로 서로 연결되어 있습니다. 이번에는 태스크 디스크립터에서 프로세스 사이의 관계를 나타내는 다음과 같은 필드에 대해 알아봅시다.
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 필드가 다르다.
<프로세스가 종료될 때 새로운 부모 프로세스 지정 흐름>
부모 프로세스가 종료되면 위 그림과 같이 do_exit() 함수에서 화살표 방향으로 함수를 호출한다.
함수 이름과 같이 forget_original_parent() 함수와 find_new_reaper() 함수에서 새로운 부모 프로세스를 지정한다.
1.2 children과 sibling 필드
프로세스 간의 관계를 저장하는 children과 sibling 필드를 보자.
n Struct list_head children
부모프로세스가 자식 프로세스를 생성할 때 children 연결리스트에 자식 프로세스를 등록한다.
n Struct list_head sibling
같은 부모 프로세스로 생성된 프로세스의 연결리스트 주소를 저장한다. 필드명에서 알 수 있듯이 형제 관계의 프로세스들이 등록된 연결리스트이다.
Children과 sibling필드가 어떤 방식으로 연결됐는지 구체적으로 알아보자.
명령어 : ps axjf
부모와 자식 프로세스의 관계를 확인하는 명령어
출력 결과 kworker/0:0H, mm_percpu_wq, ksoftirqd/0
프로세스들의 부모 프로세스는 kthreadd임을 알 수 있다.
각 프로세스의 PPID(Parent Process PID) 항목을 보면 모두 2인데, 커널 스레드를 생성하는 kthreadd프로세스의 pid가 2이다.
1.3 task_struct 구조체의 sibling 필드로 연결된 프로세스 리스트
부모 프로세스인 kthreadd 입장에서 태스크 디스크립터는 다음 그림과 같이 구성되어있다.
태스크 디스크립터 관점에서 보면 «kthreadd» 프로세스 태스크 디스크립터의 children 필드는 연결리스트이다.
연결 리스트 헤드에 등록된 자식 프로세스의 task_struct 구조체의 sibling 필드 주소를 저장한다.
«kthreadd»프로세스의 자식 프로세스인 «kworker/0:0H»입장에서 «mm_percpu_wq»와 «ksoftirqd/0»프로세스는 자신의 sibling 연결리스트로 이어져 있다. 같은 부모 프로세스에서 생성된 프로세스이기 때문이다.
1.4 프로세스 연결리스트
task_struct 구조체의 tasks 필드는 list_head 구조체로서 연결리스트 타입이다. 커널에서 구동 중인 모든 프로세스는 tasks 연결리스트에 등록돼 있다.
그렇다면 프로세스의 태스크 디스크립터 필드 중 연결리스트 타입인 task필드는 연제 init 프로세스의 태스크 디스크립터 필드 중 연결리스트 타입인 tasks 필드에 등록될까 ?
è 프로세스는 처음 생성될 때 init_task 전역변수 필드인 tasks 연결 리스트에 등록된다.
프로세스 생성 시 호출되는 copy_process() 함수를 보며 처리 과정을 살펴보자.
kernel/msm-4.9/kernel/fork.c
Dup_task_sturct함수를 실행하게 되면 커널로부터 태스크 디스트립터를 할당 받는다.
Init_task.task 연결리스트의 마지막 노드에 현재 프로세스의 task_struct 구조체의 tasks 주소를 등록한다.
1.5 프로세스 실행시각 정보
u64 utime :
유저모드에서 프로세스를 실행한 시각
Account_user_time()함수에서 변경된다.
u64 stime
커널모드에서 프로세스가 실행한 시각
account_system_index_time()함수에서 변경
struct sched_info sched_info.last_arrival
프로세스 스케쥴링 정보를 저장
info.last_arrival 필드는 sched_info_arrive() 함수에서 변경
context_switch(). 함수 내에서 컨텐스트 스위칭을 수행하기 전에 다음과같은 함수 흐름으로 sched_info_ arrive()함수가 호출된다.
context_switch()
prepare_task_switch()
sched_info_switch()
__sched_info_switch()
sched_info_arrive()
커널은 sched_info_arrive() 함수에서 프로세스의 실행시간을 업데이트 한다.
프로세스 실행 시각 확인 명령어 : ps -l
2. thread_info 구조체 - Intro
커널에서는 태스크 디스크립터 뿐만 아니라 thread_info 구조체로 프로세스 실행 동작을 관리한다.
2.1 thread_info란?
태스크 디스크립터는 프로세스의 공통 속성 정보를 저장하고 관리한다. 추가로 커널에서는 프로세스의 세부 실행 정보를 저장하거나 로딩하는 자료구조가 필요한데, 이를 thread_info 구조체에서 관리한다.
thread_info 구조체는 다음과 같은 프로세스의 핵심 실행 정보를 저장
1) 선점 스케줄링 실행 여부
2) 시그널 전달 여부
3) 인터럽트 컨텍스트와 Soft IRQ 컨텍스트 상태
4) 휴면 상태로 진입하기 직전 레시스터 세트 로딩 및 백업
'Embedded : : Linux > : : Linux Kernel' 카테고리의 다른 글
커널 로그 분석: WARN 매크로 (0) | 2024.10.31 |
---|---|
Debugging the kernel using Ftrace (4) | 2024.10.24 |
커널 스레드 (0) | 2024.10.12 |
do_fork() 함수, 그리고 5.x 이후 (0) | 2024.10.12 |
프로세스, 유저 프로세스, 커널 프로세스 (0) | 2024.09.27 |