본문 바로가기

System Programming/CSAPP Book

Ch 8-3. Process Control with C Program: getpid, exit, fork

Process Control

c 프로그램에서 라이브러리에 있는 system call 함수를 사용하면 process level에 대한 조작을 할 수 있다. 예를 들어, process의 ID를 확인하고, 새로운 process를 만들거나, 돌아가는 process를 종료시키는 등인데, 여기서는 그 예시를 코드와 함께 몇 가지 살펴보기로 한다.

 

Process ID

getpid(), getppid() 함수는 각각 calling process의 Process ID (이하 PID), calling process를 만들었던 parent process의 PID를 return한다. 다음과 같은 예시 코드를 작성하였다.

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(){
	printf("PID of caller: %d\n", getpid());
	printf("PID of parent: %d\n", getppid());
	return 0;
}

이를 반복하여 실행한 결과는 아래와 같았다.

parent PID는 같은 값이고, calling process의 PID는 달라지는 결과를 얻었다. 이는 같은 shell 프로그램이 여전히 돌아가기 때문인 것이라고 생각했는데, 실제로 새로운 shell을 켜서 마찬가지로 실행시키면 parent PID도 달라지는 것을 확인할 수 있었다.

 

Create New Process & Terminate Process

exit() 함수: process 종료시키기

exit() 함수는 process를 exit status로 종료시킨다. main() 함수에서 integer 값을 return하는 것과 같은 기능을 한다.

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

int main(){
	
	exit(0);
	printf("hello world!\n");
	//return 0;
}

예를 들어 위와 같은 코드를 실행시키면, hello world가 출력되지 않는다. exit(0)에서 이미 이 process가 terminated 상태가 되었기 때문이다.

 

fork() 함수: 새로운 child process 생성하기

fork() 함수는 parent process로부터 child process를 새롭게 만들어낸다. fork() 함수가 일반적인 함수와 다른 점 중 하나는, 한 번 호출되지만 두 번 return한다는 것이다. 이를 예시 코드를 통해 확인해보자.

#include <stdio.h>
#include <sys/types.h>	// getpid()
#include <unistd.h>		// getpid()
#include <stdlib.h>		// exit()

int main(){
	pid_t pid;
	pid = fork();
	// Child Process
	if (pid == 0) {
		printf("This is a new child process.\n");
		printf("Get PID of child process with getpid(): %d\n", getpid());
		exit(0);
	}
	// Parent Process
	else {
		printf("This is a parent process, and new child process has PID %d\n", pid);
		printf("Get PID of parent process with getpid(): %d\n", getpid());
		exit(0);
	}
	
	//return 0;
}

pid라는 변수에 fork() 함수의 return 값을 저장한다. 이 때, parent process에서는 fork() 함수가 새롭게 생성된 child process의 PID를, child process에서는 0을 return한다. 출력은 아래와 같다.

child process가 생성된 후부터, 이 두 process는 서로 동시에(concurrently) 실행된다. 여기서는 parent process에 대한 print문이 먼저 출력된 것으로 보아 parent process가 먼저 실행되었지만, 이들은 기본적으로 concurrent execution이므로 순서에 대한 가정을 하고 프로그램을 짜면 안 된다.

 

Child process는 parent process와 동일한 stack, heap, global/local variable 등을 갖지만, 서로 다른 process이기에 이들은 서로 다른 private address space를 가지고 있다. 이를 아래 예제 코드에서 int형 변수 x를 통해 확인할 수 있다.

#include <stdio.h>
#include <sys/types.h>	// getpid()
#include <unistd.h>		// getpid()
#include <stdlib.h>		// exit()

int main(){
	pid_t pid;
	int x;
	pid = fork();
	x = 2;
	
	// Child Process
	if (pid == 0) {
		x -= 1;
		printf("This is a new child process and x is %d\n", x);
		printf("Get PID of child process with getpid(): %d\n", getpid());
		exit(0);
	}
	// Parent Process
	x += 1;
	printf("This is a parent process, and x is %d\n", x);
	printf("Get PID of parent process with getpid(): %d\n", getpid());
	exit(0);
	
	//return 0;
}

먼저, child process 입장에서 보면 if 문 내로 들어오게 되는데, if 블록 내의 코드가 실행된 후에 exit()을 통해 process를 종료되므로 if 문 밖은 실행되지 않는다. x가 2에서 1로 변하기 때문에, 1이 출력된다. 한편, parent process의 경우 if 문으로 들어가지 않고 x에 1을 더하게 되는데, child process와 구분되는 private address space를 가지기 때문에 x에 1을 뺀 것과는 무관하게 2에 1을 더한 3을 출력한다.

 

출력 결과는 아래와 같다.

여기서도 마찬가지로 getpid() 함수를 통해 PID를 확인할 수 있다.