NOWS/ULK

ulk-ch3. processes

emzei 2014. 1. 29. 10:51

Process : an instance of a program in execution
    in Linux... Process = task = thread


▷ Processes, Lightweight Processes, and Threads

Parent process 와 Child process ...  code는 공유할수도 있지만, data(stack/heap)는 분리
Multithread application... 하나의  프로세스는 여러개의 user thread로 구성된다.


in Linux (초기버전)

- multithread application 미지원.

- 커널 입장에서는 멀티스레드 역시 그냥 프로세스... multithread application의 실행은 전부 User Mode (POSIX)

  

  (오늘날)

- 경량 프로세스(Lightweight process)를 이용하여  multithread application 구현

- 단순한  multithread application은 경량 프로세스들을 연결하여 각각의 thread들을 독립적으로 스케줄링하여 일부 수행동안 일부는 잠자고 있는 식으로.

POSIX 기반의 멀티스레드 어플리케이션은 thread group을 지원하는 커널 아래서 제일 잘 다뤄짐.


thread group : a set of lightweight processes that implement a multithreaded application 

=> getpid(), kill(), _exit()과 같은 시스템콜에 대해서는 하나의 개념으로 동작.





▷ Process Descriptor

커널이 프로세스를 관리하려면? 프로세스가 뭐하는지 정확한 정보가 필요함 (우선순위, 상태, 할당주소 등등)

리눅스에서는 프로세스마다 process descriptor 가 존재 ...  task_struct 가 이러한 정보를 갖고 있음.

task_struct 구조체 : 하나의 프로세스에 관한 모든 정보를 담고 있음.

이번 챕터에서는 프로세스의 상태와 부모/자식 프로세스 간의 관계에 중점.




▶ Process State

State field : 프로세스 상태... 

각 state는 상호배제 (단독으로만 사용) --- 따라서 state 필드의 하나의 flag만이 set
<flag 종류>

TASK_RUNNING : CPU에서 실행중이거나 실행 대기

TASK_INTERRUPTIBLE : 어떤 상태가 참이 되기전까지 suspend. (예) H/W인터럽트, 시스템 자원 대기, signal 대기

TASK_UNINTERRUPTIBLE : INTERRUPTIBLE과 유사하지만, signal을 받아도 상태가 변하지 않음. (잘 안쓰임)
                                            ※ 그.러.나. 인터럽트 없이 주어진 이벤트가 발생할 때까지 대기해야하는 특정 조건에서 중요함

TASK_STOPPED : 프로세스 실행 정지

TASK_TRACED : debugger에 의해 실행 정지.


하위의 두 상태는 오직 프로세스의 실행이 종료되었을 때 state 필드와 exit_state 필드에 저장


EXIT_ZOMBIE : 프로세스는 종료되었으나, 부모 프로세스가 아직 wait4()/wiatpid()를 호출하지 않아 죽은 프로세스를 거두지 못한 상태. wait() 계열의 시스템콜이 호출되기 이전에, 커널은 죽은 프로세스의 process descriptor에 있는 정보를 제거할 수 없음. 부모 프로세스가 필요로 할지도 모르기 때문.

EXIT_DEAD : 최종상태 : 부모프로세스가 wait4()/wiatpid()를 호출하여 시스템에 의해 프로세스가 제거되고 있는 중. EXIT_ZOMBIE에서 EXIT_DEAD로 상태를 바꾸는 것은, 같은 프로세스 상에서 다른 스레드의 wait() 계열의 시스템 콜 수행으로 인한 race condition을 피하기 위함.


+ 커널은 set_task_state 및 set_current_state 매크로 사용 : 각각특정 프로세스와 현재 실행인 프로세스의 상태를 설정


▶ Identifying a Process

lightweight process도 자기만의 task_struct 갖는다 ... 독립적으로 스케줄링되는 것들은 모두 process descriptor 가짐

process와 descriptor을 1:1로 대응 -process descriptor pointer-> 커널이 프로세스들을 구분하기 쉬움 ...


unix 계열... pid를 통한 프로세스 구분...PID Limit =  PID_MAX_DEFUALT
pid를 파악하기 위해 pidmap_array 관리. 


linux에서는 각 프로세스/경량 프로세스가 다른 pid 가짐.

unix는 같은 그룹내의 스레드는 공통된 pid를 가짐.

* POSIX 1003.1c 기준 : 멀티스레드 어플리케이션에서 모든 스레드는 반드시 같은 pid를 갖는다.


POSIX 기준을 따르기 위해 linux는 thread group 적용.

thread group에서는 첫번째 경량 프로세스의 PID가 thread group의 PID ... tgid 필드에 저장.


    - process descriptor handling

process descriptor : dynamic memory에 저장. 

linux는 하나의 전처리 메모리영역에 존재하는 두개의 다른 자료구조(thread_info 구조체, 커널 모드의 프로세스 스택)를 묶음
메모리 영역은 보통 두개의 page frame임. 

효율성 이유로 두개의 연속하는 page frame의 8KB 영역에 저장. (아래 그림 참고)



* esp 레지스터 : CPU Stack Pointer


    - Identifying the current process

커널은 esp 레지스터로부터 현재 CPU에서 동작하는 프로세스의 정보(thread_info)의 주소를 얻을 수 있음

구현상, current 매크로를 이용. = current_thread_info()->task


    - Double linked lists 

list_head 자료구조 : next, prev ...  not circular ... hash table 사용 


    - The process list

process list :  존재하는 모든 process descriptor를 연결한 리스트 ... double linked list로 구현
각 process descriptor는 tasks 필드에서 list_head 타입을 사용.


    - The lists of TASK_RUNNING processes

CPU에서 동작할 새로운 프로세스를 찾을 때, 커널은 runnable process를 찾음.


초기 linux에서는 모든 runnable process를 runqueue라 하는 동일한 리스트에 넣음


v2.6에서는 runqueue를 다르게 구현. 프로세스 우선순위 별로 리스트를 구성하여, runqueue를 나눔.

각 task_struct에는 run_list 필드가 존재하여, 동일한 우선순위를 갖는 프로세스들끼리 연결됨.

멀티프로세스 시스템에서는 각 CPU는 자신만의 runqueue를 가짐.

▶ Relationships among processes

* 프로세스 0과 1은 커널에 의해 생성.

* 프로세스 1(init)은 모든 다른 프로세스들의 조상.


process descriptor에서 부모와의 관계를 나타내기 위해 쓰이는 필드들

- real_parent

- parent

- children

- sibling



process descriptor에서 한 프로세스와 다른 프로세스 간의 관계를 나타내기 위해 쓰이는 필드들

- group_leader

- signal->pgrp

- tgid

- signal->session

- ptrace_children

- ptrace_list


- the pidhash table and chained lists

프로세스 탐색 시... sequentially 탐색하는 것은 비효율적 --> 4가지의 hash table 존재 (modulo 2048)
:프로세스가 4가지 타입의 PID를 갖고있기 때문 ( pid / tgid / pgid / sid )


hash function이 table index와 pid 간에 1:1 대응을 보장하는 것은 아님.

문제 ) colliding : 두개의 다른 pid가 같은 table index에 hash 될 때.

해결 방안 ) chaining :각 table entry는 colliding되는 process descriptor들의 double linked list의 head.

==> pid에서 table index로 선형 변환일 때 선호.  프로세스의 최대 개수가 32,768개니까, 최대개수만큼 엔트리만들면 사용되지 않을 공간이 더 많다고 봄. 공간낭비하니까 해싱이 나음.


pid 구조체


nr : PID number

pid_chain : next, prev links

pid_list : PID list 별 head


+) PID hash table 다룰 때 쓰는 함수들

do_each_task_pid() , while_each_task_pid(), find_task_by_pid_type(), find_task_by_pid(), attach_pid(), detach_pid(), next_thread()




▶ How proceses are organized

runqueue 리스트들은 TASK_RUNNING 상태의 모든 프로세스를 그룹화

상태에 따른 프로세스 그룹화는 다양~

+) TASK_STOPPED, EXIT_ZOMBIE, EXIT_DEAD : 특정 list에 연결ㄴㄴ

+) task_INTERRUPTIBLE, TASK_UNINTERRUNTIBLE : 어떤 event에 대응하느냐에 따라 세분화.  

   정보가 충분치 않으면 wait queue로 ...


    - wait queues

interrupt handling, process synchronization, timing 등에서 주로 쓰임.(커널에서)

set of sleeping processes which are woken up by the kernel when some conditions become true.

double linked list로 구현.


각 wait queue는 wait queue head로 구별.

synchronization(동기화)은 wait queue head의 spin lock 락으로 가능.

task_list : waiting processes list의 첫 번째



wait queue list의 각 element는 sleeping process를 나타냄... 

task : sleeping process의 descriptor 주소 

task_list : 동일한 이벤트에 대기하는 프로세스의 리스트의 포인터

flags :

+) 참고 thunder herd http://en.wikipedia.org/wiki/Thundering_herd_problem

어떤 상황에서는 한 이벤트에 관련된 프로세스 중 특정 프로세스만 wake up 해야할 필요가 있음.

exclusive processes : flags = 1 : 커널에 의해 선택적으로 woken up

nonexclusive processes : flags = 0 : 이벤트 발생 시 항상 woken up

func : to specify the processes sleeping in the wait queue should be woken up.




    - handling wait queues

DECLARE_WAIT_QUEUE_HEAD(name) : 새로운 wait queue의 head를 정의할 때 쓰는 매크로. 정적으로 선언.

  새로운 wait queue head 변수를 name 이라 하고.

  lock 및 task_list 필드를 초기화함.


 init_waitqueue_head(): 동적으로 할당된 wait queue head 변수를 초기화하는 데 쓰임.

ex. init_waitqueue_head(q,p) 는 wait_queue_t 구조체의 q를 다음과 같이 초기화


<대안> 

DEFINE_WAIT() :  새로운 wait_queue_t 변수를 선언하고, 

현재 CPU에서 실행중인 프로세스의 descriptor와 autoremove_wake_function() 함수의 주소를
갖고서 초기화.

+ autoremove_wake_function()은 default_wake_function()을 불러서 sleeping process를 깨우고, wait queue list에서 해당 wait queue element 삭제.

+ 커널 개발자는 init_waitqueue_func_entry() 함수로  wait queue element를 초기화하는 함수를 만들 수 있을 것.


add_wait_queue() : wait queue list의 첫 위치에 nonexclusive process 삽입

add_wait_queue_exclusive() : wait queue list의 첫 위치에 exclusive process 삽입

remove_wait_queue() : wait queue에서 process 하나 삭제

waitqueue_active() : 주어진 wait queue list가 비었는지 확인



특정 조건을 기다리는 process는 다음 함수 목록 중 어느 하나에 의해서라도 invoke 가능

(1) sleep_on() : 현재 프로세스의 상태를 TASK_UNINTERRUPTIBLE로 설정하고, wait queue에 삽입.

  다른 프로세스의 실행을 재개하는 스케쥴러에 의해 invoke.

  sleeping process가 깨어나면, 스케쥴러가 sleep_on() 함수의 실행을 재개하여 해당 프로세스를 wait queue에서 제거

(2) interuptible_sleep_on()

(3) sleep_on_timeout()

(4) prepare_to_wait()

(5) wait_event(), wait_event_interruptible()


... DEFINE_WAIT 이나 finish_wait()을 사용하지 않는이상, 커널은 반드시 waiting process가 깨어나면 list에서 해당 원소를 삭제해줘야함.


... 커널은 다음 매크로 중 하나를 이용하여 wait queue에 존재하는 프로세스를 깨워서 TASK_RUNNING 상태로 변경;

wake_up, wake_up_nr, wake_up_all, wake_up_interruptible, wake_up_interruptible_nr,

wake_up_interruptible_all, wake_up_interruptible_sync, wake_up_locked


▶ Process Resource Limits

resource limits : 해당 프로세스가 사용할 수 있는 시스템 자원의 양을 명시.


<i386에서 resource limits>





▷ Process Switch

= task switch = context switch


Hardware Context

모든 프로세스는 고유의 주소 공간을 갖는 반면에, CPU register는 공유...


hardware context 

: 프로세스가 CPU에서 실행을 재개하기 전에, 일련의 데이터를 레지스터로 반드시 로드하는 것.

: 프로세스 실행 흐름의 일부로서, 프로세스에 필요한 모든 정보를 포함

: 리눅스에서는 정보의 일부는 process descriptor에 저장하고, 나머지는 kernel mode kernel에 저장.



▶ Task Statement Segment (TSS)

: 80x86에서 특정 segment type을 포함 ... hardware context를 저장하기 위해서.


+  user mode 에서 kernel mode로 변경될때, kernel mode의 주소는 TSS에서 가져옴.

+ user mode 프로세스가 I/O port에 접근을 시도할 때, CPU는 TSS에 저장된 I/O Permission Bitmap에 접근을 해서 프로세스가 해당 port를 주소화할 수 있는 지 여부를 확인할 수 있음.


TSS의 형식 : tss_struct 구조체



각 TSS는 고유의 8바이트 Task State Segment Descriptor(TSSD)를 갖는다. 

TSSD ~ base(32bit) : TSS의 시작 위치, Limit(20bit) 



- The thread field

프로세스 스위칭 때, 리눅스는 각 프로세서 마다 하나의 TSS를 두기때문에 hardware context를  TSS에 저장할 수 없음.

그래서, 각 process descriptor는 thread_struct 타입의 thread 필드를 두어 hardware context를 저장.



▶ Performing the Process Switch

process switch '2단계'

(1) 새로운 주소 공간을 할당하기 위해, Page Global Directory를 바꿈.

(2) kernel mode의 스택과 hardware context를 바꿈.


    - The switch_to macro

단계 (2)는 switch_to 매크로를 통해 실행. 하드웨어 의존적.

인자값 : prev, next, last.

switching에 필요한 프로세스는 2개가 아니라 3개.


(예+설명)

프로세스 A,B,C가 있다. A를 내리고 B를 실행하고자 함. schedule() 함수에서 prev는 A를, next는 B를 가리킴.

switch_to 매크로가 A를 비활성화하면 A의 흐름은 멈춤.

커널이 A를 재개하고자 할 때, switch_to 매크로를 통하여 다른 프로세스를 비활성화해야함.

이 때 schedule() 함수에서 prev는 C를, next는 A를 가리킴.

A를 재개할 때, 예전의 kernel mode 스택을 찾게되고, 해당 스택에서는 prev는 A를 next는 B를 가리켰음.

이 때, A를 실행하는 스케쥴러는 C에 대한 참조를 잃게 됨.

switch_to 매크로의 마지막 인자는, 매크로가 프로세스 C의 descriptor 주소를 쓸 메모리 위치를 나타냄. (output 역할)

++

process switching 전에, 매크로는 eax CPU 레지스터를 저장함. 이때 eax는 prev에 해당하는 변수값을 가짐. (즉, 프로세스 A의 kernel mode stack에 할당된 prev 지역변수)

A가 재개된 후에는, 매크로는 last 변수로 구분되는 A의 메모리 위치에 eax 레지스터의 내용을 write.

CPU 레지스터가 프로세스 스위칭으로 바뀌진 않으니까, 이 메모리 위치는 C의 descriptor의 주소를 받게됨.

현재 흐름에서 마지막 파라미터가 A의 지역변수 prev 였고, 이는 C의 주소로 덮어쓰기가 됨.



- The __switch_to() function

__switch_to() 함수는 switch_to() 매크로에서 시작된 대량의 process switch.

prev_p와 next_p는 각각 이전 프로세스와 새로운 프로세스를 나타냄.

이 함수의 파라미터 prev_p와 next_p는 eax와 edx 레지스터에 저장됨. (일반적인 함수 경우 스택에 저장되지만..)



Saving and Loading the FPU, MMX, and XMM Registers

FPU : arithmatic floating-point unit ... CPU와 통합됨 (80486dx부터) : mathematical coprocessor

이후, 팬티엄부터 새로운 어셈블리 언어 명령어 집합을 도입하게되는데 이걸 MMX intruction이라하고, 멀티미디어 어플리케이션 실행에 있어서 실행속도를 높이고자 함.  MMX는 FPU의 floating point register들을 이용. 이러한 구조의 단점은 프로그래머가 floating point 명령어와 MMX 명령어를 섞어서 사용할 수 없음.

MMX는 SIMD(single instruction multiple data) 파이프라인을 도입했기 때문에 멀티미디어 프로그램의 속도를 높일수있음

팬티엄3은 SIMD 능력을 확장시켜 SSE 확장을 선보임.


8086은 FPU, MMX, XMM레지스터를 TSS에 자동으로 저장하지 않음. hardware support는 cr0 레지스터내의 TS 플래그.


+ process descriptor 관련

TS_USEDFPU ... thread_info 내 status 필드 : 현재 실행에서 프로세스가 FPU, MMX, XMM을 사용중인지 여부

PF_USED_MATH ... tast_struct 내 flags 필드 :thread.i387 세부 필드가 significant 한지 여부


- Saving the FPU registers

- Loading the FPU registers

- Using the FPU, MMX, and SSE/SSE2 units in kernel mode

   




▷ Creating Processes

<기존 UNIX 시스템> 

프로세스 생성 시, 부모 프로세스의 모든 리소스를 자식 프로세스에게 복사. 비효율적. 전체 주소 공간을 복사해야함.


<현대 UNIX 시스템> >> 3가지의 메커니즘 도입

(1) Copy On Write : 부모랑 자식 프로세스가 같은 리소스를 읽다가, 둘중에 하나가 페이지에 write를 시도하면, 

 커널이 그 내용을 쓰려는 프로세스에게 할당한 새로운 페이지를 복사해줌.

(2) Lightweight process : 부모와 자식이 커널 데이터 구조를 공유

(3) vfork() system call : 부모 프로세스의 메모리 주소 공간을 공유. 

overwrite를 막기위해, 자식이 존재하거나 자식이 새로운 프로그램 실행하기 전까지는 부모의 실행이 block 됨.


▶ The clone(), fork(), and vfork() system calls

clone() 리눅스에서 Lightweight process를 생성할 때 사용.

parameters) fn / arg / flags / child_stack / tls / ptid / ctid

※ child_stack : 자식 프로세스의 esp 레지스터에 할당할 User Mode 스택 포인터를 나타냄. 

(즉, 자식 프로세스의 스택위치)

clone() ~ sys_clone()

fork(), vfork() 또한 clone() 기반


- The do_fork() function

do_fork() : clone(), fork(), vfork()를 다룸.

parameters) clone_flag / stack_start / regs / stack_size / parent_tidptr, child_tidptr

do_fork() ~ copy_process() 이용 ... process descriptor 및 자식의 실행에 필요한 커널 데이터 구조체 설정


<do_fork() 실행 순서>

1) pidmap_array 비트맵을 참고하여 새로운 PID 할당

2) 부모의 ptrace 필드를 확인 : 0이 아니면 다른 프로세스가 trace중 ...디버거가 trace 원하는지 확인

3) copy_process() 호출하여 process descriptor 복사 ... 리소스가 충분하면 task_struct 반환

4) CLONE_STOPPED 플래그가 설정되어 있거나, 자식프로세스를 반드시 trace 해야하면, 

p->ptrace 내의 PT_PTRACED 플래그는 설정되있는 것... 이 때 자식의 상태를 TASK_STOPPED로 설정하고 SIGSTOP 신호 보냄. 다른 프로세스가 복귀하여 TASK_RUNNING 상태가 될 때까지는 TASK_STOPPED 상태로 잔존.

5) CLONE_STOPPED 플래그가 설정되어 있지 않는 경우. wake_up_new_task() 호출.

※ wake_up_new_task() 역할:

a) 부모 및 자식 프로세스의 스케줄링 parameter를 조정

b) 부모와 자식이 같은 CPU에서 동작하지만 동일한 page table을 공유하지 않으면, 부모의 runqueue에서 자식이 부모보다 앞에 강제로 삽입. 부모를 먼저 실행했다면, COW(copy on write) 메커니즘은 필요없는 page 복제만 수행함.

c) 부모와 자식이 다른 CPU에서 동작하거나 동일한 page table을 공유한다면, 부모의 runqueue의 마지막 위치에 자식을 삽입


- The copy_process() function

copy_process(): process descriptor을 설정하고, 자식의 실행에 필요한 커널 데이터 구조체를 설정.

parameters) do_fork()와 동일. + 자식의 PID
(주요단계가 너무 길다. 다시 공부할 때 마저 적을래.)


Kernel Threads

전통적 UNIX 시스템에서는 중요한 task들을 실행중인 프로세스에게 위임하기도 했음. 

( flushing disk caches, swapping out unused page, servicing network connection 등 )

그러나 이중에는 반드시 kernel mode에서만 수행되어야 하는 것들도 있어서, 현대 운영체제에서는 이와 같은 작업은 kernel thread를 통해 구현될 수 있도록 함. 이렇게 하면 불필요한 user mode에 지장을 주지 않아도 됨.

리눅스에서는,  일반적인 프로세스와 kernel thread는 좀 다름...

> kernel mode 에서만 kernel thread가 동작. 정규 프로세스는 user mode, kernel mode 둘다 가능

> kernel thread는 kernel mode에서만 동작하므로, 항상 PAGE_OFFSET 보다 큰 선형 주소만 사용.

   정규 프로세스 경우에는 4GB의 전체 주소공간 사용.


- Create a Kernel Thread

kernel_thread() : 새로운 kernel thread를 생성한다.

parameters) fn (실행시킬 커널 함수) / arg(해당 함수에 들어갈 인자) / flags(clone flag 집합)

본 함수는 반드시 do_fork()↓↓를 호출한다.

do_fork( flags|CLONE_VM|CLONE_UNTRACED, 0, pregs, 0, NULL, NULL);


- Process 0

모든 프로세스의 조상. process 0 = idle process = swapper process(역사적인 이유).

kernel thread 로서, 리눅스 초기화 단계 때 아무것도 없는 상태에서 생성됨.


- processor descriptor : init_task 변수에 저장. INIT_TASK 매크로 통해서 초기화

- thread_info 와 kernel mode 스택은 init_thread_union 변수에 저장. INIT_THREAD_INFO 매크로 통해서 초기화

- 다음 변수는 소문자.  매크로는 대문자

; init_mm / init_fs / init_files / init_signals / init_sighand

- master kernel Page Global Directory는 swapper_pg_dir에 저장


startk_kernel() : 커널이 요구하는 모든 데이터를 초기화, 인터럽트를 허용.

 또다른 kernel thread를 생성하여 process 1로 지음.

=> kernel_thread(init, NULL, CLONE_FS|CLONE_SIGHAND)


새롭게 생성된 process 1(PID=1, init)은 process 0와 모든 프로세스의 커널데이터를 공유.

스케줄러에 의해 선택된 경우, init 프로세스는 init()를 수행.


process 0는 process 1를 만들고나서, cpu_idle()을 호출함. 

어떠한 프로세스도 TASK_RUNNING 상태가 아닐 때, 스케쥴러는 process 0를 호출.



- Process 1

init() 함수를 수행. 커널의 초기화를 완료함.

init()은 execve()를 호출하여, 실행 프로그램 init을 로드함. 

커널 스레드 init은 자신의 커널 데이터 구조를 갖는 정규 프로세스가 되어, 시스템이 종료되기전까지 살아있음.

모든 프로세스의 활동을 지켜보고, 모든 프로세스를 생성하는 역할.



- Other kernel threads

커널 초기화 단계에서  생성되어 시스템 종료까지 유지되는 것과, 요구에 따라 생성되는 것으로 구분.

예: keventd, kapmd, kswapd, pdflush, kblockd, ksoftirqd 등




▷ Destroying Processes

대부분 프로세스는 code의 실행이 끝나서 '종료'.

종료가 발생하면, 커널은 알림을 받아서 해당 프로세스 소유의 자원을 반환하도록 함. 

일반적은 종료 : exit() 호출. C 컴파일러는 main()의 맨 마지막 stmt 다음에 exit()을 항상 삽입


▶ Process Termination

리눅스 커널 2.6에서 User Mode 어플리케이션을 종료하는 2가지.

(1) exit_group() : thread group 전체를 종료. system call do_group_exit() 으로 구현. exit()으로 호출됨.

(2) _exit() : 하나의 프로세스 종료. thread group 상관않음. 시스템 콜 do_exit()으로 구현. 


두 함수의 자세한 내용은 다음에 복습하면서 다시!

- The do_group_exit() function

현재의 thread group에 속한 모든 프로세스를 종료.


- The do_exit() function

모든 프로세스 종료는 do_exit()에 의해 수행. 


▶ Process Removal

Unix 운영체제에서는 부모 프로세스의 PID나  자식의 실행 상태를 커널에게 물을 수 있다.

커널은 프로세스가 종료된 직후, process descriptor를 제거하지 않고, 부모 프로세스가 wait() 계열의 시스템 콜을 호출 한 뒤 제거한다.


자식이 종료되기 전에 부모가 먼저 종료되면? 시스템에는 좀비 프로세스들이 넘쳐나겠지만... 

이 문제는 모든 고아 프로세스를 init의 자식으로 두면 해결이 가능하다.


release_task() 함수는 좀비 프로세스의 descriptor로부터 마지막 데이터 구조를 분리한다.

이 함수를 좀비 프로세스에게 적용하는 데에는 2가지 방법이 있음.

(1) do_exit() :  자식프로세스가 보낸 시그널을 받는 데에 부모 프로세스가 관심이 없음. 

이 경우에 process descriptor가 사용한 메모리는 스케줄러가 반환함.

(2) wait4() / waitpid() : 시그널이 부모에게 전해진 다음 호출. 

 이 경우에는,  process descriptor가 사용한 메모리의 반환을 요구.


'NOWS > ULK' 카테고리의 다른 글

ulk-ch2. memory addressing  (0) 2014.01.17
ulk-ch1. introduction  (0) 2014.01.13