#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
printf("pid : %d", (int) getpid()); // pid : 29146
int rc = fork(); // 주목
if (rc < 0) {
exit(1);
} // (1) fork 실패
else if (rc == 0) { // (2) child 인 경우 (fork 값이 0)
printf("child (pid : %d)", (int) getpid());
}
else { // (3) parent case
printf("parent of %d (pid : %d)", rc, (int)getpid());
}
}
[출력]
pid: 29146
parent of 29147 (pid: 29146)
child (pid: 29147)
Parent와 Child의 순서는 non-deterministic함. 즉, 확신할 수 없음. Scheduler가 결정하는 일임!
PID: 프로세스 식별자. UNIX 시스템에서 PID는 프로세스에게 명령을 할 때 사용함
Fork()가 실행되는 순간. 프로세스가 하나 더 생기는데, 이 때 생긴 프로세스(Child)는 fork를 만든 프로세스(Parent)와 거의 동일한 복사본을 갖게 된다. 이 때, OS는 똑같은 2개의 프로그램이 동작한다고 생각하고, fork()가 return될 차례라고 생각한다. 그 때문에 새로 생성된 Process(Child)는 main에서 시작하지 않고, if문에서 시작하게 된다.
그러나 차이점이 있다. 바로 Parent와 Child의 fork() 값이 다르다는 점이다. 따라서, 완전히 동일한 복사본이라 할 수 없다.
Parent와 Child의 fork 값이 다르다는 점은 매우 유용한 방식이다.
그러나! Scheduler가 부모를 먼저 수행할지 아닐지 확신할 수 없다. 따라서 아래와 같이 출력될 수 있다.
[출력]
pid: 29146
child (pid: 29147)
parent of 29147 (pid: 29146)
위의 예시에 int wc = wait(NULL)만 추가함.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char *argv[]) {
printf("pid : %d", (int) getpid()); // pid : 29146
int rc = fork(); // 주목
if (rc < 0) {
exit(1);
} // (1) fork 실패
else if (rc == 0) { // (2) child 인 경우 (fork 값이 0)
printf("child (pid : %d)", (int) getpid());
}
else { // (3) parent case
int wc = wait(NULL) // 추가된 부분
printf("parent of %d (wc : %d / pid : %d)", wc, rc, (int)getpid());
}
}
[출력]
pid: 29146
child (pid: 29147)
parent of 29147 (wc: 29147 / pid: 29146)
wait을 통해서, Child의 실행이 끝날 때까지 기다려줌. Parent가 먼저 실행되더라도, wait()은 Child가 끝나기 전에는 return하지 않으므로, 반드시 Child가 먼저 실행됨.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char *argv[]) {
printf("pid : %d", (int) getpid()); // pid : 29146
int rc = fork(); // 주목
if (rc < 0) {
exit(1);
} // (1) fork 실패
else if (rc == 0) { // (2) child 인 경우 (fork 값이 0)
printf("child (pid : %d)", (int) getpid());
char *myargs[3];
myargs[0] = strdup("wc"); // 내가 실행할 파일 이름
myargs[1] = strdup("p3.c"); // 실행할 파일에 넘겨줄 argument
myargs[2] = NULL; // end of array
execvp(myarges[0], myargs); // wc 파일 실행.
printf("this shouldn't print out") // 실행되지 않음.
}
else { // (3) parent case
int wc = wait(NULL) // 추가된 부분
printf("parent of %d (wc : %d / pid : %d)", wc, rc, (int)getpid());
}
}
exec가 실행되면, execvp(실행파일, 전달인자) 함수는 code segment 영역에 실행 파일의 코드를 읽어와 덮어 씌운다.
씌운 이후에는 heap, stack, 다른 메모리 영역이 초기화되고, OS는 그냥 실행한다. 즉, 새로운 Process를 생성하지 않고, 현재 프로그램에 wc라는 파일을 실행한다.
그로 인해서, execvp() 이후의 부분은 실행되지 않는다.
참고 📖