2021. 1. 30. 22:04ㆍ운영체제
1. 개요
운영체제는 사용자 또는 응용 프로그램이 프로세스를 제어하고 생성할 수 있도록 인터페이스를 제공해야한다. 이러한 인터페이스를 통해 윈도우의 경우에는 작업관리자와 같은 응용 프로그램을 사용할 수 있고, 리눅스는 ps, kill과 같은 소프트웨어를 사용할 수 있게 된다.
2. 종류
(1) fork 시스템콜
이 시스템 콜의 정확한 용도를 확인하기 위해, man 페이지를 보자.
#include <unistd.h> |
fork는 fork 시스템콜을 호출한 그 위치에서 동일한 내용을 갖는 분리된 메모리 공간의 자식 프로세스를 만든다.
정리하자면,
1. (응용프로그램) 부모 프로세스가 fork를 호출한다.
2. (시스템콜 호출) trap(특권 수준 상향 하드웨어 명령어) 발생!
3. (운영체제) trap handler가 자식프로세스를 생성하는 작업을 수행
- 이 때, child에게는 fork에서 리턴하는 값을 0으로 부여 (rc == 0)
- parent에게는 fork에서 리턴하는 값을 자식 프로세스의 pid로 전달
4. return to trap! (부모를 실행할지, 자식을 실행할지, 또는 아예 다른 프로세스를 실행할 지 알 수 없다.)
5. (응용 프로그램) 자식 프로세스는 아래 코드의 else if 실행
부모는 else 실행
하는 과정을 통해 한 프로세스는 다른 프로세스를 생성할 수 있다. 이 생성한 프로세스는 fork에서 호출한 위치에서부터 pc가 실행되기 때문에, main의 시작점부터 수행하는 것은 아니다. 또한, 동일한 내용이지만 완전히 분리된 주소공간과 레지스터, PC 값을 지닌다.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char * argv[]){
int rc = fork();
if(rc < 0) {
fprintf(stderr, "포크 실패!\n");
exit(1);
} else if(rc == 0){
printf("나는 자식 프로세스인 %d입니다\n",(int) getpid());
} else {
printf("나는 %d의 부모 %d 입니다~!\n", rc, getpid());
}
return 0;
}
이 코드는 반드시 자식이 먼저 수행한다는 것을 또는 부모가 먼저 수행한다는 것을 보장하지 않는다. 이는 운영체제가 상황에 따라 스케쥴링할 프로세스를 다르게 선택하기 때문이다. 즉, nondeterminism(비결정성)이라고 하는데, 엄밀히 말하면, 스케쥴러의 정책에 의해 우선적으로 수행되는 프로세스를 결정적이나, 부모, 자식 중 누가 먼저 수행될 것인지는 결정할 수 없으므로 비결정성을 띈다고 할 수 있다.
(2) Wait() 시스템콜
wait, waitpid, waitid - wait for process to change state #include <sys/types.h> #include <sys/wait.h> pid_t wait(int *wstatus); pid_t waitpid(pid_t pid, int *wstatus, int options); int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options); All of these system calls are used to wait for state changes in a child of the calling process, and obtain information about the child whose state has changed. A state change is considered to be: the child terminated; the child was stopped by a signal; or the child was resumed by a signal. |
자식 프로세스의 상태 변화를 기다리는 시스템콜이다. 주로 자식 프로세스의 종료를 기다리지만, signal에 의한 정지와 재개를 기다릴 수도 있다.
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, int *argv[]){
int status;
int i = 0;
pid_t pids[10];
for(i = 0; i < 5; i++){
if((pids[i] = fork()) == 0){
printf("자식 프로세스 %d 입니다\n", getpid());
sleep(3);
exit(0);
}
}
wait(&status);
printf("%d\n", status);
wait(&status);
printf("%d\n", status);
wait(&status);
printf("%d\n", status);
wait(&status);
printf("%d\n", status);
wait(&status);
printf("%d\n", status);
for(i = 0; i < 5; i++){
printf("%d ", pids[i]);
}
printf("의 부모 프로세스 %d가 종료되었습니다.\n", getpid());
return 0;
}
이 소스는 자식 프로세스를 5가지 만들고, 5명의 자식을 모두 기다리기 위해 총 5번의 wait함수를 부르는 과정이다. status라는 변수의 주소를 넣어, 자식프로세스가 어떻게 종료되었는지 상태를 받아올 수 있다.(리턴 값은 종료된 자식 프로세스의 pid 또는 -1이다.)
일반적으로는 status에 0이 들어가게 되는데, kill -9 (SIGKILL)로 강제로 종료시킨 경우, status에 9가 들어가게 된다.
일반적으로는 단순히 wait이 아닌, waitpid로 옵션과 기다릴 자식프로세스의 pid를 명시해 주는 것이 코드의 복잡함에서 발생할 수 있는 에러를 야기하지 않을 수 있으므로, wait(NULL)의 사용은 자제하는 것이 좋다.
(3) exec() 시스템콜
fork로 생성한 자식 프로세스에게 다른 프로그램을 실행하도록 시스템콜이다. 실행파일의 명칭과 argument를 전달하여, 실행 파일의 코드와 정적데이터를 읽고, 현재 실행중인 프로세스에 덮어쓴다. 힙과 스택, 주소공간을 초기화한다. 즉, 달리말해, 기존에 포크된 자식 프로세스는 기존의 코드를 더 이상 실행하지 않고, 다른 프로그램이 된다는 뜻이다.
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
int rc = fork();
if(rc == 0){
char* args[] = {"echo", "laugh haha!", NULL};
//각 인수는 1. 실행 파일 명칭 2. 인수 3. 종료 알림 NULL
int result = execvp("echo", args);
printf("%d 입니다.\n", result);
printf("여기가 나올까요?\n");
} else {
wait(NULL);
printf("부모 프로세스는 %d 를 기다리고 종료되었습니다. \n",rc);
}
}
이렇게 execvp에 파일명과 인수를 넘겨주는 것으로 자식 프로세스가 실행하는 프로그램을 바꿔버린다. 여기서, exec, execvp... 등등 다양한 기능을 갖는 시스템콜들이 있으니, man페이지를 참조하길 바란다.
여기서 왜 fork랑 exec를 분리한 것일까? 이는 유닉스의 단순한 사용자프로그램 중 하나인 쉘의 활용도를 높이기 위해서이다. 분리해 놓음으로써 프로세스의 생성과 파일의 실행사이에 필요한 환경을 세팅할 수 있게 된다.
만약 분리하지 않고 echo Home! > copy.txt 라는 프로그램을 실행한다고 해보자.
"Home!"이라는 말을 copy.txt에 적는 프로그램이지만, 이를 구현하는 것은 매우 어렵게 된다. echo라는 프로그램은 인자를 표준출력으로 출력하는 함수인데, 문제는 이 프로그램의 변경 없이는 copy.txt에 원하는 결과를 넣기 어렵기 때문이다. 그러나 fork와 exec를 분리함으로써, 프로세스가 echo를 실행하기 이전에 표준출력을 copy.txt 파일의 디스크립터로 변경할 수 있게 된다.
'운영체제' 카테고리의 다른 글
운영체제 05 : 스케줄링 (0) | 2021.02.05 |
---|---|
프로세스란? (0) | 2021.01.21 |
운영체제 개요 (0) | 2021.01.21 |