23 프로세스

프로세스들은 시스템의 자원들의 할당을 위한 기본적인 단위이다. 각 프로세스는 자신만의 주소공간과 (보통) 한 개의 제어 쓰레드를 갖는다. 프로세스는 프로그램을 실행한다; 당신은 같은 프로그램을 실행하는데 여러개의 프로세스를 가질 수 있지만, 각각의 프로세스는 자신의 주소공간에서 자신의 프로그램 복제본을 갖고 다른 프로그램 복사본과 독립적으로 실행된다.

프로세스들은 계층적으로 구성된다. 각 프로세스는 그것을 만들었던 부모 프로세스를 갖는다. 주어진 부모 프로세스에 의해 만들어진 프로세스는 자식 프로세스라고 불린다. 자식 프로세스는 부모 프로세스로 부터 그 속성의 대부분을 상속받는다.

이 장은 프로그램에서 어떤 자식 프로세스를 만들고, 종료하고, 제어하는지에 대해서 설명하고 있다. 실제로, 새로운 자식 프로세스를 만들로, 새로운 프로세스가 프로그램을 실행시키고, 원래의 프로그램과 자식 프로세스가 조화롭게 수행되도록 하는데에는 세 가지의 독림적인 명령이 있다.

system 함수는 다른 프로그램을 실행시키기 위한 간단하고, 이식성 있는 메커니즘을 제공한다. 그들은 자동적으로 세 개의 단계를 거친다. 만일 당신이 이러한 일들을 하는데 세심하게 제어할 필요가 있다면, 당신은 각 단계를 개별적으로 수행하는 기본 함수들을 사용할 수 있다.


23. 1 명령 실행시키기

다른 프로그램을 실행하기 위한 가장 쉬운 방법은 system 함수를 사용하는 것이다. 이 함수는 실행되는 서브프로그램의 모든 작업을 수행하지만, 당신이 세밀하게 제어를 할 수는 없다: 당신이 어떤 것을 할 수 있기 전에 그 서브 프로그램이 종료될 때까지 기다려야만 한다.

함수 : int system (const char *command)

이 함수는 쉘 커맨드로써 command를 실행한다. GNU C 라이브러리에서, 그것은 항상 커맨드를 실행하도록 디폴트 쉘 sh를 사용한다. 특별하게, 실행하기 위한 프로그램을 찾기 위해서 PATH에 있는 디렉토리들을 탐색한다. 반환값은 만일 쉘 프로세스를 만들기가 불가능하면 -1을 반환하고 그렇지 않다면 쉘 프로세스의 상황이 된다. 이 상황 코드가 어떻게 해석되어지는지에 대해서는 23. 6절 [Process Completion] 참조. system 함수는 헤더파일 `stdlib. h'에 선언되어 있다.
이식성 노트 : 어떤 C에서는 다른 프로그램을 실행할 수 있는 커맨드 프로세스의 개념을 가지지 않을 수도 있다. 당신은 커맨드 프로세스가 존재하는지에 대한 여부를 system(NULL)을 실행해봄으로써 알아낼 수 있다; 만일 그 반환값이 0이 아니면, 커맨드 프로세서는 유용하다.

popen 과 pclose 함수들은(10. 2절 [Pipe to a Subprocess] 참조) system 함수와 밀접하게 연관되어 있다. 그들은 실행되고 있는 커맨드의 표준 입/출력 채널과 통신하도록 부모 프로세스에게 허용한다.


23. 2 프로세스 만들기에 대한 원칙

이 절은 프로세스와 프로세스를 만드는데 포함되는 단계와 그것이 다른 프로그램을 실행하도록 만드는데 대한 개요가 있다. 각 프로세스는 프로세스 ID 번호에 의해 이름지어진다. 어떤 단일한 프로세스 ID는 그것이 만들어질 때 각 프로세스에게 할당되어진다. 프로세스의 수명이 다하면 부모 프로세스에게 보고된다; 그때, 그 프로세스 ID에 포함된 프로세스의 모든 자원들은 해제된다.

프로세스들은 fork 시스템 호출로 만들어진다( 그래서 새로운 프로세스를 만드는 동작을 forking a process라고 부르기도 한다. ). fork에 의해 만들어진 자식 프로세스는 오직 자신의 프로세스 ID를 제외하고는, 원래 부모 프로세스의 완전한 복제이다.

자식 프로세스를 만든 후에, 부모와 자식 프로세스 모두는 정상적으로 실행을 계속한다. 만일 당신이 당신의 프로그램에서 실행을 계속하기 전에 자식 프로세스가 실행을 끝내도록 기다리기 원한다면, fork 연산을 수행한 후에 wait 나 waitpid(23. 6절 [Process Completion] 참조)를 호출함으로써 명백하게 이일을 하도록 해야만 한다. 그들 함수들은 자식프로세스가 왜 종료되었는지에 대한 제한된 정보_예를 들면, exit 상황코드_를 준다.

새롭게 만들어진 자식 프로세스는 fork가 return을 호출한 지점에서 부모 프로세스와 같은 프로그램을 실행한다. 당신은 그 프로그램이 부모 프로세스 또는 자식 프로세스에서 실행되고 있는지에 대해서 알기 위해서 fork로부터의 반환값을 사용할 수 있다.

여러 개의 프로세스가 같은 프로그램을 실행하기는 때때로 유용하다. 그러나 자식 프로세스는 exec 함수들중의 하나를 사용해서 다른 프로그램을 실행할 수 있다; 23. 5절 [Executing a File] 참조. 프로세스가 실행시키고 있는 프로그램을 프로세스 이미지(process image)라고 부른다. 새로운 프로그램의 실행을 시작하는 것은 프로세스가 그 전의 프로세스 이미지에 대한 모든 것을 잊게 한다; 새로운 프로그램이 종료될 때, 그 전의 프로세스 이미지를 반환하지 않고, 프로그램처럼 종료한다.


23. 3 프로세스 식별

pid_t 데이터 타입은 프로세스 ID들을 나타낸다. 당신은 getpid를 호출함으로써 프로세스의 ID를 얻을 수 있다. getppid 함수는 현재 프로세스의 (이것은 또한 부모 프로세스의 ID로써 알려져 있다. ) 부모 프로세스 ID를 반환한다. 당신의 프로그램에서 그들 함수를 사용하기 위해서는 `unistd. h'와 `sys/types. h'의 헤더파일을 포함해야한다.

데이터 타입 : pid__t

pid_t 데이터 타입은 프로세스 ID를 표현하는 부호화 정수 타입이다. GNU 라이브러리에서, 이것은 int 이다.

함수 : pid_t getpid (void)

getpid 함수는 현재 프로세스의 프로세스 ID를 반환한다.

함수 : pid_t getppid (void)

getppid 함수는 현재 프로세스의 부모 프로세스 ID를 반환한다.


23. 4 프로세스 만들기

fork 함수는 프로세스를 만드는 기본동작(primitive)이다. 그것은 헤더파일 `unistd. h'에 선언되어 있다.

함수 : pid_t fork (void)

fork 함수는 새로운 프로세스를 만든다. 만일 그 명령이 성공하면, 그곳에는 부모와 자식 프로세스가 존재하고 그 둘은 fork의 반환값을 서로 다른 값으로 보게된다. 자식 프로세스 안에서는 0의 값을 반환하고 부모 프로세스 안에서는 자식 프로세스의 ID를 반환한다. 만일 프로세스 만들기가 실패하면, fork는 부모 프로세스에게 -1의 값을 반환한다.
다음의 errno는 fork를 위해 정의된 에러 상황이다.

EAGAIN

다른 프로세스를 만들만한 충분한 시스템 자원이 없거나, 사용자가 이미 너무 많은 프로세스들을 실행시키고 있다.

ENOMEM : 프로세스는 시스템이 공급할 수 있는 것보다 더 많은 공간을 필요로 한다.

다음은 부모 프로세스와는 다른, 자식 프로세스의 정해진 속성이다.

자식 프로세스는 자신만의 단일한 프로세스 ID를 갖는다.
자식 프로세스의 부모 프로세스 ID는 그 자신의 부모 프로세스의 ID이다.
자식 프로세스는 부모 프로세스가 개방한 파일 기술자의 자신 소유의 복사본을 가진다. 부모 프로세스 안에서 연속적으로 속성이 변화하는 파일 기술자들은 자식 프로세스의 파일 기술자에게 영향을 미치지 않고, 그리고 자식 프로세스에 속한 파일기술자의 속성이 변한다고 해도 그것또한 부모 프로세스의 기술자에게 영향을 미치지 못한다. 8. 7절 [Control Operations] 참조.
자식 프로세스를 위하여 경과된 프로세서 시간은 0으로 설정된다; 17. 1절 [Processor Time] 참조.
자식 프로세스는 부모 프로세스에 의해 설정된 파일 락(lock)들을 상속받지 않는다. 8. 7절 [Control Operations] 참조.
자식 프로세스는 부모 프로세스에 의해 설정된 알람(alarm)을 상속받지 않는다. 17. 3절 [Setting an Alarm] 참조.
자식 프로세스를 위해서 미처리중인 시그널의 설정(21. 1. 3절 [Delivery of Signal] 참조)은 소거된다. (자식 프로세스는 부모 프로세스로 부터 블록된 시그널의 마스크와 시그널 동작을 상속받는다. )

함수 : pid_t vfork (void)

vfork 함수는 fork와 유사하지만 더 효율적이다; 하지만, 그것을 안전하게 사용하기 위해서 따랴야만 하는 제한이 있다.
fork는 호출된 프로세스의 주소공산의 완전한 복제본을 만들고 부모와 자식 프로세스가 독립적으로 실행되도록 허용하지만, vfork는 이 복제본을 만들지 않는다. 대신에, vfork로 만들어진 자식 프로세스는 exec 함수들중의 하나가 호출될 때까지 부모의 주소공간을 분배받는다. 그것은 부모 프로세스가 일시적으로 실행을 멈추게 됨을 의미한다. 당신은 vfork로 만들어진 자식 프로세스가 부모로부터 분배받은 전역 데이터나 심지어 지역 변수들의 갱신을 허용하지 않도록 매우 주의해야만 한다. 게다가, 자식 프로세스는 호출된 vfork 함수로부터 반환할 수 없다(또는 vfork 함수의 밖으로 long jump하는 것). 이것은 부모 프로세스의 제어 정보를 매우 혼란스럽게 만든다. 만일 의심이 된다면, 대신에 fork를 사용하라. 어떤 운영체제는 실제로 vfork를 지원하지 않는다.
GNU C 라이브러리는 모든 시스템에서 vfork를 사용하도록 허용하지만, vfork가 유용하지 않다면 실제로는 fork를 실행한다. 만일 당신이 vfork를 사용하기 위해서 적당하게 예방조치를 취한다면, 당신의 프로그램은 시스템에서 대신에 fork를 사용해서라도 여전히 작업할 것이다.


23. 5 파일 실행시키기

이 절은 프로세스 이미지로써 파일을 실행시키는 exec부류의 함수들을 설명한다. 당신은 자식 프로세스가 만들어진 후에 자식 프로세스가 새로운 프로그램을 실행하도록 그들 함수들을 사용할 수 있다. 이 부류의 함수들은 같은 일을 하지만, 인수를 정하는 방법은 차이가 있다. 그들은 헤더파일 `unistd. h'에 선언되어 있다.

함수 : int execv (const char *filename, char *const argv[])

execv 함수는 새로운 프로세스 이미지로써 파일이름 filename을 실행한다. 널-종료 문자열의 배열인 argv 인수는 실행시키려는 프로그램의 메인 함수에 argv 인수로써 제공하기 위한 값으로 사용된다. 이 배열의 마지막 요소는 반드시 널 포인터가 되어야만 한다. 관례대로, 이 배열의 첫 번째 요소는 디렉토리 이름이 없는 프로그램의 파일 이름이다. 어떻게 프로그램이 그들 인수들을 억세스 하는지에 대한 자세한 것은 22. 1절 [Program Arguments] 참조. 새로운 프로세스 이미지를 위한 환경은 현재 프로세스 이미지의 environ변수로부터 획득된다; 환경에 대한 자세한 정보는 22. 2절 [Environment Variables] 참조.

함수 : int execl (const char *filename, const char *arg(), . . . )

이것은 execv와 유사하지만, argv 문자열은 배열이 아니라 개별적으로 정해져있다. 널 포인터가 마지막 인수로 주어져야만 한다.

함수 : int execve (const chat *filename, chat *const argv[], chat *const env[])

이것은 execv와 유사하지만, env 인수를 통해 새로운 프로그램을 위한 환경을 명시적으로 지정하도록 허용한다. 이것은 environ변수와 같은 형식으로써, 문자열의 배열이다; 22. 2. 1 [Environment Access] 참조.

함수 : int execle (const char *filename, const char *arg(), char *const env[], . . . )

이것은 execl과 유사하지만, 명시적으로 새로운 프로그램을 위한 환경을 정하도록 허용한다. 환경 인수는 마지막 argv 인수로써 표시된 널 포인터의 뒤에 따르고, environ 변수와 같은 형식으로 문자열의 배열이 된다.

함수 : int execvp (const char *filename, char *const argv[])

execvp 함수는 filename에 슬래쉬가 포함되어 있지 않으면 filename으로부터 완전한 파일 이름을 찾기 위해서, PATH 환경변수에 있는 디렉토리를 탐색한다는 점을 제외하고는, execv 와 유사하다 (22. 2. 2 [Standard Environment] 참조. ). 이 함수는 사용자가 선택한 곳에서 그들을 찾기 때문에, 시스템유틸리티 프로그램들을 실행하기에는 유용하다. 쉘들은 사용자가 타이핑한 커맨드를 실행하기 위해서 그것을 사용한다.

함수 : int execlp (const char *filename, const char *arg(), . . . )

이 함수는 execvp 함수처럼 filename과 같은 이름을 탐색한다는 점을 제외하고는, execl 함수와 같다. 서로 받아들인 인수 리스트와 환경 리스트의 크기는 ARG_MAX 바이트 보다 크지 않아야만 한다. 27. 1절 [General Limits] 참조. GNU시스템에서 크기(ARG_MAX와 비교하여)는 각 문자열에서, 문자열 안에 있는 문자들의 개수에 char *의 크기를 더하고, char *의 크기의 배수로 반올림된 하나를 더한다. 다른 시스템들은 크기를 셈하기 위한 다른 규칙을 갖는다.

그들 함수들은 일반적으로 반환하지 않는다. 실패가 발생하면 -1의 값을 반환한다. 보통의 파일 이름 구문 에러(6. 2. 3절 [File Name Errors] 참조. )들에 더해서, 다음의 errno는 이 그들 함수들을 위해서 정의된 에러상황이다.

    E2BIG

    새로운 프로그램의 인수 리스트와 환경 리스트의 결합된 크기가 ARG_MAX 바이트보다 크다. GNU 시스템은 인수 리스트의 크기에 아무런 제한을 두지 않기 때문에 이 에러가 발생하지는 않지만, 그 인수가 유용한 메모리의 크기보다 크다면 ENOMEN의 에러는 발생할 것이다.

    ENOEXEC : 정해진 파일이 올바른 형식이 아니기 때문에 실행될 수 없다.

    ENOMEM : 정해진 파일을 실행시키는데는 현재 유용한 것보다 더 많은 공간이 필요하다.

만일 새로운 파일의 실행이 성공하면, 그것은 마치 그 파일을 읽은 것 그 파일의 억세스 타임 필드(access time field)를 갱신한다. 9. 8. 9절[File Times] 를 참조하여, 파일의 억세스 타임에 대한 자세한 정보를 얻어라. 그 파일이 폐쇄된 지점에서 다시 어떤 것도 정해지지 않았다면, 그것은 프로세스가 종료되기전에나 다른 프로세스 이미지가 실행되기 전인 어떤 지점이다.

새로운 프로세스 이미지를 실행하는 것은 새로운 위치로 인수와 환경 문자열을 복사하고, 메모리의 내용을 완전히 바꾸는 것이다. 그러나 프로세스의 많은 다른 속성들은 변경되지 않는다.

만일 프로세스 이미지 파일의 set-user-ID 와 set-group-ID 모드 비트가 설정되면, 이것은 프로세스의 유효 사용자 ID와 유효 그룹 ID에게 영향을 미친다. 그러한 개념들은 25. 2절 [Process Persona] 에 상세하게 설명되어 있다.

현존하는 프로세스 이미지에서 무시되도록 설정된 시그널들은 새로운 프로세스 이미지에서도 또한 무시되도록 설정된다. 모든 다른 시그널들은 새로운 프로세스 이미지에서 디폴트 동작으로 설정된다. 시그널에 대한 더 많은 정보는, 21장 [Signal Handling] 참조하라.

실행중인 프로세스 이미지에서 개방한 파일 기술자들은, 그들이 FD_CLOEXEC (close-on-exec) 플래그를 설정하지 않는다면, 새로운 프로세스 이미지에서도 개방된 채로 남겨진다. 개방된 상태로 남겨진 파일들은 실행중인 프로세스 이미지로부터 파일 락들을 포함한, 개방 파일 기술의 모든 속성을 상속받는다. 파일 기술자는 8장 [Low-Level I/O] 에 상세하게 설명되어 있다.

파일과 비교하여 스트림은, exec 함수들을 통해서도 살아남지 못한다, 왜냐하면 그들은 프로세스 자체의 메모리 안에 위치하고 있기 때문이다. 새로운 프로세스 이미지는 그들이 새롭게 만든 스트림을 제외하고는 아무런 스트림도 갖지 않는다. pre-exec프로세스 이미지 안에 있는 스트림들의 각각은 그 내부에 기술자를 갖고있고, 그들 기술자는 exec을 통해서 살아남는다. (그들은 FD_CLOEXEC의 설정을 가지지 않고 제공된 것. 새로운 프로세스 이미지는 fdopen을 사용해서 새로운 스트림에 그들을 재연결 할 수 있다. (8. 4절 [Descriptors and Streams] 참조. )


23. 6 프로세스 종료

이 절에서 설명하고 있는 함수들은 자식 프로세스가 종료하거나 멈추도록 기다리는데 사용되고, 그러한 상황인지의 여부를 알아보는데 사용된다. 그들 함수들은 헤더파일 `sys/wait. h'에 선언되어 있다.

함수 : pid_t waitpid (pid_t pid, int *status_ptr, int options)

waitpid 함수는 프로세스 ID를 pid로 가진 자식 프로세스로 부터 상황 정보를 요청하는데 사용된다. 일반적으로, 호출된 프로세스는 자식 프로세스가 종료됨으로써 유용한 상황정보를 만들 때까지 잠시 중지되어 있다.

pid 인수를 위한 다른 값들은 특별한 뜻을 갖는다. -1의 값이나 WAIT_ANY는 어떤 자식 프로세스를 위한 상황정보를 요청한다; 0의 값이나 WAIT_MYPGRP는 호출된 프로세스와 같은 프로세스 그룹에 있는 어떤 자식 프로세스를 위한 정보를 요청한다; 그리고 다른 음수값 - pgid는 프로세스 그룹 ID로써 pgid를 가진 자식 프로세스를 위한 정보를 요청한다.

만일 자식 프로세스를 위한 상황정보가 즉시 유용한 상태라면, 이 함수는 기다림이 없이 즉시 반환한다. 만일 한 개의 적합한 자식 프로세스보다 많은 프로세스가 유용한 상황 정보를 갖고있다면, 그둘중의 하나가 임의로 선택되고, 그 상황은 즉시 반환된다. 다른 적합한 자식 프로세스로 부터 상황을 얻기 위하여, 당신은 waitpid를 다시 호출할 필요가 있다.

options 인수는 비트마스크이다. 그 값은 0이나 WNOHANG와 WUNTRACED 플래그들이 비트별 OR로 조합되어질 것이다. 당신은 부모 프로세스가 기다리지 않을 것임을 지적하기 위해서 WNOHANG 플래그를 사용할 수 있다; 그리고 WUNTRACED플래그는 종료된 프로세스 뿐만 아니라 멈추어진 프로세스들로 부터 상황 정보를 요청하기 위해서 사용되어진다.

자식 프로세스로 부터의 상황정보는 status_ptr이 널 포인터가 아니라면, status_ptr이 가리키고 있는 오브젝트 안에 저장된다. 반환값은 보통 보고된 상황을 가진 자식 프로세스의 프로세스 ID 가 된다.

만일 WNOHANG 옵션이 지정됐고 어떤 자식 프로세스도 기다리고 있지 않다면, 그 값은 0이된다. 에러가 발생한 경우에 -1을 반환한다. 다음의 errno는 이 함수를 위해서 정의된 에러상황이다.

    EINTR

    그 함수는 호출된 프로세스에게 배달된 시그널에 의해 인터럽트 되어졌다. 21. 5절 [Interrupted Primitives] 참조.

    ECHILD

    그곳에 기다리고 있는 자식 프로세스가 아무 것도 없거나, 정해진 pid가 호출된 프로세스의 자식프로세스가 아니다.

    EINVAL : 올바르지 못한 값이 options 인수로써 공급되었다.

다음 기호 상수들은 waitpid 함수에게 pid 인수를 위한 값으로써 정의되었다.

    WAIT_ANY

    이 상수 매크로는 waitpid가 어느 자식 프로세스에 대한 상황정보를 반환하도록 지정되었다. (값은 -1)

    WAIT_MYPGRP

    이 상수는(값은 0) 호출된 프로세스와 같은 프로세스 그룹 안에 있는 어떤 자식 프로세스에 대한 상황정보를 반환하도록 지정되었다.

다음의 기호 상수들은 waitpid 함수의 options 인수를 위한 플래그로써 정의되었다. 당신은 그들은 비트별-OR 연산을 통해서 조합할 수 있다.

    WNOHANG

    이 플래그는 만일그곳에 준비된 자식 프로세스가 없다면, waitpid가 기다리지 않고 즉시 반환하도록 지정되었다.

    WUNTRACED

    이 플래그는 종료된 것뿐만 아니라 멈추어진 자식 프로세스에 대한 상황을 보고하도록 지정되었다.

함수 : pid_t wait (int *status_ptr)

이 함수는 waitpid의 간소화된 변형이고, 어떤 한 개의 자식 프로세스가 종료될 때까지 기다리는데 사용된다. 호출은:
wait (&status)
는 정확히 다음과 동등하다:
waitpid (-1, &status, 0)

다음은 기다림이 없이, 종료된 모든 자식 프로세스에서 보내온 상황을 얻기 위해서는 waitpid를 어떻게 사용하는지에 대한 예제가 있다. 이 함수는 SIGCHLD 시그널을 위한 핸들러로써 만들어졌는데, 그 시그널은 적어도 한 개의 자식 프로세스가 종료되었음을 알리기위해서 발생 된다.

void
sigchld_handler (int signum)
{
int pid;
int status;
while (1)
{
pid = waitpid (WAIT_ANY, &status, WNOHANG);
if (pid < 0)
{
perror ("waitpid");
break;
}
if (pid == 0)
break;
notice_termination (pid, status);
}
}


23. 7 프로세스 종료 상황들

만일 자식 프로세스의 종료 상황 값(22. 3절 [Program Termination] 참조. )이 0이면, waitpid 또는 wait에 의해 보고된 상황 값 또한 0이다. 당신은 다음의 매크로를 사용해서 반환된 상황값안에 있는 암호화된 정보를 테스트할 수 있다.

매크로 : int WIFEXITED (int status)

이 매크로는 만일 자식 프로세스가 exit 또는 _exit로써 정상적으로 종료되면 0이 아닌 값을 지닌다.

매크로 : int WEXITSTATUS (int status)

만일 status가 참인 상태의 WIFEXITED이면, 이 매크로는 자식프로세스로부터의 종료 상황 값의 하위 8비트를 반환한다. 22. 3. 2절 [Exit Status] 참조.

매크로 : int WIFSIGNALED (int status)

이 매크로는 만일 자식 프로세스가 처리되지 않은 시그널을 받았기 때문에 종료되었다면 0이 아닌 값을 반환한다. 21장 [Signal Handling] 참조.

매크로 : int WTERMSIG (int status)

만일 status가 참인 WIFSEGNALED이면, 이 매크로는 자식 프로세스를 종료시켰던 시그널의 시그널 번호를 반환한다.

매크로 : int WCOREDUMP (int status)

이 매크로는 자식 프로세스가 종료되고 코어를 생성했다면, 0이 아닌 값을 반환한다.

매크로 : int WIFSTOPPED (int status)

이 매크로는 만일 자식 프로세스가 멈추어져있다면 0이 아닌 값을 반환한다.

매크로 : int WSTOPSIG (int status)

만일 status가 참인 WIFSTOPPED 이면, 이 매크로는 자식 프로세스를 멈추게 한 원인이 된 시그널의 시그널 번호를 반환한다.


23. 8 BSD 프로세스 Wait 함수들

GNU 라이브러리는 BSD 유닉스와의 호환성을 위해서 그들과 연관된 기능들을 제공한다. BSD는 int 와 다르게 상황 값을 표현하는 union wait 데이터 타입을 사용한다. 두 개의 표현은 실제로 상호간에 변경 가능하다; 그들은 동일한 비트 패턴을 표현하기 때문이다. WEXITSTATUS 와 같은 매크로를 정의하고 있는 GNU C 라이브러리는 오브젝트의 둘중 한 종류를 선택해서 작업할 것이고, wait 함수는 status_ptr 인수로써 포인터의 한 종류를 받아들이도록 정의된다. 그들 함수들은 `sys/wait. h'에 선언되어 있다.

데이터 타입 : union wait

이 데이터 타입은 프로그램 종료 상황 값들을 표현하고, 다음과 같은 멤버들을 갖는다.

int w_termsig

이 멤버의 값은 매크로 WTERMSIG의 결과와 같다.

int w_coredump

이 맴버의 값은 매크로 WCOREDUMP의 결과와 같다.

int w_retcode

이 맴버의 값은 매크로 WEXITSTATUS의 결과와 같다.

int w_stopsig

이 맴버의 값은 매크로 WSTOPSIG의 결과와 같다.

직접적으로 이 맴버들을 억세스 하는 대신에, 당신은 동등한 매크로 를 사용하도록 하라.

함수: pid_t wait3 (union wait *status_ptr, int options, struct rusage *usage)

usage가 널 포인터라면, wait3는 waitpid(-1, status`ptr, options)와 같다. 만일 usage가 널 포인터가 아니라면, wait3는 *rusage에 있는 자식 프로세스를 위한 사용 형태를 저장한다(그러나 오직 멈추어 있는 것이 아니라, 종료된 자식 프로세스라면. )17. 5절 [Resource Usage] 참조.

함수 : pid_t wait4 (pid_t pid, union wait *status_ptr, int options, struct rusage *usage)

만일 usage가 널 포인터라면, wait4는 waitpid (pid, status`ptr, options)와 같다. 만일 usage가 널 포인터가 아니라면, wait4는 *rusage에 있는 자식 프로세스를 위하여 사용 형태들을 저장한다. (그러나 오직 자식 프로세스는 멈추어 있는 것이 아니라, 종료된 것이다. ) 17. 5절 [Resource Usage] 참조.


23. 9 프로세스 만들기 예제

다음은 내장 시스템과 유사한 함수를 어떻게 만들 것인지를 보여주는 예제 프로그램이다. 그것은 `sh -c command'와 동등한 것을 사용하여 command 인수를 실행한다.

#include <stddef. h>
#include <stdlib. h>
#include <unistd. h>
#include <sys/types. h>
#include <sys/wait. h>
/* 쉘 프로그램을 사용해서 command 를 실행한다. */
#define SHELL "/bin/sh"
int
my_system (const char *command)
{
int status;
pid_t pid;
if ((pid = fork()) == 0) { /* 이것은 자식 프로세스로써 쉘 커맨드를 실행한다. */
execl (SHELL, SHELL, "-c", command, NULL);
_exit (EXIT_FAILURE);
} else if (pid < 0) { /* fork가 실패하였으므로 실패를 보고하라. */
status = -1;
} else { /* 이것은 부모 프로세스로써, 자식이 수행을 완료할 때까지 기다린다. */
if (waitpid (pid, &status, 0) != pid)
status = -1;
}
return status;
}

당신이 이 예제에서 주목해야 할 것이 두 가지 있다. 프로그램에 공급된 첫 번째 argv 인수는 실행시키려는 프로그램의 이름을 표현한다는 것을 기억하라. 그것은 execl의 호출에서, SHELL에게 일단 실행하려는 프로그램의 이름이 주어지고 두 번째로 argv[0]을 위한 값을 공급하는 이유이다.

자식 프로세스에서 execl 호출은 만일 그것이 성공하면 반환하지 않는다. 만일 그것이 실패하면, 당신은 자식 프로세스가 종료되도록 무엇인가를 해야만 한다. 단지 return을 통하여 나쁜 상황 코드만을 반환하는것은 원래의 프로그램을 실행시키던 두개의 프로세스를 그냥 남기게 된다. 대신에, 올바른 처리는 부모 프로세스에게 자식 프로세스가 실패를 보고하는 것이다.

그것을 수행하도록 -exit를 호출하라. exit 대신에 _exit를 사용하는 이유는 stdout와 같은 완전히 버퍼화된 스트림들을 플러쉬 하는 것을 피하기 위함이다. 아마도 데이터를 담고 있는 그들 스트림의 버퍼들은 fork에 의해 부모 프로세스로 부터 복사된 것이고, 데이터는 부모 프로세스에 의해 결국 출력될 것이다. 자식 프로세스에서 호출된 exit는 데이터를 두 번 출력할 것이다. 22. 3. 5절 [Termination Internals] 참조.