CS

OS - Program Management

wwns 2023. 10. 18. 12:21
반응형
본 글은 (KOCW) 운영체제, 이화여자대학교 반효경 교수님의 강의를 듣고 내용을 요약 및 정리했습니다.
스터디를 진행하는 것에 목적이 있으며, 자세한 사항은 여기를 참고하시면 됩니다.

프로세스의 생성(Process Creation)

https://walkccc.me/CS/OS/Chap03/#331-process-creation

  • 운영체제는 부모 프로세스(Parent Process)의 Address Space를 복제하여 자식 프로세스(Child Process)를 만든다
    • 자식 프로세스에 해당하는 PCB는 별도로 생성됨
    • 자식 프로세스는 할당받은 주소공간에 새로운 프로그램을 올림
  • 생성 단계
    • fork(): fork 시스템 콜을 통해 부모 프로세스를 복제하여 자식 프로세스를 생성
    • exec(): exec 시스템 콜을 통해 자식 프로세스가 주소공간에 새로운 프로그램을 올림
    • 두 단계는 서로 독립적
      • fork를 수행하지 않아도 exec를 통해 새로운 프로세스를 생성할 수 있음
      • 시스템 콜을 사용하기 때문에 사용자 프로그램이 운영체제에 요청을 통해 수행됨

최초의 프로세스 하나부터 시작하여 프로세스의 트리 구조를 형성

  • https://walkccc.me/CS/OS/Chap03/#331-process-creation
  • 자원을 공유하는 형태
    • 부모와 자식이 모든 자원을 공유하는 모델
    • 일부를 공유하는 모델
    • 전혀 공유하지 않는 모델

부모 프로세스와 자식 프로세스는 서로 독립적으로 존재하며 CPU제어권을 두고 경쟁하는 관계 -> 자원을 공유하지 않는 경우가 일반적이다

 

리눅스를 비롯한 일부 모델에서는 부모 자식 프로세스가 자원을 공유하다가 부모 또는 자식 프로세스에서 변경사항이 생겼을 때 해당 부분만 복제해서 물리적 메모리에할당하는 Copy-On-Write(COW) 기법을 사용

https://www.cs.uic.edu/~jbell/CourseNotes/OperatingSystems/9_VirtualMemory.html

  • Copy-On-Write는 write가 발생해서 내용이 수정됐을 때 copy를 한다는 의미로, 메모리를 보다 효율적으로 사용할 수 있다

프로세스의 수행(Execution) 형태

  • 부모와 자식이 공존하며 CPU제어권을 두고 경쟁하는 모델
  • 자식이 종료(terminate)될 때까지 부모가 기다리는(blocked, wait) 모델

프로세스의 종료 (Process Termination)

  • 프로세스가 마지막 명령을 수행한 후 운영체제에게 이를 알려준다
    • exit 시스템 콜 - 자발적인 종료
      • 프로세스는 항상 자식 프로세스가 먼저 종료되며, 자식 프로세스는 종료 시점에 wait 시스템 콜을 통해 부모에게 output data를 전달
      • 프로세스의 각종 자원들이 운영체제에게 반납됨
  • 부모 프로세스가 자식 프로세스의 수행을 종료시키는 3가지 경우
    • abort 시스템 콜 - 비자발적인 강제 종료
      • 자식이 할당 자원의 한계치를 넘어서는 경우
      • 자식에게 할당된 task가 더 이상 필요하지 않는 경우
      • 부모 프로세스가 먼저 종료(exit)되는 경우
        • 운영체제는 부모 프로세스가 종료되는 경우 자식 프로세스가 수행되도록 두지 않음
        • 자식이 생성한 모든 자손 프로세스들을 종료
          • 트리의 가장 깊은 레벨의 자손부터 단계적으로 종료시킨 후 맨 마지막에 부모 프로세스를 종료

프로세스와 관련된 시스템 콜

  • fork: 자식 프로세스를 생성할 때 사용
// Parent Process

int main() {
  pid_t pid;
  printf("\n Hello, I am parent!\n");

  /* fork a child process */
  pid = fork();	// 자식 프로세스 생성

  if (pid < 0) {
    /* error occurred */
    fprintf(stderr, "Fork Failed");
    return 1;
  } else if (pid == 0) {
    /* child process */
    /* a version of the `exec()` */
    execlp("/bin/ls", "ls", NULL);
  } else {
    /* parent process */
    /* parent will wait for the child to complete */
    wait(NULL);
    printf("Child Complete");
  }
  return 0;
}

자식 프로세스는 부모 프로세스의 문맥을 동일하게 가지고 있기 때문에 main 함수의 처음이 아니라 fork 실행 이후부터 실행을 이어간다

<‘fork를 통한 프로세스 복제 생성’의 이슈와 해결방안>

fork를 사용해서 프로세스를 복제 생성할 경우 아래 두 가지 이슈가 발생할 수 있다.

  1. 복제본이 원본 역할을 하려는 경우
  2. 부모/자식 프로세스의 flow control

-> 운영체제는 이러한 이슈들을 부모와 자식의 pid로 fork의 리턴값을 다르게 줘서 부모/자식을 구분

pid가 0이면 자식 프로세스 / 0보다 큰 양수이면 부모 프로세스 / 0보다 작은 음수면 에러를 의미한다

pid를 통해 운영체제가 부모/자식을 구분하여 프로세스의 실행 흐름을 제어할 수 있음

 

 

  • exec: 기존 코드를 새로운 코드로 덮어쓸 때 사용 (완전히 새로운 프로그램으로 덮어씌워버린다)
int main() {
  printf("Hello!");
  execlp("echo", "echo", "3", (char *)0);
  printf("Can't execute me");
}
/* print
Hello
3
*/

exec 시스템 콜과 fork 시스템 콜은 독립적이기 때문에 반드시 fork를 해야만 exec 할 수 있는 게 아니다

exec는 한 번 실행되면 되돌릴 수 없으며 코드를 새롭게 덮어쓰게 되면 새로운 코드의 실행이 끝난 뒤 돌아와 작업을 이어가는 것이 아니라 프로세스가 종료된다

 

  • wait: 자식 프로세스가 종료될 때 까지 기다릴 때 사용
// Parent Process

int main() {
  (...)

  pid = fork();

  if (pid < 0) {
    /* error occurred */
    ...
  } else if (pid == 0) {
    /* code for child process */
    ...
  } else {
    /* parent process */
    wait(NULL);
    printf("Child Complete");
  }
  ...
}

프로세스 A가 wait 시스템 콜을 호출하면 자식 프로세스가 종료될 때까지 프로세스 A를 blocked 상태로 sleep 시키고 자식 프로세스가 종료되면 다시 프로세스 A를 Ready 상태로 만듦

 

  • exit: 프로세스를 종료할 때 사용, 명시적 호출로 사용하거나 컴파일러가 알아서 종료 시점에 호출하기도 함
int main() {
  ...
  
  /* exit() 호출 */
  exit();
  printf("Can't execute me");

  ...
}
/* print

*/

exec와 마찬가지로 exit 실행 이후의 코드는 실행되지 않고 프로세스가 종료됨


프로세스 간 협력

프로세스는 각자의 주소공간을 가지고 수행되므로 하나의 프로세스가 다른 프로세스의 수행에 영향을 미치지 못하지만(독립적 프로세스, Independent process)

협력 메커니즘을 통해 하나의 프로세스가 다른 프로세스 수행에 영향을 미칠 수 있다(협력 프로세스, Cooperating process)

 

IPC(Interprocess Communication) 프로세스 간 협력 메커니즘

 

https://www.geeksforgeeks.org/inter-process-communication-ipc/

  • shared memory
    • 서로 다른 프로세스 간에 일부 주소공간을 공유 -> 물리적인 메모리의 주소공간을 공유한다 (프로세스 A, B의 주소공간을 겹치게 할당해 공유)
    • shared memory를 mapping할때만 시스템콜을 호출 -> 빠르다
  • message passing
    • 프로세스 사이에 공유 변수(shared variable)을일체 사용하지 않고 커널을 통해 통신하는 방법
    • 메시지를 보낼 때마다 시스템콜을 호출 -> 느림
    • 통신하려는 프로세스의 이름을 명시적으로 표시하는지 여부에 따라 direct / indirect로 구분

Direct Message Passing - 메시지를 받을 프로세스를 명시

Indirect Message Passing - mailbox(또는 port)를 통해 메시지를 간접적으로 전달

메시지를 메일박스에 넣으면 어떤 프로세스든 메시지를 받을 수 있음

 

  • Thread
    • 스레드는 사실상 하나의 프로세스이므로 프로세스 간 협력을 보기는 어렵지만 동일한 process를 구성하는 thread들 간에는 주소 공간을 공유하므로 협력이 가능

  • 자식 프로세스가 상태를 알리지 않고 죽거나, 부모 프로세스가 먼저 죽게 되면 어떻게 처리하나요?
    • 자식 프로세스가 상태를 알리지 않고 종료되는 경우에는 좀비 프로세스가 되며 커널에서 종료된 자식 프로세스에 대한 최소한의 정보를 저장(부모 프로세스에서 활용할 수 있으므로)하고 있게 됩니다.
      좀비 프로세스로 인한 메모리 낭비를 막기 위해 부모 프로세스에서 wait 시스템 콜을 이용해 자식 프로세스의 종료 상태를 회수하게 되면 좀비 프로세스를 제거할 수 있습니다
    • 부모 프로세스가 먼저 종료되면 자식 프로세스는 고아 프로세스가 되며, 이때 부모 프로세스는 시스템이 시작될 때 생성되는 init 프로세스(pid=1, 유닉스 계열은 데몬 프로세스)가 새로운 부모 프로세스가 되며 고아 프로세스가 작업을 종료하면 wait 시스템 콜로 종료 상태를 회수하여 좀비 프로세스가 되는 것을 방지합니다
  • IPC의 Shared Memory 기법은 프로세스 주소공간의 어디에 들어가나요? 그런 이유가 있을까요?
    • 프로세스 주소 공간 중 Data 영역에 할당되어 메모리 주소를 직접 공유하는 방식으로 데이터를 교환할 수 있게 해 줍니다
    • IPC의 기본 방식은 커널을 통해 데이터를 전달하는 반면, 공유 메모리에 읽고 쓰기 때문에 데이터 전달이 필요 없어 빠른 데이터 공유가 가능하며 효율적이게 됩니다 뿐만 아니라 여러 프로세스에 공유 메모리 주소만 공유하게 되면 접근이 가능하기 때문에 유연성이 좋습니다
    • 여러 프로세스가 동시에 접근하여 수정할 수 있기 때문에 동기화 문제, 공유 메모리의 낭비가 발생할 수 있습니다
반응형