전공/운영체제

5. Interlude : Process API

nongdamgom 2023. 12. 29. 12:44

chapter 5

 

** System call == API

 

The fork() System Call - 새로운 process를 만드는 함수

  • fork()시 자식 process가 생성되고, 이 자식 process는 부모의 code, data, stack, heap을 물려받음
  • disk에서 가져오는게 x 
  • fork() 후 자식과 부모가 병렬적으로 동시 진행한다.
  • fork 시 총 세 단계 - 복제 되면서 pc 값도 복제, fork()를 가리키고 있을것임

=> 현재는 fork 중이라서 p2 바로 실행 x p2가 나중에 실행되면 fork()의 중간부터 실행할 것이라는 의미

  1. 부모 프로세서 복제 후 자식 프로세스 생성
  2. 자식프로세스에 pid 값 줌
  3. fork() return 
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

int main(int argc, char *argv[]){
    printf("hello world (pid: %d)\n", (int) getpid());
    int rc = fork(); // 여기서부터 자식 생성, 부모-자식이 같은 코드 실행
    
    /* 부모 : rc 값에 자식의 pid 값, 자식 : rc 값에 0 값 */
    if(rc < 0){ // error
    	fprintf(stderr, "fork failed\n");
        exit(1);
    } 
    else if(rc == 0){ // child
    	printf("hello, I am child (pid: %d)\n", (int) getpid());
    }
    else{ // parent
    	printf("hello, I am parent of %d (pid: %d)\n", rc, (int) getpid());
    }
    return 0;
}

 

Result(Not deterministic)

  • 운영체제에 따라, 부모가 먼저 실행 될 수도 있고 자식이 먼저 실행 될 수도 있어서 결과가 여러개 나올 수 있음

 

 

The wait() System Call - not deterministic을 없애기 위해 도입 

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

int main(int argc, char *argv[]){
    printf("hello world (pid: %d)\n", (int) getpid());
    int rc = fork(); // 여기서부터 자식 생성, 부모-자식이 같은 코드 실행
    
    /* 부모 : rc 값에 자식의 pid 값, 자식 : rc 값에 0 값 */
    if(rc < 0){ // error
    	fprintf(stderr, "fork failed\n");
        exit(1);
    } 
    else if(rc == 0){ // child
    	printf("hello, I am child (pid: %d)\n", (int) getpid());
    } // 자식이 끝남 (밑에 실행 코드가 더 없다)
    else{ // parent
    	int wc = wait(NULL) // 부모가 먼저 실행돼도, 자식 죽을 때까지 wait
        // 자식이 죽으면 wait이 죽은 자식의 pid 값을 return 
    	printf("hello, I am parent of %d (wc: %d) (pid: %d)\n", rc, wc, (int) getpid());
    }
    return 0;
}

 

Result(Deterministic)

  • 무조건 자식이 먼저 실행할 수 있게 결정된다

 

 

The exec() System call

  • 자식이 부모 거 상속 안하고, disk에서 새로 가져오고 싶을 때 사용
  • fork() 후 exec()를 써서 disk에서 code와 data를 가져오고, stack과 heap을 새로 할당해줌
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/wait.h>

/* 시스템 상에서 어떤 정보를 주면, 그 토큰의 개수가 argc,
	그 토큰(string)들의 주소값을 담고 있는 배열이 argv*/
    
int main(int argc, char *argv[]){
    printf("hello world (pid: %d)\n", (int) getpid());
    int rc = fork(); // 여기서부터 자식 생성, 부모-자식이 같은 코드 실행
    
    // 부모 : rc 값에 자식의 pid 값, 자식 : rc 값에 0 값 
    if(rc < 0){ // error
    	fprintf(stderr, "fork failed\n");
        exit(1);
    } 
    else if(rc == 0){ // child
    	printf("hello, I am child (pid: %d)\n", (int) getpid());
        /* disk에서 code를 가져와서 실행하려면 새로운 argc, argv 필요,
           자식 process의 정보를 넘겨줘서 argv 재료를 만든다*/
        char *myargs[3]
        myargs[0] = strdup("wc");  // wc -> word count
        myargs[1] = strdup("p3.c");  // argument: file to count
        myargs[2] = NULL;  // marks end of array
        // strdup : heap에 string 복사 후 시작 주소 return
        
        execvp(myargs[0], myargs);  // 여기서 main에 필요한 argc, argv 추출
        //execvp 이후 결과값이 찍힘
        printf("this shouldn't print out"); 
        // execvp 한 후 새로운 code와 data를 받아왔기 때문에, 여기서부턴 실행되면 안됨(기존코드라)
    }
    else{ // parent
    	int wc = wait(NULL);
    	printf("hello, I am parent of %d (wc: %d) (pid: %d)\n", rc, wc, (int) getpid());
    }
    return 0;
}

 

 

 

All of the above with redirection

  • fork와 exec 사이에 간격 존재 => 부수적인 작업을 할 수 있음
  • 굳이 불편하게 fork와 exec로 나눈 이유가, 새로운 프로세스를 만들고, 그 자식 프로세스의 부가적인 일을 추가해주기 위해서인데, fork+exec 하나로 되어있으면, 부가적인 일을 하게 됐을 때 자식 프로세스에 적용 되는 것이 아닌 부모 프로세스에 적용되게 됨.