2014년 1월 30일 목요일

[리눅스 시스템 프로그래밍] 프로세스 종료 (Terminating a Process)

#include <stdlib.h>

void exit (int status);

exit() 호출은 몇 가지 종료 단계를 수행한 다음에 커널에게 프로세스를 종료하라고 지시한다.
status 매개 변수는 프로세스 종료 상태를 지정하기 위해 사용한다.

프로세스를 종료하기에 앞서, C 라이브러리는 다음 종료 단계를 순서대로 수행한다.
1. atexit()나 on_exit()로 등록된 함수를 등록 순서의 역순으로 호출한다.
2. 모든 표준 입출력 스트림을 강제로 비운다.
3. tmpfile() 함수로 만든 임시 파일을 삭제한다.

이런 단계를 거쳐 사용자 영역에서 프로세스가 수행해야 하는 모든 작업을 마쳤다면, exit()는 시스템 콜인 _exit()를 불러 커널이 프로세스 종료 과정에 필요한 나머지 작업을 수행하게 한다.

#include <unistd.h>

void_exit (int status);

어플리케이션에서 _exit()를 직접 호출할 수 있지만, 이런 방식은 거의 의미가 없다. 대다수 어플리케이션은 stdout 스트림을 강제로 쓰는 작업(flushing)과 같이(위의 작업 2.) exit가 제공하는 정리 기능 몇 가지를 요구하기 때문이다.

프로그램을 끝내는 전통적인(classic) 방법은 명시적인 시스템 콜이 아니라 단순히 프로그램 '끝까지 진행하는' 방법이다.  C 프로그램에서는 main() 함수가 반환될 때 이런 일이 일어난다. 하지만 '끝까지 진행하는' 방법에서도 여전히 시스템 콜을 부른다. 컴파일러는 독자적인 종료 코드 다음에 암묵적으로 _exit()를 넣어버린다. exit()나 main() 반환값을 사용해서 종료 상태를 명시적으로 반환하도록 프로그램을 작성하는 관례는 바람직하다. 셸은 종료값을 확인해서 명령이 성공했는지 실패했는지를 파악한다. eixt(0)이거나 main() 반환값이 0이면 성공을 나타낸다.

또한 기본 동작이 프로세스 종료를 의미하는 시그널을 받을 경우 프로세스는 종료한다. (SIGTERM, SIGKILL 같은 시그널)


#include <stdlib.h>

int atexit (void (*function)(void));

atexit() 라이브러리 호출은 프로세스 종료 과정에서 수행할 함수를 등록하기 위해 쓰인다.
atexit() 호출이 성공하면 인수로 넘어온 함수를 정상적인 프로세스 종료 과정 중에 수행하도록 등록한다. 다시 말해, exit() 수행이나 main()에서 반환되어 프로세스가 종료될 때, 등록된 함수가 수행된다. 프로세스가 exec 계열 함수를 수행하면 등록된 함수 목록이 깨끗하게 초기화된다. 프로세스가 시그널을 받고 종료하면 등록된 함수는 호출되지 않는다.
함수는 매개 변수를 받지 않으며, 반환값도 없다. 서식(prototype)은 다음과 같다.

void my_function (void);

함수는 등록 역순으로 수행된다. 다시 말해, 함수는 LIFO 방식으로 스택에 저장된다. 등록된 함수는 exit()를 호출해서는 안 된다. exit()를 호출할 경우 무한 재귀 호출(an endless recursion)이 일어난다.

간단한 예제

                                                                                                                                  

atexit()와 같은 동작을 하는 on_exit() 함수도 있지만 최신 솔라리스 버전에서는 더 이상 이 함수를 지원하지 않으므로 표준을 따르는 atexit() 함수를 대신 사용해야 한다.

댓글 없음:

댓글 쓰기