이 글은 KOCW에 공개되어있는 '반효경 교수님'의 운영체제 강의 및 강의 교재 Operation System Concepts(a.k.a 공룡책🦕)의 내용을 기반으로 작성했습니다.
이번 챕터에서는 프로세스 관리에 관해 정리해보겠습니다
오류가 있다면 댓글로 정정 부탁드립니다
프로세스 생성(Process Creation)
- 부모 프로세스가 자식 프로세스를 만든다(보통은 복제 생성, 문맥 복사)
- 부모 프로세스는 자식을 여러 개 가질 수 있기 때문에 트리구조(계층 구조)를 형성한다
- 프로세스는 자원(CPU, Memory)을 필요로 하며 운영체제로부터 자원을 받거나 부모 프로세스와 공유를 하기도 한다
- 자원을 부모 프로세스와 모두 공유하는 모델, 일부를 공유하는 모델, 아예 공유하지 않는 모델이 있다(공유하지 않는 모델이 일반적)
- 자식은 부모의 주소 공간(Address Space), PCB, 자원을 복사(fork)하고, 그 공간에 새로운 프로그램을 올리게 된다(exec)
| 유닉스 예시(Fork, Exec)
1) 복제(fork)로 자식 프로세스 생성
2) Exec로 새로운 프로그램을 생성된 프로세스에 덮어 씌운다
- 1, 2 단계는 독립적이며 한 단계만 일어날 수 있다
- 2단계만 실행한다면 완전히 새로운 프로세스로 바뀔 수도 있다
- fork, exec은 시스템콜이므로 시스템 프로세스가 직접 자식을 만드는 것은 아니고 운영체제가 대신 만들어주는 것이다
| COW(Copy-On-Write)
가상 메모리 쓰기 시 복사
- 리눅스 같은 경우에는 낭비를 줄이기 위해 처음엔 copy를 하지 않고 자식이 부모 주소 공간을 공유하게 된다
- 하지만 write를 진행해 값이 달라지는 경우가 있으면 그 때 부모 메모리 공간의 일부를 copy하게 된다
프로세스 종료(Process Terminate)
- 프로세스가 마지막 명령을 수행한 후 운영체제에게 이를 알려줌(exit)
- 종료될 때 자식이 부모에게 데이터를 보내게 된다(자식 프로세스 종료 -> 부모 프로세스 종료 순서)
- 프로세스의 각종 자원들이 운영체제에 반납된다
| abort
- 비 자발적으로 자식 수행 종료
- 다음과 같은 경우에 종료된다
1) 자원의 할당치를 넘어설 때 종료
2) 자식에게 할당된 태스크가 더 이상 필요하지 않을 때 종료
3) 부모가 종료되는 경우 자식 프로세스도 종료(root->부모의 부모 -> 부모 -> 자식 -> 자식의 자식 순으로 종료, 순차적 종료)
Fork, Exec, Wait, Exit 시스템 콜
| fork
fork() 시스템 콜로 process가 새로 만들어진다
int main(){
int pid;
pid = fork(); //부모는 양수, 자식은 0을 return한다
if(pid == 0) printf("\n Child");
else if(pid > 0) printf("\n Parent");
}
- fork() 명령어 이후로 같은 프로세스(자식 프로세스)가 복제되게 된다
- 부모 프로그램 프로그램 카운터가 fork()에 머물러 있고 자식이 그대로 복사하기 때문에 자식 프로세스는 fork() 명령어 이후부터 실행하게 된다
- 운영체제가 PID를 부모의 경우 양수로 설정하고, 자식은 0으로 설정해 복제본과 원본을 구분할 수 있도록 한다
- 부모와 같은 제어 흐름을 자식이 따라가게 된다
| exec
exec 시스템 콜로 process는 다른 프로그램을 실행할 수 있다
int main(){
int pid;
pit = fork();
if(pid == 0){
printf("\n Child, date 프로그램을 실행");
execlp("/bin/date", "/bin/date", (char *) 0); //exec 시스템 콜 실행 , execlp("프로그램 이름", arg1, arg2...)
}else if (pid > 0){
printf("\n Parent");
}
}
- execlp : exec 시스템 콜 실행 명령어, 새로운 프로그램으로 덮어 씌우게 된다
- exec를 할 경우 path에 있는 프로그램의 main 함수부터 시작하게 된다
- fork를 한 후 자식 프로세스에 프로그램을 덮어씌우는 경우를 볼 수 있다(위의 예시)
- 한 번 exec를 하면 되돌아올 수 없다(아래의 실행 결과는 "hello 3"이며, 2는 절대 출력되지 않는다)
int main(){
printf("1");
printf("echo", "echo", "hello", "3", (char *) 0);
printf("2");
}
| wait
프로그램을 block 상태로 만드는 것
- 부모 프로세스는 자식 프로세스를 만든 후 자식 프로세스가 종료되는 것을 기다리며 wait을 하게 된다
- 자식 프로세스가 종료되면, 부모 프로세스가 ready 상태가 되며 CPU를 얻을 수 있게 된다
- ex) 리눅스 상 프로그램 실행 명령(enter) -> 프로그램 실행 -> 프로그램 종료 후 다음 명령어 입력 가능
| exit
1) 자발적 종료
- 마지막 statement 이후 exit 시스템 콜을 이용해 이루어짐
- 프로그램에 명시적으로 exit이라는 시스템 콜을 넣어 명시적으로 종료 가능
- ex) c의 경우 main 함수의 }(중괄호 닫힘 지점)에 exit이라는 함수를 명시적으로 쓰지 않더라도 컴파일러가 자동으로 이를 수행
2) 비자발적 종료
- 부모 프로세스가 자식 프로세스를 강제 종료시킴
- 키보드로 kill, break 등을 입력한 경우
- 부모 프로세스가 종료하는 경우 자식 -> 부모 순으로 순차적 종료가 이루어짐
프로세스 간 협력
독립적 프로세스 (Independent Process)
- 프로세스는 각자의 주소 공간을 가지고 수행되므로, 원칙적으로 하나의 프로세스는 다른 프로세스에 영향을 끼치지 못한다
협력적 프로세스 (Cooperation Process)
- 프로세스 협력 메커니즘을 통해 프로세스간 정보를 주고 받으며 협력 할 수 있는 프로세스
| IPC(Interprocess Communication)
프로세스간 협력 메커니즘(IPC)을 통해 협력적 프로세스를 만들 수 있다
1. 메시지 전달 방식
Message Passing
- 커널을 통해 메시지 전달
- Message System : 프로세스 사이에 공유 변수(shared variable)를 일체 사용하지 않고 통신하는 시스템
1) Direct Communication : 통신하려는 프로세스의 이름을 명시적으로 표시 (Process A -> Process B)
2) Indirect Communication : mail box 또는 port를 통해 메시지를 간접 전달 (Process A -> MailBox/Port -> Process B)
2. 주소 공간 공유 방식
shared Memory
- 물리적 메모리로 메모리 공간을 맵핑할 때 메모리 일부가 공유될 수 있도록 맵핑하는 것
- shared 메모리를 쓰기 위해서는 두 프로세스는 상당히 신뢰할 수 있는 관계여야 한다
Thread
- 사실상 같은 프로세스이기 때문에 프로세스 간 협력은 아님
- 동일한 process를 공유하는 Thread 간에는 주소 공간을 공유하므로 협력이 훨씬 수월하다