Running: CPU를 실제로 사용중인 상태
Ready: 언제든 실행 가능하지만 CPU는 다른 프로세스에 의해 점유되어 있기 떄문에 기다려야 하는 상태
Blocked(sleep): 외부 이벤트를 기다리고있는 상태
화살표 예)
1: 프로세스가 입력을 받기위해 Blocked상태로 변함
2: Scheduler에 의해 할당된 CPU사용시간을 모두 사용한 경우.
3: 2번 이후, Scheduler에 의해 다시 CPU제어권을 다시 넘겨받은 경우
4. 1번 이후 Input을 받고 CPU제어권을 넘겨받길 기다림.
프로세스 당 유지되는 정보들
PCB, Process Control Blocked
프로세스의 구현
인터럽트가 발생하면 OS의 가장 하위 레벨에서 발생하는일.
1. 인터럽트가 발생하면
2. 하드웨어 적으로 스택에 Program counter값이 저장된다.
3. 하드웨어적으로 새로운 Interrupt vector에서 새로운 program counter값을 load한다.
4. Assembly procedure를 레지스터에 저장한다.
5. Assembly language prodedure를 new stack에 setup한다.
6. C 인터럽트 핸들러를 실행한다.
7. Scheduler는 어떤 프로세스를 다음에 실행시킬지 결정한다.
8. C procedure는 Assembly code를 다시 실행한다.
9. Assembly language procedure는 new current process를 실행시킨다.
2번과 3번은 하드웨어상에서 일어나고, 나머지는 운영체제가 담당한다. 6번은 인터럽트 핸들러, 6번은 스케쥴러에 관한 내용이다.
인터럽트 처리과정
예를 들어 터미널에 G를 입력한다면
1. 인터럽트 시그널이 발생된다.
2. CPU는 register에 저장되있는 PC, SP등을 PCB에 save한다.
PCB는 프로세스 제어블록(Process Control Block)으로, CPU에 의해 실행중인 특정한 프로세스를 관리할 필요가 있는 정보를 포함하는 운영체제 커널의 자료구조이다.
3. 커널의 Interrupt Handler를 CPU로 execute한다.
4. 종료되면 다시 PCB에 저장되어있던 실행중인 프로세스 정보를 CPU로 불러온다.
다중 프로그래밍에 따른 CPU 이용률
예를 들어 각각 20, 50, 80%의 입, 출력의 시간을 보내는 프로세스가 있다고 할 경우 프로세스의 개수에 따른 CPU의 사용률을 나타낸다.
한 개의 프로세스가 80% I/O wait를 지내는 경우 20%의 시간동안 CPU를 사용한다고 할 수 있다. 그럼 CPU 사용률은 20%이다.
다중 프로그래밍에 따른 CPU 사용량
1 - CPU사용량^프로그램 개수
CPU 사용량이 50%인 프로세스가 2개 일 경우
50% -> 1/2
1 - (1/2 * 1/2) = 1 - 1/4 = 75%이다.
System call과 Process
1. fork0
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
int main(void){
int pid, status;
pid = fork(); (1)
printf("pid = %d\n", pid); (2)
while (1); (3)
}
1. 해당 프로그램을 실행하면 (1)의 fork()로 인해 2개의 프로세스가 실행된다.
2. 출력은 2줄이다
pid = 0 <- 자식의 출력
pid = @@@@@ <- 부모의 출력
3. 부모의 프로세스 번호는 자식의 프로세스 번호보다 적다.
2. fork1
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
int main(void){
int pid, status;
pid = fork(); (1)
if(pid > 0){ [a]
// parent process
printf("PARENT: chile = %d\n", pid); (2)
do{} while(1); (3)
}else{ [b]
// pid == 0 / chile process
printf("CHILD: child process is running.\n"); (4)
do{} while(1); (5)
}
}
1. 이 프로그램을 실행시키면 (1)의 fork()로 인해서 두 개의 프로세스가 실행된다.
2. 2개의 line의 출력이 나오는데, 부모프로세스, 자식프로세스 각 각 1줄의 출력이 실행되고 둘 다 (3), (5)의 무한Loop에 빠지게 된다
3. 두 개의 프로세스가 실행되는데 부모의 프로세스번호는 자식의 프로세스번호보다 낮다.
4. [a]는 부모 프로세스일 경우 실행되고, [b]는 자식 프로세스일 경우 실행된다.
3. fork2
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
int main(void){
int pid, status;
1 pid = fork();
[a] if(pid > 0){
// parent process
2 printf("PARENT: chile = %d\n", pid);
3 waitpid(pid, &status, 0);
4 printf("PARENT: Child exited (parent is still running)\n");
5 do{} while(1);
[b] }else{
// pid == 0 / chile process
6 printf("CHILD: child process is running.\n");
7 do{} while(1);
}
}
해당 프로그램을 실행시킬 경우 2번(부모 프로세스)과 6번(자식 프로세스)의 출력이 함께 실행된다. 이후 부모 프로세스는 3번의 waitpid로 인하여 자식 프로세스가 종료되길 기다린다. 7번으로 인해 무한 Loop에 빠져있던 자식 프로세스가 kill되면 부모프로세스는 자식 프로세스가 종료되길 기다렸으므로 4번을 출력한 후 5번(무한 Loop)에 들어간다.
4. fork3
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<string.h>
#include<stdio.h>
int main(void) {
int pid, status;
char *arg[] = { "./hello", NULL } ; char ch;
pid = fork();
if (pid > 0) {
/* parent process */
1 printf("PARENT: Child pid = %d\n", pid);
2 waitpid(pid, &status, 0);
3 printf("PARENT: Child exited.\n");
} else {
/* child process */
4 printf("CHILD: Child process image will be replaced by %s\n", arg[0]);
5 execv(arg[0], arg);
}
return 0;
}
1. 실행 직후 부모프로세스는 1을 출력하고 2로인해 자식프로세스가 사라지길 기다린다.
2. 자식 프로세스는 4를 출력하고 5로인해 hello프로세스로 변경되어 실행된다.
3. 자식 프로세스를 kill하게 되면 부모 프로세스는 3을 실행하고 종료된다.
5. 부모 프로세스를 먼저 kill하기 전엔 자식 프로세스의 PPID(부모프로세스번호)가 fork3(부모프로세스)의 pid로 출력됨. 하지만 fork3 자식 프로세스는 execv로 인해 독립적인 프로세스임.
4. 부모 프로세스를 먼저 kill하게 되면 hello프로세스는 execv로 인해 독립적으로 바뀐 프로세스이기 때문에 영향을 받지 않고 계속 실행됨.
6. 부모 프로세스를 자식프로세스보다 먼저 kill하게 되면 자식프로세스(였던것)의 PPID(부모프로세스번호)는 1로 바뀌며 쉘 자체가 부모프로세스가 됨.
5. fork4
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <stdio.h>
int main(void)
{
int pid, status;
char *arg[] = {"./hello", NULL};
char ch;
pid = fork();
1 while (1)
{
if (pid > 0)
{
/* parent process */
2 printf("PARENT: Child pid = %d\n", pid);
3 waitpid(pid, &status, 0);
4 pid = fork();
}
else
{
/* child process */
5 printf("CHILD: Child process image will be replaced by %s\n", arg[0]);
6 execv(arg[0], arg);
}
}
return 0;
}
1. 부모 프로세스는 2를 출력하고 3을 통해 부모가 종료되길 기다림.
2. 자식 프로세스는 5를 출력하고 6을 통해 새롭게 독립된 프로세스로 실행됨.
3. 부모 프로세스는 자식 프로세스가 종료되면 4를 통해 또다시 자식 프로세스를 생성하고 2번을 반복함.
4. 1번, 2번, 3번, 4번을 1로인해 반복됨.
5. 자식 프로세스를 죽여도 4번으로 인해 계속 자식 프로세스가 생성됨
6. 부모 프로세스를 먼저 kill해도 run하던 자식 프로세스는 유지됨.
프로세스 상태(process state)
1. running
int main()
{
do{} while(1);
}
ps -l
- 실행결과:
Runnable 상태이며 프로세스의 상태는 변하지 않는다. Running과 Ready 상태 둘 다 의미한다.
2. blocked1
#include<unistd.h>
int main(){
sleep(3600);
}
ps -l
실행결과:
- Sleep 상태이며 Block상태를 의미한다.
3. blocked2
#include<stdio.h>
int main(){
int a;
scanf("%d", &a);
}
ps -l
실행결과:
- T상태 sTopped상태, 멈춰있거나 흔적이 남아있는 상태.
4. blocked3
#include<unistd.h>
int main(){
int i;
do{
sleep(1);
for(i = 0; i < 100000000; i++) {}
}while (1);
}
ps -l
실행 결과:
Sleep상태와 Runnable상태가 나온다. 이유는 sleep(1)이 실행 될 땐 Sleep상태가 나오며 sleep(1)이 끝난 이 후에는 for문이 돌아가며 실행중인 상태다.