4-1강, Process Management 1
프로세스 생성(Process Creation)
- 부모 프로세스가 자식 프로세스를 생성한다. 보통 복제 생성을 하게 된다.
- 프로세스의 트리 형성(계층 구조)
- 프로세스는 자원을 필요로 한다.
- 운영체제로부터 받는다.
- 부모와 자원을 공유할 수도 있고 공유하지 않을 수도 있다. 원칙적으로는 별도의 프로세스가 되고 경쟁하는 사이(CPU를 얻을려고, 메모리를 더 많이 얻으려고)가 되기에 공유하지 않는다고 말할 수 있다.
- 자원의 공유
- 부모와 자식이 모든 자원을 공유하는 모델
- 일부를 공유하는 모델
- 전혀 공유하지 않는 모델
- 수행(프로세스가 실행될 때, Execution)
- 부모와 자식은 공존하며 수행되는 모델
- 자식이 종료(terminate)될 때까지 부모가 기다리는(wait) 모델
- 프로세스 생성
- 주소 공간(Address space)
- 자식은 부모의 공간(주소 공간, address space)을 복사한다.(binary, OS data(PCB 혹은 자원들)도 다 복사한다.)
- 그렇게 복제된 곳에 새로운 프로그램을 올린다.
- 따라서 서로 다른 프로그램들이 컴퓨터 내에 존재하게 된다.
- Unix의 예
- fork() 시스템 콜이 새로운 프로세스를 생성
- 부모를 그대로 복사(OS data except PID(process ID) + binary)
- 주소 공간 할당
- fork() 다음에 이어지는 exec() 시스템 콜을 통해 새로운 프로그램을 메모리에 올린다.
- 시스템 콜이기에 이러한 일은 운영체제(OS)가 수행한다.
- 복제한 다음에 필요한 부분만 덮어씌우는 형식이다.
- copy on write(COW)
- write가 발생했을 때, 즉 원래 있던 내용을 바꿀 때 copy해서 새로운 것을 만들고, 그 전까지는 부모의 자원을 공유하는 것이다. 내용이 수정되면 부모의 code, data, stack을 복제한다. 이 때 모두 복제하는 것이 아니라 잘게 쪼개진 필요한 부분만 카피한다. 가능하면 이렇게 하는 것이 효율적이나 원칙적으로는 독립적인 것이 원칙이다.
- 자식 프로세스가 종료된 다음에 부모 프로세스가 종료되어야 한다.
- 프로세스 종료
- 프로세스가 마지막 명령을 수행한 후 exit() 시스템 콜을 통해 운영체제에게 알려준다.(자발적)
- 자식이 부모에게 output data를 보내게 된다.(via wait() 시스템 콜) ex. wait(NULL)
- 프로세스의 각종 자원들이 운영체제에게 반납된다.
- 부모 프로세스가 abort() 시스템 콜을 통해 자식의 수행을 종료시킨다.(비자발적)
- 자식이 할당 자원의 한계치를 넘어설 때(자식을 낳아놨더니 너무 펑펑 쓰는 경우)
- 자식에게 할당된 태스크가 더 이상 필요하지 않음.(일 시킬려고 만들었고, 더 이상 시킬 일이 없을 때)
- 부모가 종료(exit)되는 경우
- 어쩌다 보니 부모가 종료되어야 하는 상황이 생겼다.
- 그러면 자식들을 모두 종료시킨 다음에 종료되어야 한다.
- 운영체제는 부모 프로세스가 종료되는 경우, 자식이 더 이상 수행되도록 두지 않는다.
- 자식이 자식을 낳아놓았을 수도 있다. 자식의 자식이 먼저 종료되고, 자식이 종료되고 부모가 종료되는 단계적인 종료를 걸쳐 종료된다.
4-2강, Process Management2
fork()
int main() {
int pid;
printf("\n Hello!\n");
pid = fork();
if (pid == 0) {
printf("\n Hello, I am child!\n");
} else if (pid > 0) {
printf("\n Hello, I am parent!\n");
}
}
- 부모 프로세스는 pid = fork()일 때, 즉 PC가 pid = fork()를 가리킬 때 자식 프로세스를 복제하게 된다. 복제 시 PC도 똑같이 복제되므로 자식 프로세스의 PC도 pid = fork()를 가리키고 있다.(복제된 그 당시에) main 함수의 시작 부분을 가리키고 있는 것이 아니다. 그리고 그 밑의 코드를 수행하게 된다.
- 부모 프로세스는 fork()의 반환 값(process id)으로 양수를 얻게 된다. 자식 프로세스는 0을 얻게 된다. 이를 통해 뭐가 부모이고 뭐가 자식인지 구별할 수 있게 된다.
- 부모는 Hello가 찍히지만, 자식은 그걸 출력한 기억은 있지만 출력되지는 않는다.
exec()
#include <stdio.h>
int main() {
int pid;
pid = fork();
if (pid == 0) {
printf("\n Hello, I am child! Now I'll run date\n");
execlp("/bin/date", "/bin/date", (char *)0);
printf("Can I?");
} else if (pid > 0) {
printf("\n Hello, I am parent!\n");
}
}
- execlp를 통해 다른 프로그램으로 기억이 덮어씌워지게 된다.
- execlp("/bin/date", "/bin/date", (char *)0);
- 리눅스에서의 커맨드 혹은 프로그램이다. date는 현재 날짜와 시각을 출력해주는 프로그램이다. 마지막은 NULL 포인터이다.
- 자식 프로세스는 Hello, I am child! Now I'll run date를 출력시키고 나서 date 프로그램을 실행하게 된다.
- exec()을 하고 나면 돌아갈 수 없다. 따라서 Can I?는 출력되지 않는다.
* execlp()
int main() {
printf("1");
execlp("echo", "echo", "hello", "3", (char *)0);
printf("2");
- 1이 출력되고 hello 3이 출력되고 종료된다.
execlp("echo", "echo", "hello", "3", (char *)0);
- echo라는 명령어를 실행해라. 그 인자는 hello와 3이다. 그리고 마지막에 NULL 포인터를 적어주는 것이 형식이다.
echo hello 3
wait()
- 프로세스를 잠들게 하는 것(block상태, sleep)이다.
- 오래 걸리는 event를 기다려야 할 때, 결과가 나오면 다시 깨어나게 된다.
- 프로세스가 A가 wait() 시스템 콜을 호출하면,
- 커널은 자식 프로세스가 종료될 때까지 기다리다가 프로세스 A를 sleep시킨다.
- 자식 프로세스가 종료되면 프로세스 A는 Block에서 ready 상태로 바뀌어 CPU를 얻을 수 있는 상태가 된다. (ready queue에 들어가게 된다)
- 부모 프로세스는 else문의 wait를 만나 자식 프로세스가 끝날 때까지 기다리게 된다. 즉 block 상태가 되어 CPU를 얻지 못하고 기다리게 된다.
exit()
int main() {
int pid;
pid = fork();
exit();
if (pid == 0) {
printf("\n Hello, I am child! Now I'll run date\n");
execlp("/bin/date", "/bin/date", (char *)0);
printf("Can I?");
} else if (pid > 0) {
printf("\n Hello, I am parent!\n");
}
}
- exit()을 만나 부모, 자식 모두 종료된다.
- 자발적 종료
- 마지막 statement 수행 후, 프로그램 코드에 적힌 exit() 시스템 콜을 통해
- 프로그램에 명시적으로 적어주지 않아도 main 함수가 리턴되는 위치에 컴파일러가 넣어준다.
- 비자발적 종료
- 막 실행 중 인데 밖에서 해당 프로세스를 죽이는 경우이다.
- 부모 프로세스가 자식 프로세스를 죽이는 경우(대표적)
- 자식 프로세스가 한계치를 넘어서는 자원을 요청했을 때
- 자식에게 할당된 업무가 더 이상 필요하지 않을 때
- 키보드로 kill, break 등을 친 경우(터미널에 ctrl + c를 눌렀을 경우)
- 부모가 종료하는 경우(그냥 부모의 일이 다 끝나서)
- 부모 프로세스가 종료하기 전에 자식들이 먼저 종료되어야 한다.
프로세스와 관련한 시스템 콜
- fork() : create a child(copy)
- exec() : overlay new image
- wait() : sleep until child is done
- exit() : frees all the resources, notify parent
프로세스 간 협력