프로세스 생성 (Process Creation)
- 누가 프로세스를 만드느냐?
- 부모 프로세스(parent process)가 자식 프로세스(children process)를 생성
- 일반적으로 복제 생성을 함
- 자식은 부모의 공간을 복제함 (binary & OS data 등을 모두 복제)
- fork() 라는 system call의 역할임 : 부모를 그대로 복사, 주소공간 할당
- 복제된 곳에 새로운 프로그램을 덮어씌움
- exec() 이라는 system call의 역할임 : 새로운 프로그램을 메모리에 올림
- 복제만 해놓고 덮어씌우지 않을 수도 있음 (두 과정이 독립적이기 때문)
- 복제하지 않고 exec()을 하면 완전히 새로운 프로그램이 될수도 있음
- 사용자 프로세스가 직접 다른 프로세스를 생성하는 것은 아니고, OS에게 부탁해서 OS가 대리 생성
- fork와 exec가 system call이기 때문
- 프로세스의 트리(계층 구조) 형성
- 부모 프로세스가 자식을 여럿 복제할 수 있고, 자식이 또 자식을 복제할 수 있고.. 반복해서 트리 형태로 구성됨
- 프로세스는 자원을 필요로 함
- 자원은 보통 OS로부터 받음
- 부모 프로세스와 자원을 공유하는 경우도 있고, 그렇지 않을 수도 있음
- 원칙적으로는 자원을 공유하지 않음
- 부모 프로세스가 자식 프로세스를 생성하면, 그때부터 별도의 프로세스가 되어 자원을 두고 경쟁하게 됨
- 부모와 자식의 메모리 내용이 동일하다면, 당장 copy하기보다는 공유할 수 있는 것은 공유하는 것이 좋음. 따라서 리눅스 등의 운영체제에서는 일부 자원을 공유하는 방식을 사용
- 추후 부모와 자식의 내용이 달라지게 되면(함수 호출 등의 문제로 stack 등이 달라질 것), 그제서야 공유하던 부모의 메모리 공간 일부를 copy해서 사용 : Copy-on-write(COW) 기법
- 내용이 바뀔 때 그것을 copy해서 새로운 것을 만들고, 그 이전에는 부모의 것을 공유함
- Execution
- 부모와 자식이 공존하며 실행되는 모델
- 자식을 하나 생성하고 이것이 종료될 때까지 부모가 기다리는 모델
프로세스 종료 (Process Termination)
- 프로세스가 마지막 명령을 수행한 후 OS에게 이를 알려줌
- exit 이라는 system call을 통해 종료
- 자식이 부모에게 output data를 보냄
- wait system call의 역할임
- 항상 자식이 먼저 종료가 되고, 이후 부모가 종료되는 구조임
- 프로세스의 각종 자원들이 운영체제에게 반납됨
- 비자발적으로 프로세스가 종료되는 경우 : 부모 프로세스가 자식의 수행을 종료시킴
- abort 라는 system call을 통해 종료
- 강제 종료되는 상황 예시
- 자식이 자원의 할당치를 넘어선 요청을 하는 경우
- 자식에게 할당된 태스크가 더 이상 필요하지 않은 경우
- 부모 프로세스가 종료되는 경우
- 단계적으로 종료됨 (트리구조 밑에서부터 위로)
프로세스와 관련한 시스템 콜
fork() system call
int main()
{
int pid;
pid = fork();
if (pid == 0)
printf("\n hello, I am child!\n");
else if (pid > 0)
printf("\n hello, I am parent!\n");
}
- fork를 하고 나면 동일한 코드를 가진 child process가 생성됨
- child process는 parent process의 context(그중에서도 PC)를 그대로 복제해왔으므로, fork 이후 줄부터 실행 시작
- parent process는 그냥 하던 순서대로 fork 이후 줄부터 실행 시작
- 복제생성했을 때 자기 자신이 child인건 어떻게 알려주지?
- parent process는 fork의 return value가 양수로 나타나고(자식 프로세스의 pid를 얻게 됨), child process는 0을 return받음
exec() system call
int main()
{
int pid;
pid = fork();
if (pid == 0)
printf("\n hello, I am child!\n");
execlp("/bin/date", "/bin/date", (char*)0);
else if (pid > 0)
printf("\n hello, I am parent!\n");
}
- 덮인 새로운 프로그램(예시 코드에서는 /bin/date)을 시작부터 실행
- 한번 exec을 하면 되돌아올 수는 없음
- exec이 반드시 자식을 만들어서 해야 하는 것은 아님
- exec 이후의 코드는 영원히 실행되지 않음 (덮어써지니까)
- execlp 사용법
execlp("echo", "echo", "hello", "3", (char*)0);
wait() system call
- 프로세스를 blocked 상태로 보내는 역할
- 보통 child process를 만든 다음에 wait system call을 하게 됨
- child process가 종료될 때까지 sleep시킴(block 상태)
- child process가 종료되면 프로세스를 깨움 (ready 상태)
int main
{
int childPID;
childPID = fork();
if(childPID == 0){
pass
} else{
wait();
}
}
exit() system call
- 프로세스를 종료하는 system call
- 보통 프로세스가 자발적으로 종료될 때 실행됨
- 마지막 statement 수행 후 exit() system call 호출
- 프로그램에 명시적으로 적지 않아도 main 함수가 리턴되는 위치에 컴파일러가 삽입
- 비자발적 종료도 있을 수 있음
프로세스 간 협력
- 독립적 프로세스(Independent process)
- 프로세스는 각자의 주소 공간을 가지고 수행되므로 원칙적으로 하나의 프로세스는 다른 프로세스의 수행에 영향을 미치지 못함
- 협력 프로세스(Cooperating process)
- 프로세스 협력 매커니즘을 통해 하나의 프로세스가 다른 프로세스의 수행에 영향을 미칠 수 있음
- 경우에 따라서는 프로세스끼리 협력했을 때 더 효율적일 수 있음
프로세스 간 협력 매커니즘(IPC : Interprocess Communication)
- 프로세스 간에 정보를 주고받는 방법
- message passing
- 프로세스A가 프로세스B에 메시지를 전달하고, 이것의 영향을 받아서 프로세스B가 실행되고, 또 메시지를 보내고.. 하는 방식
- 프로세스는 매우 독립적임
- 자신의 메모리 주소공간만 볼 수 있으므로, 원칙적으로는 프로세스끼리 메시지를 전달할 방법이 없음
- 따라서 커널을 통해 메시지를 전달하는 방식 사용
- 메시지 패싱 방식
- Direct Communication
- Indirect Communication
- 메시지를 받을 프로세스의 이름을 명시하지 않음
- 받을 프로세스의 이름을 넣는 것이 아니라, 우체통(mailbox)만 명시함
- 경우에 따라서는 특정 프로세스만이 아니라, 조건을 만족하면 아무나 꺼내가라! 처럼 작동하게 할수도 있음
- shared memory
- 원칙적으로는 프로세스끼리 독자적인 주소공간을 가지는데, 그 중 일부 주소공간을 두 프로세스가 공유하도록 하는 방식
- shared memory도 프로세스끼리 당장 메모리 공간을 공유할 수 있는 것이 아니라, 커널에 shared memory를 사용할 것이라는 시스템 콜을 보내서 매핑을 한 후부터 사용이 가능
- 최초에는 커널의 도움을 받으나, 이후에는 사용자 프로세스끼리 사용
- +) Thread들은 곧 하나의 프로세스이므로, 프로세스간 협력이라고 하기는 어려우나 동일한 프로세스를 구성하는 thread 간에는 주소 공간을 공유하므로 협력이 가능
질문
참고 링크
KOCW 운영체제 - 이화여대 반효경 교수 (2014-1) 8, 9강