System Call Error
void를 반환하는 일부 함수를 제외한 모든 system-level function은 리턴 상태를 반드시 체크해야 한다.
if ((pid=fork())<0) {
fprintf(stderr, "fork error: %s\n", strerror(errno));
exit(0);
}
리눅스 system-level function은 에러 발생시 -1을 리턴하고 전역변수 errno을 통해 원인을 나타낸다.
Stevens-style error-handling wrapper를 작성하여 에러 확인을 할 수 있다.
// 예
void unix_error(char* msg)
{
fprintf(stderr, "%s: %s\n", msg, strerror(errno));
exit(0);
}
pid_t Fork(void)
{
pid_t pid;
if ((pid=fork())<0) unix_error("Fork error");
return pid;
}
pid = Fork();
프로그래머의 관점에서 프로세스는 세 가지 상태를 가진다고 생각할 수 있다.
Running
프로세스가 실행 중이거나 kernel에 의해 곧 실행할 작업으로 스케쥴링되어 실행 대기 중인 상태
Stopped
프로세스 실행이 중단되어 추후 signal이 있을 때 까지 스케쥴링 되지 않는 상태
Terminated
프로세스가 영구적으로 중단된 상태
프로세스가 Terminate 되는 데에는 3가지 이유가 있다.
1. terminate signal을 받은 경우
2. main에서 return된 경우
3. exit 함수를 호출한 경우
void exit(int status)main에서 정수 값을 리턴하는 방법이 있다.Parent 프로세스는 fork를 호출함으로써 child 프로세스를 생성할 수 있다.
int fork(void)코드를 통해 확인해보자
// fork.c
int main()
{
pid_t pid;
int x = 1;
pid = Fork();
if (pid==0) { /*Child*/
printf("child : x = %d\n", ++x);
exit(0);
}
/*Parent*/
printf("parent : x = %d\n", --x);
exit(0);
}
linux> ./fork
parent : x = 0
child : x = 2
concurrent program의 부분적인 순서를 파악하기 위해서 process graph를 사용할 것이다.
각 vertex는 statement의 실행이다.
a->b는 "a가 일어난 후 b가 일어난다."의 의미이다.
edge는 현재 변수들의 값들로 레이블링 될 수 있다.
printfvertices는 output으로 레이블링 될 수 있다.
각 그래프는 in-edge 없는 vertex로 시작된다.
그래프의 위상 정렬은 실행 가능한 순서를 나타낸다.


이 때 가능한(Feasible) 순서는 다음과 같다.

프로세스가 종료되어도 시스템 리소스를 소비하는 상태를 좀비(zombie)라고 한다.
Reaping
child가 종료된 후 parent가 child의 종료상태를 확인하고(wait, waitpid) 시스템 자원(메모리, PID 등)을 회수하는 작업이다.
만약 parent가 reaping을 수행하지 않고 종료되면 orphaned child는 init 프로세스(pid==1)에 의해 reaping된다. 그러므로 긴 프로세스에서는 필요할 때만 명시적 reaping이 필요하다.
wait : Child와 동기화
parent는 wait 함수를 호출하여 child를 reaping한다.
int wait(int *child_status) :
child가 종료될 때 까지 현재 프로세스를 대기시킨다.
리턴은 child가 종료될 때의 pid다.
만약 child_status != NULL면, 포인터는 child가 종료된 이유와 종료 상태를 가리킨다.

waitpid : 특정 프로세스를 기다림
pid_t waitpid(pid_t pid, int &status, int options) :
현재 프로세스를 특정 프로세스가 종료될 때 까지 대기시킨다.
execve : 프로그램 로딩, 실행
int execve(char* filename, char* argv[], char* envp[]) :
현재 프로세스에서 프로그램을 로드하고 실행시킨다.
#!로 시작하는 오브젝트 파일 또는 스크립트 파일을 로드할 수 있다.
컨벤션에 의해서 argv[0]==filename이다.
환경변수리스트 envp는 "name=value" 형태로 저장된다.
PID는 유지한 채로 code, data, stack은 덮어써진다.
에러가 없다면 리턴은 없다.


<참고자료>
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
안성용, "시스템소프트웨어", 부산대학교