Process Management
in Sidemenu / OS
프로세스 관리
프로세스 생성
- 부모 프로세스가 자신을 복제(프로세스의 context를 모두 복사)해서 자식 프로세스를 생성
- 프로세스의 계층 구조가 트리 형태로 형성
- 프로세스가 실행되려면 자원(CPU, 메모리 등)이 필요하기 때문에 자원을 운영체제로부터 받는다.
- 자원의 공유
- 부모 프로세스와 자식 프로세스가 모든 자원을 공유하는 모델
- 일부를 공유하는 모델
- 전혀 공유하지 않는 모델
- 부모 프로세스가 자식 프로세스를 낳으면 그때부터는 별도의 프로세스이기 때문에 서로 CPU와 메모리를 얻기위해 경쟁하는 사이가 되기 때문에 원칙적으로는 자원을 공유하지 않는다.
- 수행(Execution) 모델
- 부모 프로세스와 자식 프로세스가 공존하며 수행되는 모델
- 자식 프로세스가 종료(terminate)될 때까지 부모 프로세스가 기다리는(blocked) 모델
- 주소 공간
- 자식 프로세스는 일단 부모 프로세스의 공간을 복제한 뒤(fork()), 그 공간에 새로운 프로그램을 올린다.(exec())
Copy-on-write(COW) 기법
write가 발생했을 때!!!! 그 때 copy를 하겠따!!
- 자식 프로세스가 부모 프로세스를 그대로 복제하면 결국엔 메모리에 두 개가 똑같이 올라간다. 이는 메모리의 낭비를 일으키기 때문에 일단 똑같은 내용을 만든다고 하면 당장에 복제할 필요는 없다.
- Linux나 일부 모델에서는 부모 프로세스의 모든것을 복제하지 않고 공유 가능한 것들은 일단 공유해본다.
- 부모 프로세스를 복제해서 주소공간이 만들어지는게 원칙이지만 Linux나 좀 더 효율적인 운영체제에서는 복제하지 않고 일단 부모 프로세스의 주소공간을 공유하는 상태를 가진다.
- 결국 별개의 프로세스이기 때문에 stack에 쌓이는 내용이나 함수 호출 등 다른 부분이 발생하여 각자의 길을 가게 되면 그제서야 공유하던 부모의 주소공간을 복제한다.
- 주소공간을 복제할 때에도 전부를 복제하는게 아님.. 물리적인 메모리에 code, data, stack이 잘게 쪼개져서 필요한 부분만 올라가기 때문에.. 공유하는 내용과 다른 부분만 복제해서 사용한대..
ex) fork() system call
int main()
{
int pid;
pid = fork();
if(pid == 0) /* this is child */
printf("\n Hello, I am child!\n");
else if(pid > 0) /* this is parent */
printf("\n Hello, I am, parent!\n");
}
- 처음에 부모 프로세스가 실행을 하다가 fork()를 통해 자식 프로세스를 만들고 나머지 아랫부분을 수행한다.
- 자식 프로세스는 부모 프로세스의 문맥을 복제하기 때문에 fork() 함수가 끝난 뒤이기 때문에 if문부터 수행한다.
- ISSUE
- 복제를 해놨더니 지가 복제본이 아니라 원본이라고 주장하면서 부모 프로세스를 복제본 취급할 수 있다.
- 부모 프로세스를 복제했기 때문에 모두 같은 일을 하게 된다.
- 이를 방지하기 위해 fork()함수의 return 값을 다르게 준다.
- 부모 프로세스는 자식 프로세스의 pid(양수)를 얻게 되고, 자식 프로세스는 0을 얻는다.
- 이를 통해 부모 프로세스와 자식 프로세스가 서로 다른 일을 하게 할 수 있다.
ex) exec() system call
기존 프로그램
int main()
{
int pid;
pid = fork();
if(pid == 0) { /* this is child */
printf("\n Hello, I am child! Now I'll run date \n");
execlp("/bin/date", "/bin/date", (char *)0);
}
else if(pid > 0) /* this is parent */
printf("\n Hello, I am, parent!\n");
}
date 프로그램
int main()
{
printf("\n I am date program! \n");
}
- 기존 프로그램에서 부모 프로세스가 fork()를 하여 자식 프로세스가 새로 생긴다.
- 자식 프로세스는 pid가 0이기 때문에
Hello, I am child! Now I'll run date
을 print하고 execlp함수를 만나게 된다. - execlp에서 date 프로그램을 자식 프로세스에게 덮어씌우고 date 프로그램을 실행하게 한다. -> 자식 프로세스는 기존 프로그램의 execlp이후의 코드를 실행할 수 없다.
프로세스 종료
자발적인 프로세스 종료(exit)
보통은 프로세스가 종료될 때 exit이라는 system call을 통해서 종료한다.
명시적으로 exit을 수행할 수도 있고, 프로그램에 명시적으로 적어주지 않아도 main 함수가 리턴되는 위치에 컴파일러가 넣어준다.
프로세스의 구조에서는 항상 자식 프로세스가 먼저 죽고나서 부모 프로세스가 죽는 구조이기 때문에 자식 프로세스가 부모 프로세스에게 output data를 보낸다.(wait)
종료된 자식 프로세스들의 각종 자원들이 운영체제에게 반납된다.
비자발적인 프로세스 종료(abort)
- 사람이 키보드로 kill, break 하는 경우
- 어떤 자원의 할당 한계치를 넘어섰을 때 부모 프로세스가 강제로 자식 프로세스를 종료시킨다.
- 더 이상 자식 프로세스에게 시킬 일이 없을 때 강제로 자식 프로세스를 종료시킨다.
- 부모 프로세스가 exit하는 경우, 자식 프로세스가 먼저 죽어야 되기 때문에 부모 프로세스가 낳은 모든 자식 프로세스를 먼저 종료시킨 뒤 부모 프로세스가 종료된다.
- 가장 아래 자식 프로세스부터 단계적인 종료가 이루어진다.
wait() system call
- 프로세스 A가 wait() system call을 호출하면
- 커널은 child가 종료될 때까지 프로세스 A를 sleep시킨다.(block)
- Child process가 종료되면 커널은 프로세스 A를 깨운다(ready)
int main()
{
int childPID;
childPID = fork();
if(childPID == 0)
printf("\n Hello, I am child!\n");
else
wait();
}
프로세스 간 협력
- 독립적 프로세스(Independent process)
- 프로세스는 각자의 주소 공간을 가지고 수행되므로 원칙적으로 하나의 프로세스는 다른 프로세스의 수행에 영향을 미치지 못함
- 협력 프로세스(Cooperating process)
- 프로세스 협력 메커니즘을 통해 하나의 프로세스가 다른 프로세스의 수행에 영향을 미칠 수 있음
프로세스 간 협력 메커니즘(IPC:Interprocess Communication)
- Message Passing
- 프로세스 사이에 공유 변수(shared variable)를 일체 사용하지 않고 통신하는 시스템
- 프로세스는 자신의 주소 공간밖에 보지 못하기 때문에 다른 프로세스에게 메시지를 직접 보낼 수 없어 커널을 통해 메시지를 전달한다.
- Direct Communication
- 통신하려는 프로세스의 이름을 명시적으로 표시
- Send(A, message) -> Receive(B, message)
- Indirect Communication
- mailbox(또는 port)를 통해 메시지를 간접적으로 전달
- Send(M, message) -> Mailbox -> Receive(M, message)
- Shared Memory
- 서로 다른 프로세스 간에도 일부 주소 공간을 공유하게 한다.
- 서로 자신만의 주소공간을 가지고 있지만, 물리적인 메모리에 매핑할때 일부분을 공유할 수 있도록 매핑한다.
- 커널에게 share를 한다는 system call을 통해 매핑을 먼저 해놓아야 한다.
- thread
- 스레드는 사실상 하나의 프로세스이므로 프로세스 간 협력으로 보기는 어렵지만, 동일한 process를 구성하는 스레드들 간에는 주소 공간을 공유하므로 협력이 가능하다.