프로세스가 종료될 때, 커널은 프로세스가 갖고 있던 자원을 반납시키고 부모 프로세스에게 이 소식을 전달해야만 한다.
일반적으로 프로세스는 exit() 시스템 콜을 호출해야 종료된다.
필요시 이 시스템콜을 명시적으로 호출할 수 도 있고, 암시적으로 호출되기도 한다. (컴파일러는 main() 함수가 종료된 다음, 암시적으로 exit() 시스템콜을 호출한다)
프로세스가 처리하거나 무시할 수 없는 시그널이나 예외 상황이 발생했을 경우가 그러하다.
프로세스가 어떻게 종료되던간에, 대부분 작업들은 do_exit()가 실행하는데, 그것을 정리하면 다음과 같다.
*우선 task_struct 구조체의 flags 멤버에 PF_EXITING 플래그를 설정한다.
*다음 del_timer_sync()를 호출하여 커널 타이머를 해제한다. 이 함수가 리턴되는 시점에서는 어떠한 타이머도 등록되어 있지 않으며 어떠한 타이머 핸들러도 실행중이지 않다는 것이 보장된다.
* 만약 BSD 프로세스 어카운팅이 사용중이면, do_exit()는 acct_process()를 호출하여 어카운팅 정보를 기록한다.
* __exit_mm()을 호출하여 프로세스가 잡고 있는 mm_struct 구조체를 반환시킨다. 만약 다른 프로세스에서 이것을 사용하고 있는ㄱ ㅕㅇ우가 없다면 그 메모리를 제거한다.
* 다은 exit_sem을 호출한다. 만약 프로세스가 IPC 세마포어를 얻기위해 큐에서 대기중이었다면, 이 시점에서 해당 큐를 빠져나오게 된다.
*__exist_files(), __exit_fs(), exit_namespace(), exit_sighand()를 호출하여 각각 파일 서술자, 파일 시스템 데이터, 프로세스 네임스페이스, 시그너 핸들러와 관련된 객체의 사용 카운트를 감소시킨다. 만약 사용 카운트가 0이면, 그 객체는 언, 누구에 의해서도 사용되지 않으므로 제거한다.
* 태스크의 종료 코드를 task_struct 구조체의 exit_code멤버에 설정한다. 이 값은 exit()으로 부터 주어진 값이거나 혹은 커널의 종료 매커니즘에서 결정된 값이다. 이 exit() 코드값은 후에 부모가 요청할 경우 돌려주기 위해 여기 저장된다.
* 그리고나서 exit_notify()를 호출하여 태스크의 부모에게 시그널을 보내고, 종료되는 태스크의 자식들의 부모를 같은 스레드 그룹의 다른 스레드나 혹은 init프로세스로 지정한다. 다음 , 태스크의 상태를 TASK_ZOMBIE로 설정한다.
* 마지막으로, do_exit()는 schedule()함수를 호출하여 새프로세스로 스위칭한다. TASK_ZOMBIE 상태인 태스큰 스케줄링에서 제외되므로, 이것이 바로 종료되는 태스크의 마지막 실행코드가 된다.
일반적으로 프로세스는 exit() 시스템 콜을 호출해야 종료된다.
필요시 이 시스템콜을 명시적으로 호출할 수 도 있고, 암시적으로 호출되기도 한다. (컴파일러는 main() 함수가 종료된 다음, 암시적으로 exit() 시스템콜을 호출한다)
프로세스가 처리하거나 무시할 수 없는 시그널이나 예외 상황이 발생했을 경우가 그러하다.
프로세스가 어떻게 종료되던간에, 대부분 작업들은 do_exit()가 실행하는데, 그것을 정리하면 다음과 같다.
*우선 task_struct 구조체의 flags 멤버에 PF_EXITING 플래그를 설정한다.
*다음 del_timer_sync()를 호출하여 커널 타이머를 해제한다. 이 함수가 리턴되는 시점에서는 어떠한 타이머도 등록되어 있지 않으며 어떠한 타이머 핸들러도 실행중이지 않다는 것이 보장된다.
* 만약 BSD 프로세스 어카운팅이 사용중이면, do_exit()는 acct_process()를 호출하여 어카운팅 정보를 기록한다.
* __exit_mm()을 호출하여 프로세스가 잡고 있는 mm_struct 구조체를 반환시킨다. 만약 다른 프로세스에서 이것을 사용하고 있는ㄱ ㅕㅇ우가 없다면 그 메모리를 제거한다.
* 다은 exit_sem을 호출한다. 만약 프로세스가 IPC 세마포어를 얻기위해 큐에서 대기중이었다면, 이 시점에서 해당 큐를 빠져나오게 된다.
*__exist_files(), __exit_fs(), exit_namespace(), exit_sighand()를 호출하여 각각 파일 서술자, 파일 시스템 데이터, 프로세스 네임스페이스, 시그너 핸들러와 관련된 객체의 사용 카운트를 감소시킨다. 만약 사용 카운트가 0이면, 그 객체는 언, 누구에 의해서도 사용되지 않으므로 제거한다.
* 태스크의 종료 코드를 task_struct 구조체의 exit_code멤버에 설정한다. 이 값은 exit()으로 부터 주어진 값이거나 혹은 커널의 종료 매커니즘에서 결정된 값이다. 이 exit() 코드값은 후에 부모가 요청할 경우 돌려주기 위해 여기 저장된다.
* 그리고나서 exit_notify()를 호출하여 태스크의 부모에게 시그널을 보내고, 종료되는 태스크의 자식들의 부모를 같은 스레드 그룹의 다른 스레드나 혹은 init프로세스로 지정한다. 다음 , 태스크의 상태를 TASK_ZOMBIE로 설정한다.
* 마지막으로, do_exit()는 schedule()함수를 호출하여 새프로세스로 스위칭한다. TASK_ZOMBIE 상태인 태스큰 스케줄링에서 제외되므로, 이것이 바로 종료되는 태스크의 마지막 실행코드가 된다.
'OS이야기' 카테고리의 다른 글
schedule() (0) | 2011.10.13 |
---|---|
타임슬라이스 재계산 (0) | 2011.10.13 |
커널 스레드 (0) | 2011.10.11 |
리눅스의 스레드 구현 (0) | 2011.10.11 |
프로세스 생성 (0) | 2011.10.11 |