티스토리 뷰

프로세스의 생성과 복사, 전환

  1. 프로그램이 실행할 때 프로세스를 새로 생성.
  2. fork() 시스템 호출을 통해 실행중인 부모 프로세스를 복사하여 똑같은 내용의 새로운 자식 프로세스 생성.
  3. exec() 시스템 호출을 통해 기존의 프로세스를 새로운 프로세스로 전환.
  • 부모 프로세스는 여러 자식 프로세스를 가질수 있다.
  • 프로세스의 트리 (계층 구조)를 형성한다.
  • 프로세스는 자원을 필요로 하는데, 운영체제로부터 받으며 프로세스 간 공유할 수 있다.

 

자원의 공유 방식 3가지

  1. 부모와 자식이 모든 자원을 공유하는 모델
  2. 일부를 공유하는 모델
  3. 전혀 공유하지 않는 모델

→ 일반적으로 프로세스는 자원의 할당으로부터 경쟁관계이므로 대부분 공유를 하지 않는다.

 

수행 방식 2가지

  1. 부모 자식이 공존하며 수행되는 모델
  2. 자식이 종료될 때까지 부모가 wait()하는 모델

 

프로세스 관련 시스템 콜

fork()

  • 새로운 프로세스를 부모 프로세스를 복제해서 생성한다.
  • 부모를 그대로 복사 (Code, Data, Stack, binary and OS data)
  • 주소 공간 할당 (Code, Data, Stack으로 이뤄진 가상 메모리 공간)

exec()

  • (생성한, 기존) 프로세스 공간에 새로운 프로그램으로 덮어 씌워서 메인 메모리에 프로세스를 올려서 실행한다.

exit()

  • 프로세스를 종료한다
  • 프로세가 종료될 때 자식이 부모에게 output data를 보낸다 (부모는 wait() 중)
  • 항상 자식이 먼저 죽고 부모가 이 후 일을 처리한다.
  • 프로세스의 각종 자원들이 운영체제에 반납된다.

abort()

  • 부모프로세스가 자식 프로세스의 수행을 강제로 종료 시키는 경우
    • 자식이 할당 자원의 한계치를 넘어서서 사용하는 경우 (폭주)
    • 자식에게 할당된 태스크가 더 이상 필요하지 않는 경우
    • 부모가 종료하는 경우(exit), 부모가 종료되는 경우더라도 자식부터 단계적으로 죽고 부모가 죽는다.
    • 운영체제는 부모 프로세스가 종료하는 경우 자식이 더이상 수행되도록 허용하지 않는다.

프로세스의 생성과 복사 : fork()

실행중인 프로세스를 복사하여 부모 프로세스와 똑같은 자식 프로세스를 생성한다.

즉, 부모의 주소공간 (Code, Data, Stack), Program Counter (프로세스의 instruction을 어디까지 실행했는지 나타내는 레지스터) 을 복제 생성.

  • 부모와도 결국 자원을 경쟁하는 관계
  • 단, 유닉스에서 Copy-On-Write(COW)의 형태로 Write명령이 오기 전까지는 일단 부모 프로세스를 공유하면서 존재하는 경우도 있다. write가 발생하여 새로운 공간을 가질때도 메인메모리에 올라간 부모 프로세스 코드 중 달라진 부분만 새로 갖고 최대한 공유를 지향하는 형태.

 

코드

int main()
{
    int pid;
    printf("Only parent\n");
    pid = fork();
    printf("\n PID : %d", pid);
    // fork()가 일어나고 생성된 자식 프로세스는 부모의 Context를 복사
    // 해서 생성되므로 생성된 바로 다음 위치부터 수행한다 (PC부터 인지)
    if (pid == 0)
        printf("\n hello, I am Child\n");
    else if (pid > 0)
        printf("\n hello, I am Parent\n");
    return 0;
}

fork()가 실행되면 부모는 pid가 양수이고, 자식은 pid로 0을 부여해서 부모, 자식을 구분 짓는다

 

Only parent

PID : 7758
hello, I am Parent

PID : 0
hello, I am Child

 

fork() 시스템 호출로 복제되어 생성된 자식 프로세스가 별도로 갖는 값

  • 프로세스 PID : 프로세서의 구분자가 새로 생성된다.
  • 메모리 관련 정보 : 물리적 메모리의 상주 위치가 다르므로 새로 생성된다.
  • 부모 프로세스 구분자 (PPID), 자식 프로세스 구분자 (CPID) : 부모 프로세스를 가리키는 값과 자식 프로세스를 가리키는 값이 변경된다.

 

fork() 시스템 호출의 장점

  • 프로세서의 생성 속도가 빠르다.
    • 프로그램을 새롭게 메모리에 올리는 것이 아니라, 이미 메모리에 올라온 프로세스를 메모리에 복사해서 생성하는 것이므로 빠르다. 크롬 새 창을 cmd + N으로 생성하면 더 빠른 이유이다.
  • 추가 작업 없이 자원을 상속할 수 있다.
    • 부모 프로세스가 특정 파일을 사용하기 위한 세팅이 된 경우 fork된 자식 프로세스는 바로 해당 파일을 사용할 수 있다.
  • 시스템 관리를 효율적으로 할 수 있다.
    • 부모, 자식 프로세스가 PID로 연결되어 있으므로 자식 프로세스가 종료되면 부모 프로세스가 사용하던 자원을 정리해줄 수 있다.

프로세스의 전환 : exec()

어떠한 프로세스를 완전히 새로운 프로세스로 태어나게 해준다. 프로세스는 가만히 두고 내용만 전부 변경한다. 이러한 목적은 이미 만들어진 프로세스의 구조를 재활용하기 위함이다.

 

코드

int main()
{
    int pid;
    printf("Only parent\n");
    pid = fork();
    printf("\n PID : %d", pid);
    // fork()가 일어나고 생성된 자식 프로세스는 부모의 Context를 복사
    // 해서 생성되므로 생성된 바로 다음 위치부터 수행한다 (PC부터 인지)
    if (pid == 0){
        printf("\n hello, I am Child. now I will run date~\n");
        execlp("/bin/date", "/bin/date", (char*)0);
    }
    else if (pid > 0)
        printf("\n hello, I am Parent\n");
    return 0;
}

execlp() - 실행할 파일명을 두번적고, 매개변수를 큰따움표로 감싸서 콤마로 구분하여 넣은뒤 마지막에 (char*)0을 준다.

  • execlp("echo", "echo", "3", (char*)0); → echo 3

fork()를 통해 부모를 복사한 프로세스가 태어났고, execlp()명령어를 통해 새로운 프로세스를 덮어씌어서 생성하는데, /bin/date의 프로세스를 실행하도록 하는 경우이다.

 

즉, 자식 프로세스가 fork()로 부터 생성되서 exec()을 실행해서 딴 함수로 간 후 종료되며, 다시 기존 프로세스(부모프로세스)로 돌아오지 않는다.

 

Only parent

PID : 8109
hello, I am Parent

PID : 0
hello, I am Child. now I will run date~
2021년 9월 19일 일요일 17시 39분 12초 KST

참고

exec()는 자식 프로세스가 아닌 기존의 프로그램에서도 사용할 수 있다

 

exec() 시스템 호출의 장점

  • 새로운 프로세스를 만들려면 PCB를 새로 만들고, 해당 프로세스를 위한 메모리 공간을 확보하는 과정이 필요하다.
    또한, 해당 프로세스의 종료 후 메모리 관리를 위해 상위 프로세스에 부모-자식 관계를 맺어 줘야 한다.
    하지만, 이미 만들어진 프로세스에 exec()로 덮어씌운다면 이러한 과정을 생략해줄 수 있으므로 효율적이다.

wait()

해당 프로세스를 잠들게 해준다 (blocked)

  • blocked는 보통 오래걸리는 event(I/O)를 경우 발생
  • 부모 프로세스가 wait()시스템콜을 자식 프로세스를 생성한 후 호출하면,
    자식 프로세스가 종료될 때까지 기다리면서 blocked된다.
  • 이후 자식 프로세스가 종료가 되면 부모 프로세스가 ready가 되고 실행이 된다.

대표적인 wait() - shell

  • 쉘에서 입력을 기다리는 것 또한 사용자의 입력을 wait()하고 있는 것이고, 사용자가 입력한 프로세스를 처리하고 다시 부모 프로세스로 돌아가 wait()를 반복하게 되는 것이다

 

코드

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

int main()
{
    int pid;
    int status;
    printf("Only parent\n");
    pid = fork();
    printf("\n PID : %d", pid);

    if (pid == 0)
    {
        printf("\n hello, I am Child.\n");
        for (int i = 0; i < 10; i++)
            printf("%d ", i);
        printf("\n");
    }
    else if (pid > 0)
    {
        printf("\n hello, I am Parent. i will wating my child\n");
        wait(&status);
        printf("status : %d\n", status);
    }

    if (pid > 0)
        printf("parnet is DONE\n");
    else
        printf("child is DONE\n");
    return 0;
}

 

결과

Only parent

 PID : 8516
 hello, I am Parent. i will wating my child

 PID : 0
 hello, I am Child.
0 1 2 3 4 5 6 7 8 9
child is DONE
status : 0
parnet is DONE

exit()

프로세스를 종료한다

자발적 종료

  • 사용자가 명시하지 않아도, 컴파일러가 프로그램이 끝나는 시점에 넣어놓게 된다 (마지막 statement 수행 후, main()함수가 리턴되는 위치에)

비자발적인 종료

  • 부모프로세가 자식 프로세스를 종료 시키는 경우
    • 한계치를 넘어서는 자원을 요청하는경우
    • 자식에게 할당된 태스크가 더이상 필요하지 않는 경우
  • 사용자가 키보드로 kill, break을 입력하는 경우 (control c 등)
  • 부모가 종료하는 경우 (단, 자식부터 순차 종료되고 부모가 종료됨)

프로세스 간 협력 메커니즘 (IPC : Inter Process Communication)

독립적 프로세스 (Independent Process)

  • 프로세스는 각자의 주소 공간을 갖고 수행되므로 원칙적으로 하나의 프로세스는 다른 프로세스의 수행에 영향을 주지 못한다

협력 프로세스 (Cooperating Process)

  • 프로세스 간 협력 메커니즘을 통해 하나의 프로세스가 다른 프로세스의 수행에 영향을 미칠 수 있다

 

message passing

  • 메시지를 전달하는 방법
  • 커널을 통해 메시지를 전달한다.
  • 프로세스 사이에 공유 변수(Shared variable)를 일체 사용하지 않고 통신하는 시스템.
  • DirectIndirect 방법 두가지가 존재, 둘다 커널을 거쳐서 전달된다.

 

Direct Communication (통신 하려는 프로세스의 이름을 명시)

  • 프로세스 P와 Q의 통신
  • P : Q에게 메시지 전달
  • Q : P에게서 메시지 받음
  • send(Q, message) → Receive(P, message)

 

Indirect Communication (통신 하려는 프로세스의 이름을 명시하지 않음)

  • mailbox (또는 port)를 통해 메시지를 간접 전달
  • 누가 받을지 명시를 하지 않는다, 받을 프로세스는 그 mailbox에 접근해서 받으면 된다.
  • P : 메일박스에 message 전달
  • Q : 메일박스에서 message 받음
  • Send (M, message) -> Receive (M, message)
  • Process P → (Mailbox M) → Process Q

 

Shared memory

주소 공간을 공유하는 방법

  • 프로세스의 주소공간을 메인 메모리에 Mapping할 때 공유해서 mapping한다. 이때 커널한테 공유를 요청해서 되는 것
  • 공유된 이후에는 커널의 도움 없이 공유메모리를 사용해서 협력을 한다.

 

동 프로세스 내 Thread 간의 협력

  • Thread는 하나의 프로세스내 CPU 수행 단위로서 프로세스간의 협력으로 볼수는 없지만, 동일 process를 구성하는 thread끼리는 주소공간을 공유하므로 협력이 가능하다. (스레드끼리는 강한 결합, 프로세스끼리는 약한 결합)
반응형
Comments
반응형
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday