Operating System Concepts, 10th Edition을 읽고 정리하기 위해 작성하는 글입니다.
실행 과정에서 프로세스는 여러 개의 새로운 프로세스를 생성할 수 있다. 이때, 생성하는 프로세스를 부모 프로세스라고 하며, 새로운 프로세스는 해당 프로세스의 자식이다.
대부분의 운영 체제(UNIX, Linux, Windows)는 프로세스 식별자나 pid에 따라 프로세스를 식별한다. pid는 프로세스의 고유한 값이며, 커널 내에서 프로세스의 다양한 속성에 액세스하는 인덱스로 사용할 수 있다.

위의 그림은 리눅스 시스템의 일반적인 프로세스 트리를 나타낸다.
UNIX 및 Linux 시스템에서 ps 명령을 사용하여 프로세스 목록을 얻을 수 있다.
ps -el또, pstree 명령을 통해 모든 프로세스 트리를 표시할 수 있다.
프로세스가 자식 프로세스를 생성할 때, 자식 프로세스는 작업을 완료하기 위해 특정 리소스가 필요하다. 리소스는 운영 체제에 직접 얻거나, 부모 프로세스의 리소스로 제한될 수 있다. 자식 프로세스를 부모 프로세스의 리소스로 제한하면 특정 프로세스가 너무 많은 자식 프로세스를 만드는 것을 방지할 수 있다.
부모 프로세스는 자식 프로세스에 리소스를 전달하는 것 말고 초기화 데이터를 전달할 수 있다.
프로세스가 새로운 프로세스를 생성할 때 실행과 주소 공간에는 두 가지 가능성이 존재한다.
먼저 실행에서 부모 프로세스는 자식 프로세스와 동시에 계속 실행되거나 자식 프로세스 중 일부 또는 전부 종료될 때까지 기다린다. 그다음 주소 공간에서 자식 프로세스는 부모 프로세스의 복제본이거나 새로운 프로그램이 로드될 수 있다.
UNIX 운영 체제에서는 새로운 프로세스는 fork() 시스템 콜을 통해 생성되고, 기존 프로세스의 주소 공간 사본으로 구성된다. 이를 통해 부모 자식은 쉽게 통신할 수 있다. 자식 프로세스의 반환 코드는 0이지만, 부모 프로세스는 자식의 프로세스 식별자를 반환한다.
fork() 시스템 콜을 사용한뒤 부모 자식 프로세스중 하나는 exec() 시스템 콜을 사용하여 프로세스의 메모리 공간을 새 프로그램으로 대체한다.
그런 다음 부모 프로세스는 더 많은 자식 프로세스를 만들거나 wait() 시스템 콜을 실행하여 자식이 종료될 때까지 기다릴 수 있다.

위의 그림은 fork() 시스템 콜을 사용하여 프로세스를 생성하는 과정을 나타낸다.
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t pid;
/* 자식 프로세스를 fork */
pid = fork();
if (pid < 0) { /* 에러 발생 */
fprintf(stderr, "Fork Failed");
return 1;
}
else if (pid == 0) { /* 자식 프로세스 */
execlp("/bin/ls","ls",NULL);
}
else { /* 부모 프로세스 */
/* 자식이 완료될 때 까지 기다린다. */
wait(NULL);
printf("Child Complete");
}
return 0;
}
execlp() 시스템 콜(exec 시스템 콜의 특정 버전)을 사용하여 /bin/ls 명령을 주소 공간에 로드한다. (부모 프로세스로 부터 복사한 프로그램을 교체한다는 의미)wait() 시스템 콜을 사용하여 자식 프로세스가 완료될 때까지 기다린다.#include <stdio.h>
#include <windows.h>
int main()
{
/* 프로세스의 속성 지정 */
STARTUPINFO si;
/* 프로세스 및 스레드 식별자 */
PROCESS_INFORMATION pi;
/* 메모리 할당 */
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
/* 자식 프로세스 생성 */
if (!CreateProcess(NULL, "C:∖∖WINDOWS∖∖system32∖∖mspaint.exe",
NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
fprintf(stderr, "Create Process Failed");
return -1;
}
/* 부모 프로세스는 자식 프로세스가 완료될 때까지 기다린다. */
WaitForSingleObject(pi.hProcess, INFINITE);
printf("Child Complete");
/* 핸들 종료 */
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
CreateProcess()를 사용하여 Windows API에서 생성되고, fork()와 유사하다. fork()와 차이점은 프로세스 생성 시 지정된 프로그램을 자식 프로세스의 주소 공간에 로드해야한다는 점이다.CreateProcess()는 애플리케이션 이름, 명령줄, 프로세스, 스레드 핸들 상속, 생성 플래그, 부모 환경 블록, 디렉토리, STARTUPINFO 및 PROCESS_INFORMATION 포인터를 매개 변수로 사용한다.WaitForSingleObject()는 자식 프로세서의 핸들인 pi.hProcess가 전달되며 이 프로세스가 완료될때 까지 기다리는 점이 wait() 시스템 콜과 유사하다.자식 프로세스가 exit() 시스템 콜을 사용하여 종료되면 부모에게 프로세스 상태 값을 반환한다. 프로세스의 모든 리소스는 운영 체제에 의해 할당 해제 및 회수된다.
또, 프로세스는 적절한 시스템 콜을 통해 다른 프로세스를 종료시킬 수 있다.
TerminateProcess()일반적으로 이러한 시스템 콜은 종료될 프로세스의 부모에서만 호출할 수 있고, 자식 프로세스를 종료하려면 자식의 ID를 알아야한다. 따라서 한 프로세스가 새 프로세스를 만들면 새로 만든 프로세스의 ID가 부모에게 전달된다.
부모는 자식이 할당된 리소스의 사용량을 초과하는 경우, 자식에 할당된 작업이 더 이상 필요하지 않는 경우에 자식을 종료할 수 있다. 또, 부모가 종료되면 운영 체제가 자식이 계속 진행하는 것을 허용하지 않는다.
프로세스가 종료되면 해당 리소스는 운영 체제에 의해 할당 해제되지만 프로세스 테이블에 있는 해당 프로세스의 항목은 부모가 wait()을 호출할 때 까지 그대로 유지되어야 한다. 왜냐하면 프로세스 테이블에는 프로세스의 종료 상태가 포함되어 있기 때문이다.
종료되었지만 부모가 아직 wait()을 호출하지 않은 프로세스를 좀비 프로세스라고 부르고 프로세스가 종료될 때 잠깐만 좀비상태로 존재한다. 부모가 wait()을 호출하면 좀비 프로세스의 프로세스 식별자와 프로세스 테이블의 항목이 해제된다.
만약 부모가 wait()을 호출하지 않고 대신 종료하여 자식 프로세스를 고아로 남겨놓으면 UNIX 시스템은 init 프로세스를 고아 프로세스의 새 부모로 지정하여 해결한다. 또, Linux 시스템은 systemd 이외의 프로세스가 고아 프로세스를 상속하고 해당 프로세스의 종료를 관리하는 것은 허용한다.
제한된 메모리와 같은 리소스 제약으로 인해 모바일 운영 체제는 제한된 시스템 리소스를 회수하기 위해 기존 프로세스를 종료해야 할 수 있다.
Android는 임의의 프로세스를 종료하는 대신 프로세스의 중요도 계층을 식별하여 중요도가 증가하는 순서로 프로세스를 종료한다.
시스템 리소스를 회수해야 하는 경우 먼저 빈 프로세스를 종료한 다음 백그라운드 프로세스를 종료하는 식으로 진행한다.
또, Android는 개발 관행으로 프로세스 수명 주기의 지침을 따르는 것을 제안하고, 이를 통해 프로세스의 상태가 종료되기 전에 저장되며 사용자가 애플리케이션으로 돌아가면 저장된 상태에서 재기된다.