3 분 소요


1. 프로세스 생성

프로세스를 새로 생성하는 방법뿐만 아니라, 실행중인 프로세스로부터 새로운 프로세스를 복사하는 방법도 있다.


1.1 fork() 시스템 콜

fork() 시스템 콜이란?

  • fork() 시스템 호출은 실행 중인 프로세스를 복사본을 생성한다.
  • 부모 - 자식 관계가 형성되며, 생성된 자식 프로세스는 부모 프로세스와 동일한 메모리(같은 코드 및 데이터)를 가진다.
  • 부모와 자식 프로세스는 서로 다른 주소 공간을 가지므로, 한 프로세스에서 일어나는 메모리 변경이 다른 프로세스에 영향을 주지 않는다.


fork() 시스템 호출의 동작 과정

  • fork() 시스템 호출을 사용하면 실행 중인 프로세스와 똑같은 프로세스가 하나 더 만들어진다. (트리 구조 형성)
  • 단, PID가 변경된다.
  • fork() 성공시 반환 값
    • 부모 : 자식 프로세스의 PID
    • 자식 : 0


fork() 시스템 호출의 예⭐

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

int main()
{
    pid_t pid;

    /* 자식 프로세스 포크 */
    pid = fork();

    if (pid < 0) {	/* pid가 0보다 작으면 포크 실패 */
        fprintf(stderr, "Fork Failed");
        return 1;
    }
    else if (pid == 0) {	/* pid가 0이면 자식 프로세스 */
        execlp("/bin/ls", "ls", NULL);
    }
    else {	/* 부모 프로세스(양수) */
        /* 자식이 끝날 때(Termination)까지 부모는 대기함 */
        wait(NULL);
        printf("Child Complete");
    }

    return 0;
}


fork() 시스템 호출의 장점

  • 프로세스의 생성 속도가 빠르다.
    • 하드디스크로부터 프로그램을 새로 가져오지 않고 기존 메모리에서 복사하기 때문이다.
  • 추가 작업 없이 자원을 상속할 수 있다.
    • 부모 프로세서가 사용하던 모든 자원을 추가 작업 없이 자식 프로세스에 상속할 수 있다.
  • 효율적으로 시스템 관리를 할 수 있다.
    • 자식 프로세스가 종료되면 사용하던 자원을 부모 프로세스가 정리해주기 때문이다.



2. 프로세스의 전환

2.1 exec()

exec() 시스템 콜이란?

  • 프로세스는 그대로 둔 채 내용만 바꾸는 시스템 콜이다.
  • exec() 시스템 콜을 하면 현재의 프로세스가 완전히 새롭게 로드된 다른 프로세스로 전환된다.
  • 즉, 원래의 코드와 데이터가 사라져버리고 완전히 새롭게 로드된다. (호출 후 반환x)


exec() 시스템 콜의 장점

  • 프로세스의 구조체를 재활용할 수 있다.
  • 이미 만들어진 자원(PCB, 부모-자식 관계, 메모리 영역)을 재활용 할 수 있어 새로운 코드 영역만 가져오면 되기 때문에 OS 작업이 수월하다.
  • PID가 변경되지 않기 때문에, 프로세스가 종료된 후 부모 프로세스로 돌아올 수 있다.


exec() 시스템 호출의 예



3. 프로세스 종료 (Process Termination)

3.1 exit 시스템 콜

부모가 자식을 강제 종료하는 경우 자식 프로세스를 다 죽이고 종료 (abort() system call)

  • 자식에게 할당한 자원을 초과 사용하려는 경우
  • 자식에게 할당한 Task가 더 이상 필요하지 않을 때
  • 부모 프로세스가 exit할 경우


3.2 좀비 vs 고아⭐

  • 부모 프로세스는 자원을 회수하기 위해 자식 프로세스가 끝날 때까지 기다려야한다.
  • 하지만 프로세스가 종료된 후에도 비정상적으로 남아 있는 프로세스들을 좀비 프로세스 또는 고아 프로세스라고 한다.


  • Zombie(좀비)
    • 자식 프로세스가 먼저 종료 되었지만, 부모 프로세스가 wait를 불러주지 않은 상태로 종료해 자식 프로세스의 종료 상태를 회수하지 못한 상태를 말한다.
    • 부모 프로세스는 좀비 프로세스의 생성을 방지하기 위해 wait()함수를 호출하여 상태를 회수해야 한다.
    • 💡컴퓨터를 오래 켜두면 왜 느려질까? 좀비 프로세스가 메모리를 차지하기 때문이다! 따라서 프로세스가 종료되면 그 프로세스가 사용한 메모리 공간을 깨끗이 청소해야 한다.
  • orphan(고아)
    • 부모 프로세스가 기다리지 않고 먼저 종료된 상태를 말한다. (좀비보다 심각한 문제)
    • 고아 상태가 된 프로세스들은 PID 1인 init프로세스가 자식 프로세스의 부모가 된다.


3.3 wait() 시스템 콜

프로세스의 종료 상태와 PID를 반환하는 함수이다.

  • wait() 시스템 콜은 자식 프로세스가 끝날 때까지 부모 프로세스가 기다린다.
  • 즉, wait() 시스템 콜을 통해 좀비와 고아 프로세스 생성을 방지할 수 있는 것이다!



4. 프로세스의 계층 구조

운영체제는 프로세스를 효율적으로 관리하기 위해 init 프로세스를 가장 먼저 만든 다음, 나머지 프로세스를 init 프로세스의 자식으로 만들어 트리 구조를 이룬다.

init 프로세스는 fork() exec() 시스템 호출을 사용하여 자식 프로세스를 만든다.



5. 문제

5.1 문제 1

문제

그림 3.30에 표시된 프로그램을 사용하여 LINE A에서 출력되는 내용을 설명하라. 『 Operating System Concept 10th 연습문제 3.1번 』


새로운 프로세스는 fork()로 생성되는데, 이때 새로운 프로세스는 원래 프로세스(부모)의 복사본이다.

원래 프로세스(부모)에서의 변경은 새로운 프로세스에 영향을 미치지 않기 때문에 A라인은 부모 프로세스의 공간이므로 자식 프로세스의 value+=15가 공유되지 않는다.

따라서 A행의 출력은 “PARENT: value = 5”가 된다.

이는 부모 프로세스에서만 value 값이 5로 변경되고, 자식 프로세스에서는 변경이 없음을 의미한다.


5.2 문제 2

문제

최초의 부모 프로세스를 포함하여 그림 3.31 에 표시된 프로그램에 의해 몇 개의 프로세스가 생성되는가? 『 Operating System Concept 10th 연습문제 3.2번 』


fork()가 호출되면 프로세스 복제되어 새로운 자식 프로세스가 생성된다.

초기 부모프로세스 1개 첫 번째 fork()를 호출하면, 현재 실행 중인 프로세스가 복제되어 총 프로세스 2개 두 번째 fork()를 호출하면, 현재 실행 중인 모든 프로세스(2개)가 복제되어 총 프로세스 4개 세 번째 fork()를 호출하면, 현재 실행 중인 모든 프로세스(4개)가 복제되어 총 프로세스 8개

이를 통해 fork()는 자식 프로세스들을 생성하고 또 다시 fork를 하면 전에 생성된 자식프로세스들도 자식을 낳는 것을 확인할 수 있다. 즉 2의 3승(2^2^2) 이 되는 것이다

즉, 8개의 프로세스가 생성된다.


5.3 문제 3

문제

프로세스가 fork() 연산을 사용하여 새로운 프로세스를 생성할 때 다음 중 어떤 상태가 부모 프로세스와 자식 프로세스 간에 공유되는가? 『 Operating System Concept 10th 연습문제 3.5번 』

  • A. 스택
  • B. 힙
  • C. 공유 메모리 세그먼트


fork()가 호출되면 프로세스는 부모 프로세스의 메모리를 복사하기 때문에 각 프로세스마다 갖는 stack, heap, data, code 영역은 공유되지 않고, 공유 메모리 세그먼트만 공유가 된다.

따라서 답은 C 이다.


카테고리:

업데이트:

댓글남기기