본문 바로가기

Enginius/Linux

wait()와 exit()함수의 동작

1. wait는 어떻게 process를 rq에서 삭제할까?

 이번엔 실행중 인 process가 잠자게( wait )될때 어떻게 runqueue에서 제거 되는지 살펴 보도록 하자
실행중인 process를 재우기 위해선 wait 관련 함수wait_event(), wait_event_timeout(), wait_event_interruptible() , wait_event_interruptible_timeout() )를 사용하게 되는데 여기서 설명하는것은 함수 자체에 대한 설명보다는 이런 함수들이 어떻게 현재의 current를 runqueue에서 제거하는지를 촛점에 맞추었으며 이해를 돕기 위해 wait_event_interruptible()를 기준으로 설명하도록 하겠다.  

1. "wait.h" 에 있는 wait_event_interrupt를 호출하게 되면 
    1.1 DEFINE_WAIT 메크로를 호출하게 되는데. 이 매크로는 process의 wait_queue에 queue의 이름과 current를 등록하게 된다.
         또한 func 항목에는 autoremove_wake_function()을 등록하여 차후 wake_up될때 호출 될수 있도록 하고 있다 ( 이부분은 
         다음 세미나에서 다룰 wake_up 부분에서 다시 한번 설명 하겠다 )

2. 다시 wait_event_interrupt에 돌아 와서 본격적인 busy wait를 처리 하게 되는데
   wait_event_interrupt를 호출 할 때 조건이 만족하지 않거나 wak_up signal이 있을때 까지 loop를 돌며 
        2.1 wait.c에 있는 prepare_to_wait()함수를 호출하게된다. 
              prepare_to_wait()함수는 현재 process의 wait_queue가 wait_queue_head에 등록 되어 있지 않으면
              wait_queue_head list에 등록하고 현재 current의 task_struct 상태를 TASK_INTERRUPTIBLE로 변경 해주는데
              이는 차후 실제적으로 runqueue에서 삭제하기 위한 중요한 조건이 된다.  


        2.2 wake_up signal이 있을 때 까지sched.c 에 있는 scheduler()를 호출하게 되는데 2.1에서 설정한  
             TASK_INTERRUPTIBLE 때문에 아래 조건에 의해 deactivate_task가 호출 되어면 runqueue에서 제거 되게 된다. 

             if (unlikely((prev->state & TASK_INTERRUPTIBLE) && unlikely(signal_pending(prev)))) 
            {
                prev->state = TASK_RUNNING;
            } else {
                deactivate_task(rq, prev, 1); 
            }



2. Wait상태의 process는 어떻게 rq에 등록될까?

이번 강좌는 이전 강좌에서 잠들어( wait ) 있던 프로세스가 어떻게 다시 runqueue에 등록되는지 알아 보도록 하자..
잠들어 있던 프로세스가 깨어나는 방법은 다양하다 .. timeout이 된다든지, 어떤 조건( condition )에 만족할 경우라든지 아니면 누군가가 wake_up signal을 직접 보낼때 잠자던 프로세스가 깨어 난다.. 그럼 이때 깨어난 프로세스는 어떻게 다시 runqueue에 등록될까?

이전 강좌에 프로세스를 재우기 위해 wait_event_interruptible() 함수를 사용한 것 처럼 깨우는 함수도 wake_up_interruptible()을 사용하여 설명 하도록 하겠다..

1. 먼저 잠자던 프로세스를 깨우기 위해 wait.h 에 있는 wake_up_interruptible()을 호출 한다.

2. wake_up_interruptible() 함수는 sched.c에 있는 __wake_up() 함수를 호출하고 

3. wake_up()함수는 다시 __wake_up_common()함수를 호출 한다. 

4.__wake_up_common()함수는 등록된 wait_queue를 하나씩 찾아내며 생성시 등록된 func ( 기억 나겠지 ?? )를 호출하게 되는데

5. 그 함수가 wait.c에 있는 autoremove_wake_function() 이다. 

6. autoremove_wake_function()은 다시 sched.c에 있는 default_wake_function()을 호출 하고 
    정상적으로 처리되면 wait_queue를 삭제하게 된다. 

7. default_wake_function()으로 돌아 가서 default_wake_function()는 try_to_wake_up() 함수를 호출 하게 되는데..

8. 바로 이 try_to_wake_up() 함수에서 activate_task()를 호출하여 runqueue에 다시 등록하게 되는것이다. 



3. process 종료(exit)

process관련 마지막 강좌이다.. 
process 종료는 의외로 간단하다. 실제로 소스를 보더라도 몇라인 되지도 않는다.. 간단히 보자..

먼저 process가 종료할게 되면 exit.c의 do_exit()가 호출되어진다. 

1. 먼저 task_struct의 flag를 PF_EXITING 으로 자신이 종료 중임을 알린다.

2. __exit_mm()을 통해 process가 가지고 있던 mm_struct  해제 하고

3. ext_sem()를 통해 세마포어 해제

4. __exit_files()를 통해 파일 서술자 참조 카운트 감소

5. __exit_fs()를 통해 파일 시스템 참조 카운트 감소

6. task_struc.state = TASK_DEAD를 설정한 후 sched.c 의 schedule 호출
   
7. schedule()은 deactivate()함수를 통해 runqueue에서 삭제 한다...


process의 생성과 종료 , wait와 wake_up 의 과정을 간략하게 도식화 하면 아래와 같다.