[운영체제] 프로세스의 생성
서론
이 강의는 유튜브 - 주니온TV의 운영체제 강의를 듣고 정리한 내용을 기록한 게시물입니다.
프로세스의 정의
Process는 ‘Program in execution’의 약어로, 명령어의 집합인 Program이 실행상태에 있는 것을 말한다.
그렇다면 프로그램이 실행되는 상태라는 것은 어떤 상태일까?
바로 메모리(RAM)에 로드 되어 있는 상태를 말한다.
이렇게 메모리에 올라온 정보를 CPU가 처리하게 되는데, 동시적인 처리를 위해 CPU는 여러 프로세스를 바꿔가며 마치 동시에 처리되는 것처럼 보이도록 한다. 이 과정에서 하나의 프로세스를 다른 프로세스로 바꾸는 것을 ‘컨텍스트 스위칭’ 이라고 한다.
컨텍스트 스위칭
컨텍스트 스위칭 시에는 프로세스가 가진 정보를 저장하고 이를 불러올 공간이 필요한데, 이를 PCB (Process Control Block) 라고 한다. CPU가 스케줄러에 의해 다른 프로세스로 바뀌게 되면, 현재까지 작업한 내용과 리소스들을 PCB에 기록하고, 다음 작업할 프로세스의 PCB에서 정보를 불러와 다음 작업을 실행한다. 이러한 컨텍스트 스위칭을 통해 멀티태스킹이 가능해진다.
프로세스의 생성, fork()
새로운 프로세스는 fork() 라는 시스템콜을 통해 실행된다. 자식 프로세스가 필요한 경우에는 현재 프로세스의 주소 공간을 복사하여 자식 프로세스에 집어 넣게 되고, 이를 계속 실행된다.
자식 프로세스의 경우 fork()가 0을 리턴하고, 부모 프로세스의 경우는 실제로 부여된 pid를 리턴한다. 이를 알고있으면 어떤 게 부모 프로세스인지, 자식 프로세스인지 구분이 가능하다.
1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid;
pid = fork()
printf("Hello, Process!\n", pid)
return 0;
}
따라서 위와 같은 코드를 실행하면, 먼저 프로세스가 실행되고, fork()에 의해서 자식 프로세스가 생성된다. 따라서 전체의 로직이 두번 실행되므로, 다음과 같은 결과가 나올것이다.
1
2
Hello, Process 2547
Hello, Process 0
두번째 프로세스는 자식 프로세스이므로 0을 출력하는것을 볼 수 있다.
wait()
부모프로세스는 wait() 시스템콜을 통해 자식 프로세스가 작업을 마칠때까지 기다리게 할 수도 있는데, 예시를 보면 다음과 같다.
1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid;
pid = fork()
if (pid > 0)
wait(NULL)
printf("Hello, Process!\n", pid)
return 0;
}
fork()를 통해 자식프로세스를 생성하고, 부모프로세스는 if문에 걸려 wait() 시스템콜을 실행하게 된다. 따라서 부모 프로세스는 웨이팅 큐에 들어가게 되고, 자식 프로세스가 먼저 실행되게 된다.
실행결과는 다음과 같다.
1
2
Hello, Process 0
Hello, Process 3523
wait() 시스템콜로인해 자식 프로세스가 먼저 작업을 끝마친것을 확인 할 수 있다.
그럼 다음과 같은 예는 어떨까?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <unistd.h>
int value = 5;
int main() {
pid_t pid;
pid = fork()
if (pid == 0) {
value += 15;
return 0;
}
else if (pid > 0) {
wait(NULL)
printf("Hello, Process!\n", pid)
}
}
단순하게 그냥 보면 20이 출력된다고 생각하기 쉽다.
하지만 출력은 다음과 같이,
1
5
5가 출력된다.
하나씩 따라가보면, main() 함수의 실행을 통해 프로세스가 실행되다가 fork()를 통해 이 프로세스의 자식프로세스가 생성되고, 현재 CPU를 점유중인 프로세스는 자식 프로세스가 아니므로 pid가 0이 아니기 때문에 elseif문에 들어가게 된다. 따라서 wait() 시스템 콜을 받아 CPU 점유를 중지하고 웨이팅 큐에 들어가게 된다.
그럼 이제 pid가 0인 자식 프로세스가 CPU를 점유하게 되는데, 아까 fork()가 일어났을때의 시점을 생각해보면, 자식 프로세스가 fork() 됐을때의 시점은 value가 5이다. 따라서 해당 프로세스의 value에 15를 더해 value는 20이 되고, return을 만나 CPU 점유를 중단한다.
그럼 아까 작업하던 부모 프로세스가 CPU를 다시 점유하게 되는데, 부모 CPU의 wait() 다음에 있는 printf문을 출력하게되고, 부모 프로세스의 value는 변하지 않았으므로 5를 출력하게 된다.
execlp()
그럼, 다른 프로세스를 실행시키고 싶다면 어떻게 해야할까?
바로 execlp() 시스템콜을 사용하면, 자식프로세스를 우리가 지정해준 루트의 프로그램 내용으로 덮어쓰게 할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid;
pid = fork()
if (pid == 0) {
execlp("/bin/ls", "ls", NULL);
printf("자식 프로세스 실행완료");
}
else if (pid > 0) {
wait(NULL)
printf("Hello, Process!\n", pid)
}
}
위와 같은 코드를 실행하면 어떻게 될까?
위 코드는 자식프로세스를 fork()를 통해 생성한뒤, 해당 주소값에 ls라는 프로그램(디렉터리 정보 표시) 으로 덮어쓰게 된다.
따라서 자식 프로세스는 현재 코드가 종료되고 ls가 실행되므로, execlp() 뒤에있는 pritf(“자식 프로세스 실행완료”)는 실행되지 않는다.이미 프로세스 자체가 다른 프로그램으로 덮어 씌워졌기 때문이다.
마무리
이렇게 프로세스의 실행과 관련있는 시스템콜들에 대해 알아보고, 이러한 시스템콜들이 들어왔을때의 CPU와 메모리가 어떻게 일할지에 대해 알아보았다.
댓글남기기