System Call : fork() & exec()
System call로 하드웨어를 컨트롤하면서 여러가지 작업을 수행할 수 있다.
System call을 활용한 작업 영역을 다음 세 가지로 구분한다.
- File I/O
- Process Control
- Inter Process Communication
fork와 exec는 이 중 Process Control의 영역이다.
즉, fork()와 exec()는 모두 한 프로세스가 다른 프로세스를 실행시키기 위해 사용되는 시스템 콜이다. 하지만 분명한 차이가 존재한다. 우선 각각의 동작과 예시에 대해서 알아보자.
fork()
fork() 시스템 콜은, 기존의 프로세스를 유지하면서 새로운 프로세스를 위한 메모리를 추가적으로 할당하고, 똑같은 코드를 복사하여 각각을 실행한다.
즉, fork()를 호출한 프로세스를 새로운 공간으로 전부 복사하고, 원래 프로세스는 원래 프로세스대로, 새로운 프로세스도 그 프로세스 대로 똑같은 위치부터 실행된다.
fork()를 호출하면 또 다른 프로세스를 하나 더 생성하는데, 각각에 대해 다음과 같이 이름을 붙일 수 있다.
- 부모 프로세스, Parent Process : fork()를 호출한 프로세스(기존 프로세스)
- 자식 프로세스, Child Process : fork() 호출로 인해 생성된 프로세스(새로운 프로세스)
fork() 예시
테스트 코드
#include <sys/types.h>
#include <unistd.h>
void main()
{
pid_t pid;
printf("Before the fork()\\n");
pid = fork();
if (pid == 0)
printf("This is Child. And the value of PID = %d\\n", pid);
else if (pid > 0)
printf("This is Parent. And the value of PID = %d\\n", pid);
else
printf("Fork is faild!\\n");
}
위 코드는 fork 수행 후 실행되는 프로세스가 pid를 이용해 부모 프로세스인지 자식 프로세스인지 판단하는 코드이다. fork()가 리턴하는 값인 pid가 0이면 자식, 0보다 크면 부모 프로세스이다.
출력
출력은 다음과 같이 나올 것이다.
Before the fork()
This is Child. And the value of PID = 0
This is Parent. And the value of PID = 12753
위 프로그램에서 pid = fork() 가 실행되는 순간 위 프로세스와 똑같은 프로세스가 별도의 메모리 공간에 생성된다. 이때 두 프로세스의 변수 값과 PC(Program Counter)값, 즉 다음 실행될 코드의 위치는 정확히 동일하며, pid 값만 유일하게 다르다.
따라서 PID 값을 이용해 자식 프로세스가 할 일과 부모 프로세스가 할 일을 구분할 수 있다.
이때 Child Process가 우선적으로 실행되지만, 실행 시간이 길어질 경우 운영체제의 Process Scheduler에 따라서 Child Process들과 Parent Process가 실행된다.
exec()
exec()를 호출한 프로세스의 PID가 그대로 새로운 프로세스에 적용되며, exec()를 호출한 프로세스는 새로운 프로세스에 의해 덮어 쓰여진다.
즉, exec()가 수행되면 기존의 프로세스에서 exec() 코드 아래 부분은 모두 잃게 되는 것이다.
관련 함수
관련 함수들에는 execl, execv, execlp, execvp 등이 있는데, 이에 대해 네 가지로만 간단히 구분해 보겠다.
p가 붙은 계열 | p가 안 붙은 계열 | ||
l계열 | execlp | execl | 인자를 열거하는 방식이 나열형 |
v계열 | execvp | execv | 인자를 열거하는 방식이 배열형 |
path에 잡혀 있으면 실행된다(실행 파일의 이름만 지정) | 경로를 지정하면 현재/절대경로를 기준으로 찾는다(경로로 실행 파일 지정) |
위에서도 볼 수 있듯이 exec라는 함수명은 사실 없다. exec는 어떤 일을 하는 family 명칭으로 보는 것이 더 정확하다. exec family가 하는 일은, 현재 실행되는 프로세스에서 다른 프로세스의 일을 하게 하는 것이다.
예를 들어 어떤 문서에서 어떤 문자들이 출현했는지를 판단한 뒤, 출현한 경우 문서 내에서 자주 쓰이는 키워드를 추출한다면 두 가지 프로세스로 나눌 수 있을 것이다. 이때 자주 쓰이는 키워드를 추출하는 프로그램이 별도로 만들어져 있다면, 어떤 문자들이 출현한 문서를 찾는 프로그램에서 exec family를 이용해 만들어진 프로그램 object를 이용할 수 있게 되는 것이다.
exec() 예시
테스트 코드
#include <sys/types.h>
#include <unistd.h>
main()
{
printf("executing ls\\n");
execl("/bin/ls", "ls", "-l", (char*) 0);
perror("execl failed to run ls");
exit(1);
}
출력
위 테스트 코드가 정상적으로 실행되었다면 다음과 같이 ls - l를 실행한 결과가 출력될 것이다.
execl("/bin/ls", "ls", "-l", (char*) 0); 를 실행한 순간, 해당 프로세스는 새로운 프로세스로 덮어쓰여지게 된다.
따라서 exec() 수행이 성공한다면, execl(~) 코드 아래에 있는 perror("execl failed to run ls"); 의 결과는 당연히 출력 되지 않는다.
하지만 exec() 수행이 실패했다면 ls - l 프로세스가 메모리 공간에 로딩되지 않기 때문에 perror가 실행될 것이다.
fork()와 exec()
차이점
fork()는 기존 프로세스 외에 새로운 프로세스를 위한 메모리 공간을 할당하고, 기존의 프로세스를 그대로 복사하여 같은 위치부터 실행시킨다.
또한 exec()는 기존 프로세스 내용은 버리고 거기에 새로운 프로세스를 덮어쓰고 실행시킨다.
결국 fork는 똑같은 코드를 두 번 실행하게 되고, exec는 새로운 코드만 실행하게 된다. 따라서 이 둘을 결합하면 원래의 프로세스를 유지하면서, 새로운 프로세스에서 새로운 코드만 실행하도록 할 수 있다.
fork + exec 예시
테스트 코드
#include <sys/types.h>
#include <unistd.h>
main()
{
pid_t pid;
pid = fork();
switch(pid)
{
case -1: //실패
printf("fork failed");
break;
case 0: //자식 프로세스
execl("/bin/ls", "ls", "-l", (char*) 0);
printf("exec failed");
break;
default: //부모 프로세스
wait((int*) 0); //child process가 종료될 때까지 기다림
printf("ls completed\\n");
exit(0);
}
}
코드 흐름에 따라 실행 순서를 보면
- fork로 본 프로세스 코드를 복사한 자식 프로세스 생성
- 자식 프로세스는 case문 0으로 가서 execl로 ls -l 수행. 자식 프로세스를 덮어씀.
- 부모 프로세스는 wait((int*) 0)으로 인해 자식 프로세스가 끝나길 기다림
- 자식 프로세스가 종료되면 부모 프로세스도 다음 코드를 실행하여 ls completed를 출력하고 종료
출력
따라서 출력 결과는 다음과 같을 것이다.
참고자료
fork() 와 exec()
목차 fork() & exec() fork(), exec()의 차이점 exec() 관련 함수 System Call 에서의 fork, exec fork() 예시 exec() 예시 fork() & exec() fork()와 exec()는 모두 한 프로세스가 다른 프로세스를 실행시키기 위해 사용하게
woochan-autobiography.tistory.com
'Computer Science > Operating System' 카테고리의 다른 글
Race Condition, Mutex와 Semaphore (0) | 2024.04.16 |
---|---|
IPC, Inter-Process Communication (0) | 2024.04.15 |
멀티 프로세스와 멀티 스레드 (0) | 2024.04.15 |
JAVA의 Thread (0) | 2024.04.15 |
Thread, 스레드 (0) | 2024.04.15 |