04. 프로세스의 생성

헬리코박도·2021년 10월 15일
0

운영체제

목록 보기
5/10

공룡책 ch03. Process -2-

fork()

  • 유닉스 라이크 운영체제에서 새로운 프로세스를 생성하는 시스템 콜
  • fork()로 생성된 자식 프로세스는 부모 프로세스의 주소 공간을 복사해옴
    • fork() 이후 두 프로세스는 각각 fork 이후의 명령어를 수행
    • fork()의 return 값이 0이면 자식 프로세스
    • return값이 0이 아닌 운영체제가 부여한 pid면 부모 프로세스, 즉 fork는 자식 프로세스의 PID를 반환함.
  • wait()를 사용하면 부모 프로세스는 자식이 종료될 때까지 기다림

코드 - 주소 공간 복사

#include <stdio.h>
#include <unistd.h>

int main()
{
    pid_t pid;
    pid = fork();
    printf("Hello, Process!\n");

    return 0;
}

Hello, Process!
Hello, Process!

자식 프로세스가 부모 프로세스의 주소 공간을 복사해오고 fork() 이후의 코드를 두 프로세스 다 실행하므로 Hello, Process가 두 번 출력됨

코드 - 리턴 값으로 구분

#include <stdio.h>
#include <unistd.h>

int main()
{
    pid_t pid;
    pid = fork();
    printf("Hello, Process! %d\n", pid);

    return 0;
}

Hello, Process! 3756
Hello, Process! 0

fork()에서 리턴 받음 pid를 출력하는데 부모는 os로부터 받은 자식의 pid를 출력하고 자식은 0을 출력함

코드 - wait()

#include <stdio.h>
#include <unistd.h>
#include <wait.h>

int main()
{
    pid_t pid;
    pid = fork();
    if (pid > 0){
        wait(NULL);
    }
    printf("Hello, Process! %d\n", pid);

    return 0;
}

Hello, Process! 0
Hello, Process! 10440

조건문을 통해 pid가 0이 아닐 부모 프로세스에 wait()를 걸음.
위에서 fork()만 하던 코드들과는 다르게 자식 프로세스가 종료된 후 부모 프로세스가 작업을 재개하므로 출력 결과를 보면 pid가 0인 자식이 먼저 printf했음을 알 수 있음.

코드 - 연습문제 3.1

Using the program shown in Figure 3.30, explain what the output will
be at LINE A

#include <stdio.h>
#include <unistd.h>
#include <wait.h>

int value = 5;

int main()
{
    pid_t pid;
    pid = fork();

    if (pid == 0) {
        value += 15;
        return 0;
    }
    else if (pid > 0) {
        wait(NULL);
        printf("Parent: value = %d\n", value);
    }
}

Parent: value = 5

부모가 wait에 들어가고 자식이 먼저 15를 더해준 후 종료되고 부모가 출력을 수행하므로 20이라고 착각할 수 있음.
그러나 자식 프로세스가 주소 공간을 복사할 때 전역 변수 value 역시 복사되므로 자식의 value값만 변경되고 부모의 value는 변경되지 않는다.

코드 - 연습문제 3.2

Including the initial parent process, how many processes are created by
the program shown in Figure 3.31?

#include <stdio.h>
#include <unistd.h>
#include <wait.h>

int main()
{
    fork();
    fork();
    fork();

    return 0;
}

위의 코드를 실행했을 때 프로세스가 총 몇 개 생성되는지에 대한 문제이다.

부모는 총 3개의 자식을 생성한다.

첫 번째 fork로 생성된 자식은 2개
두 번째 fork로 생성된 자식은 1개
세 번째 fork로 생성된 자식은 남은 fork가 없으므로 0개

첫 번째 fork로 생성된 자식의 첫 fork로 생성된 자식은 1개의 자식을 생성할 수 있다.
첫 번째 fork로 생성된 자식의 두 번째 fork로 생성된 자식은 0개

두 번째 fork로 생성된 자식의 자식은 0개

다 더해주면 3 + 2 + 1 + 1 = 7
총 자식은 7개가 생성된다.
부모까지 총 8개의 프로세스가 생성된다.

코드 - 연습문제 3.11

Including the initial parent process, how many processes are created by
the program shown in Figure 3.32?

#include <stdio.h>
#include <unistd.h>

int main()
{
    int i;
    pid_t pid;

    for (i = 0; i < 4; i++) {
        pid = fork();
    }
    
    return 0;
}

위 문제와 같은 원리로 접근하면 총 16개가 만들어진다.
자식 프로세스에서는 반복문이 처음부터 다시 시작되는 거 아닐까라고 생각할 수 있지만 지역 변수 i까지 같이 복사되므로 반복 횟수가 남아 있다.

코드 - 연습문제 3.12

Explain the circumstances under which the line of code marked
printf("LINE J") in Figure 3.33 will be reached.

#include <stdio.h>
#include <unistd.h>
#include <wait.h>

int main()
{
    pid_t pid;
    pid = fork();

    if (pid == 0) {
        execlp("/bin/ls", "ls", NULL);
        printf("LINE J\n");
    }

    else if (pid > 0) {
        wait(NULL);
        printf("Child Complete\n");
    }
    
    return 0;
}

3.27_process_creation1.c 3.29_process_creation3.c 3.31_process_creation5.c 3.33_process_creation7.c fig3_8_system_call_fork.c
3.28_process_creation2.c 3.30_process_creation4.c 3.32_process_creation6.c a.out
Child Complete

execlp는 fork로 프로세스의 주소 공간에 외부 프로그램을 덮어 씌움.
이 경우 pid == 0인 자식 프로세스에 execlp 코드가 실행되므로 ls가 덮어씌워져 printf("LINE J\n"); 코드는 실행되지 않고 ls가 실행된다.

코드 - 연습문제 3.13

Using the program in Figure 3.34, identify the values of pid at lines A, B,
C, and D. (Assume that the actual pids of the parent and child are 2600
and 2603, respectively.)

#include <stdio.h>
#include <unistd.h>
#include <wait.h>

int main()
{
    pid_t pid, pid1;
    pid = fork();

    if (pid == 0) { // child process
        pid1 = getpid();
        printf("child: pid = %d\n", pid); //A
        printf("child: pid1 = %d\n", pid1); //B
    }

    else if (pid > 0) { // parent process
        pid1 = getpid();
        printf("child: pid = %d\n", pid); //C
        printf("child: pid1 = %d\n", pid1); //D
    }
    
    return 0;
}

getpid()는 현재 실행 중인 프로세스의 pid를 얻는다.

부모 프로세스의 pid를 2600, 자식은 2603이라고 할 때
A는 0 자식이 fork로부터 반환받은 0
B는 2603 getpid()로부터 반환 받은 자식 프로세스 자신의 pid
C는 2603 fork로부터 반환받은 자식 프로세스의 pid
D는 2600 getpid()로부터 반환 받은 부모 프로세스 자신의 pid이다.

코드 - 연습문제 3.16

Using the program shown in Figure 3.35, explain what the output will
be at lines X and Y

#include <wait.h>
#include <stdio.h>
#include <unistd.h>
#define SIZE 5

int nums[SIZE] = {0,1,2,3,4};
int main()
{
    int i;
    pid_t pid;
    pid = fork();
    if (pid == 0) {
        for (i = 0; i < SIZE; i++) {
            nums[i] *= i;
            printf("CHILD: %d \n",nums[i]); /* LINE X */
            }
        }
    else if (pid > 0) {
        wait(NULL);
        for (i = 0; i < SIZE; i++) {
            printf("PARENT: %d \n",nums[i]); /* LINE Y */
            }
    }
    return 0;
}

사실상 자식 프로세스는 i의 제곱 수, 부모는 i를 그대로 반환하게 된다.

profile
Data Engineer

0개의 댓글