프로세스 상태와 계층 구조
프로세스 상태
- 프로세스가 실행되면, 상태가 바뀐다.
- 프로세스 상태는 현재 프로세스의 활동으로 정의된다.
프로세스 상태의 종류
- New : 프로세스가 새로 만들어진 상태
- Running : 명령어들이 실행되고 있는 상태
- Waiting : 프로세스가 다른 이벤트가 일어나기를 기다리고 있는 상태
- Ready : 프로세스가 다른 프로세서에 할당되기를 기다리고 있는 상태
→ 이러한 이름들은 임의적이고, OS에 따라 다양하다.
→ 다만 변하지 않는 사실은 어떤 프로세서 코어의 어떤 순간에도 오직 하나의 프로세스만이 Running 상태 일 수 있다.
프로세스 상태와 계층 구조
프로세스 상태
- 프로세스가 실행되면, 상태가 바뀐다.
- 프로세스 상태는 현재 프로세스의 활동으로 정의된다.
프로세스 상태의 종류
- New : 프로세스가 새로 만들어진 상태
- Running : 명령어들이 실행되고 있는 상태
- Waiting : 프로세스가 다른 이벤트가 일어나기를 기다리고 있는 상태
- Ready : 프로세스가 다른 프로세서에 할당되기를 기다리고 있는 상태
→ 이러한 이름들은 임의적이고, OS에 따라 다양하다.
→ 다만 변하지 않는 사실은 어떤 프로세서 코어의 어떤 순간에도 오직 하나의 프로세스만이 Running 상태 일 수 있다.

프로세스 스케쥴링
- 프로세스가 시스템에 들어가면, ready queue에 들어간다. 여기에서 준비하고 CPU코어에서 실행되기를 기다린다.
- 이 큐는 linked list의 형태로 저장된다.
- ready queue 헤더는 첫번쨰 PCB 블락을 가리키는 포인터를 가지고 있으며 각각의 PCB는 포인터 필드를 가지고 있다.
- wait queue : 프로세스가 CPU 코어에 할당되고 이를 잠깐 실행하다가 결국 중단하거나 인터럽트 되거나 트정 이벤트를 기다리거나
- 프로세스가 I/O 요청을 보내면 그 외부입출력 장치는 프로세서보다 느리기 때문에 프로세스는 그 I/O 장치의 작업을 기다려야 한다. 그때 wait queue에 들어가게 된다.

- 새로운 프로세스가 처음에는 ready queue에 들어간다.
- 실행을 위해 선택되거나 dispatch 될 때까지 거기서 기다린다.
- 프로세스가 CPU 코어에 한 번 할당되어 실행되면, 아래 중 하나의 이벤트가 일어난다.
- 프로세스는 I/O 요청을 발생시킬 수 있다. 그리고 그러면 I/O wait queue에 들어간다.
- 프로세스는 새로운 child process 를 만들 수 있다. 그러면 child의 종료를 기다리는 동안 wait 큐에 들어간다.
- 프로세스는 타임 slice가 만기되거나 인터럽트의 결과로 코어로부터 강제로 제거될 수 있다. 그러면 다시 ready 큐로 들어가게 된다.
- 위의 두 케이스에서는 프로세스는 결국 waiting state에서 ready state로 바뀐다. 그리고 다시 ready queue에 담긴다. 프로세스는 이러한 사이클을 종료 전까지 반복한다. 종료되면 모든 큐에서 삭제되고 PCB와 리소스 할당이 취소된다.
CPU 스케쥴링
- CPU 스케쥴러 : ready queue에 있는 프로세스들 중 하나를 골라 CPU 코어에 할당.
- CPU 스케쥴러는 새로운 프로세스를 CPU 빈도에 맞게 잘 골라야 한다.
- CPU 스케쥴러는 최소 매 100 ms 마다 실행되며, 일반적으로 이보다 빈번하게 일어난다.
Context Switching
- CPU가 프로세스를 바꾸는 행동을 context switching이라 한다.

context switching
- 인터럽트는 운영체제가 CPU 코어를 현재 태스크에서 커널 상태로 바꾸도록 한다.
- 인터럽트가 일어나면 시스템은 현재 실행중인 프로세스의 context 를 저장해야 하고 그리하여 프로세스가 끝나고 이것을 다시 복원할 수 있다. 결국 멈춘 프로세스가 재개될 수 있다.
- context 는 PCB에 저장 돼 있다.
- 컨텍스트 스위치가 일어나면 커널은 기존 프로세스의 맥락을 저장하고, 불러올 새로운 프로세스의 저장된 컨텍스트를 가져온다.
- 컨텍스트 스위치 타임은 순전히 overhead이다 왜냐하면 시스템은 스위칭 하는 시간이 그냥 버리는 시간이기 때문이다.
- 스위칭 스피드는 기계마다 다른데, 메모리 속도,복사되어야 하는 레지스터의 개수, 존재하는 특별한 명령어의 수에 따라 다르다. 전형적으로 context switching에는 몇몇 microsecond 가 걸린다.
- 컨텍스트 스위치 시간은 하드웨어에 의존적이다. 예를 들면 몇몇 프로세서는 레지스터의 세트를 여러개 지원한다. 이때 컨텍스트 스위치는 단순히 현재 레지스터 셋의 포인터만 바꾸면 된다.
- 고급 메모리 관리 기술을 사용하려면 각 컨텍스트에 따라 추가 데이터를 전환해야 할 수도 있다.
프로세스의 생성
System call : fork(), exec
fork()
새로운 프로세스를 위한 메모리 공간 할당
- pid가 다른 또 하나의 프로세스가 생기는 것.
exec()
프로세스 실행 : 메모리 공간을 새로운 프로그램의 내용으로 덮어씌움
→ exec을 호출한 프로세스가 아니라, exec을 통해 호출된 프로세스만 메모리에 남게 된다.
- 생성되는 새로운 프로세스는 없고, 새로운 프로세스에 이 함수를 호출한 프로세스의 pid가 그대로 적용.
- 새로운 프로세스가 기존 프로세스를 덮어버린다.
프로세스 생성 과정
- 프로세스를 만드는 프로세스를 parent process, 생성되는 프로세스를 child process라고 한다.
- 이들 프로세스는 프로세스 tree를 구성한다.

- 일반적으로 프로세스가 자식 프로세스를 생성할 때, 자식 프로세스는 작업을 수행하기 위해 특정 리소스(cpu 시간, 메모리, 파일 , io장치)가 필요하다.
- 자식 프로세스는 운영체제에서 직접 리소스를 얻을 수 있거나 상위 프로세스 리소스의 하위 집합으로 제한될 수 있다. 부모는 자식 리소스를 분할해야 할 수도 있고 여러 자식 간 리소스를 공유 할 수도 있다.
- 하위 프로세스를 상위 리소스의 하위 집합으로 제한하면 하위 프로세스를 너무 많이 생성해 프로세스가 과부하 주는 것을 막을 수 있다.
- 상위 프로세스는 초기화 데이터를 하위 프로세스에 전달할 수 있다.
프로세스가 새 프로세스를 생성할 때 두 가지 가능성
- 상위 프로세스가 하위 프로세스와 동시에 계속 실행
- 상위는 하위 일부 또는 전체가 종료될 때까지 기다림
새 프로세스에는 두 가지 주소공간의 가능성
- 자식 프로세스는 부모 프로세스와 중복(프로그램 및 데이터를 상위 것을 사용)
- 하위 프로세스에는 새 프로그램이 로드
- 유닉스에서는 각 프로세스가 고유한 정수인 프로세스 식별자로 식별된다. PID
- 새로운 프로세스는 fork() 시스템 호출에 의해 생성된다.
- 새 프로세스는 원래 프로세스의 주소 공간 복사본으로 구성된다.
- 이 메커니즘을 사용하면 상위 프로세스가 하위 프로세스와 쉽게 통신 가능하다.
- 두 프로세스는 모두 포크 이후의 명령에 계속 실행
- 포크 시스템 호출 후 두 프로세스 중 하나는 일반적으로 exec 시스템 호출을 사용해 프로세스의 메모리 공간을 새 프로그램으로 대체
- exec() 시스템 호출은 바이너리 파일을 메모리에 로드하고(ecec() 시스템 호출을 포함하는 프로그램의 메모리 이미지를 삭제함) 실행을 시작한다.
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t pid;
pid = fork();
if ( pid < 0 ){
fprintf(stderr, "Fork Failed")
return 1;
}
else if (pid == 0) {
execlp("/bin/ls" , "ls", NULL);
}
else {
wait(NULL);
printf("Child Complete");
}
return 0;
}
새로운 프로세스 생성 과정 예시
- 그림 3.8에 표시된 C 프로그램은 이전에 설명한 UNIX 시스템 호출을 보여줍니다. 이제 동일한 프로그램의 복사본을 실행하는 두 개의 서로 다른 프로세스가 있습니다. 유일한 차이점은 하위 프로세스(자식프로세스)의 pid 변수 값이 0인 반면 상위 프로세스의 변수 값은 0보다 큰 정수 값이라는 점입니다(실제로 이는 하위 프로세스의 실제 pid입니다). 하위 프로세스는 상위 프로세스뿐만 아니라 열린 파일과 같은 특정 리소스로부터 권한과 일정 속성을 상속받습니다.
- 그런 다음 하위 프로세스는 execlp() 시스템 호출(execlp()은 exec() 시스템 호출의 버전임)을 사용하여 UNIX 명령 /bin/ls(디렉토리 목록을 가져오는 데 사용됨)로 주소 공간을 오버레이합니다.
- 부모는 자식 프로세스가 wait() 시스템 호출로 완료될 때까지 기다립니다. 하위 프로세스가 완료되면(exit()를 암시적으로 또는 명시적으로 호출하여) 상위 프로세스는 wait() 호출에서 다시 시작되며, 여기서 종료() 시스템 호출을 사용하- 여 완료됩니다. 이는 그림 3.9에도 나와 있습니다.
- 물론 자식이 exec()를 호출하지 않고 대신 부모 프로세스의 복사본으로 계속 실행되는 것을 막을 수 있는 방법은 없습니다. 이 시나리오에서 상위 및 하위는 동일한 코드 명령을 실행하는 동시 프로세스입니다. 하위 항목은 상위 항목의 복사본이므로 각 프로세스에는 자체 데이터 복사본이 있습니다.
- 대안적인 예로, 다음에는 Windows에서 프로세스 생성을 고려합니다. 프로세스는 CreateProcess() 함수를 사용하여 Windows API에서 생성됩니다. 이는 부모가 새 자식 프로세스를 생성한다는 점에서 fork()와 유사합니다.
자식프로세스를 위한 메모리 공간 할당
- 커널은 자식 프로세스가 생성되면 프로세스 테이블에 등록하고 메모리를 할당한다.

- 일반적으로 프로세스가 생성되면, 자식 프로세스와 부모 프로세스는 텍스트(text, 프로그램 코드) 영역은 공유하고, 데이터, 힙, 스택 영역은 복사하는데, 보통 fork() 함수를 수행한 후 바로 exec 함수가 호출되기 때문에 대부분의 시스템에서 fork() 함수 수행 시 데이터, 힙, 스택 영역을 바로 복사하지 않는다.
- 자식 프로세스의 내용이 변경될 때 나머지 영역을 복사하는 방법을 통해서 실행 속도를 높일 수 있는데 이 를
변형 시에 복사(copy-on-write, COW)라고 한다
COW 란?
- 복사시 변경되기 전까지는 동일한 주소를 바라보다가 변경된 내용이 있을 경우 새로운 주소를 할당하는 기법
프로세스 종료
프로세스 종료
프로세스의 종료에는 크게 두 가지가 있다.
1. 자발적 종료
마지막 statement를 수행한 후에 부모에게 output data를 보내고, 운영체제에게 이를 알려준 후 종료된다. 프로세스의 각종 자원들이 운영체제에 반납된다. 이때 유닉스의 exit() 시스템 콜을 이용한다. 이는 프로그램에 명시적으로 넣지 않아도 main 함수가 리턴되는 위치에 컴파일러가 넣어준다.
2. 비자발적 종료
비자발적 종료에는 세 종류가 있다.
- 부모 프로세스가 자식 프로세스를 강제 종료시키는 경우
자식 프로세스가 한계치를 넘어서는 자원을 요청하거나 자식에게 할당된 태스크가 더 이상 필요하지 않은 경우 부모 프로세스는 자식의 수행을 종료시킨다. 이때 유닉스의 abort() 시스템 콜을 이용한다.
-
키보드로 kill, break을 친 경우
-
부모 프로세스가 종료하는 경우 : 운영체제는 부모 프로세스가 종료하는 경우 자식이 더 이상 수행되도록 허용하지 않는다. 부모가 종료하기 전에 자식들이 먼저 종료된다.
참고
프로세스 종료 : https://hyunah-home.tistory.com/entry/프로세스-관리-프로세스-생성과-종료-부모-프로세스와-자식-프로세스
https://woochan-autobiography.tistory.com/207
프로세스 스케쥴링
- 프로세스가 시스템에 들어가면, ready queue에 들어간다. 여기에서 준비하고 CPU코어에서 실행되기를 기다린다.
- 이 큐는 linked list의 형태로 저장된다.
- ready queue 헤더는 첫번쨰 PCB 블락을 가리키는 포인터를 가지고 있으며 각각의 PCB는 포인터 필드를 가지고 있다.
- wait queue : 프로세스가 CPU 코어에 할당되고 이를 잠깐 실행하다가 결국 중단하거나 인터럽트 되거나 트정 이벤트를 기다리거나
- 프로세스가 I/O 요청을 보내면 그 외부입출력 장치는 프로세서보다 느리기 때문에 프로세스는 그 I/O 장치의 작업을 기다려야 한다. 그때 wait queue에 들어가게 된다.

- 새로운 프로세스가 처음에는 ready queue에 들어간다.
- 실행을 위해 선택되거나 dispatch 될 때까지 거기서 기다린다.
- 프로세스가 CPU 코어에 한 번 할당되어 실행되면, 아래 중 하나의 이벤트가 일어난다.
- 프로세스는 I/O 요청을 발생시킬 수 있다. 그리고 그러면 I/O wait queue에 들어간다.
- 프로세스는 새로운 child process 를 만들 수 있다. 그러면 child의 종료를 기다리는 동안 wait 큐에 들어간다.
- 프로세스는 타임 slice가 만기되거나 인터럽트의 결과로 코어로부터 강제로 제거될 수 있다. 그러면 다시 ready 큐로 들어가게 된다.
- 위의 두 케이스에서는 프로세스는 결국 waiting state에서 ready state로 바뀐다. 그리고 다시 ready queue에 담긴다. 프로세스는 이러한 사이클을 종료 전까지 반복한다. 종료되면 모든 큐에서 삭제되고 PCB와 리소스 할당이 취소된다.
CPU 스케쥴링
- CPU 스케쥴러 : ready queue에 있는 프로세스들 중 하나를 골라 CPU 코어에 할당.
- CPU 스케쥴러는 새로운 프로세스를 CPU 빈도에 맞게 잘 골라야 한다.
- CPU 스케쥴러는 최소 매 100 ms 마다 실행되며, 일반적으로 이보다 빈번하게 일어난다.
Context Switching
- CPU가 프로세스를 바꾸는 행동을 context switching이라 한다.

context switching
- 인터럽트는 운영체제가 CPU 코어를 현재 태스크에서 커널 상태로 바꾸도록 한다.
- 인터럽트가 일어나면 시스템은 현재 실행중인 프로세스의 context 를 저장해야 하고 그리하여 프로세스가 끝나고 이것을 다시 복원할 수 있다. 결국 멈춘 프로세스가 재개될 수 있다.
- context 는 PCB에 저장 돼 있다.
- 컨텍스트 스위치가 일어나면 커널은 기존 프로세스의 맥락을 저장하고, 불러올 새로운 프로세스의 저장된 컨텍스트를 가져온다.
- 컨텍스트 스위치 타임은 순전히 overhead이다 왜냐하면 시스템은 스위칭 하는 시간이 그냥 버리는 시간이기 때문이다.
- 스위칭 스피드는 기계마다 다른데, 메모리 속도,복사되어야 하는 레지스터의 개수, 존재하는 특별한 명령어의 수에 따라 다르다. 전형적으로 context switching에는 몇몇 microsecond 가 걸린다.
- 컨텍스트 스위치 시간은 하드웨어에 의존적이다. 예를 들면 몇몇 프로세서는 레지스터의 세트를 여러개 지원한다. 이때 컨텍스트 스위치는 단순히 현재 레지스터 셋의 포인터만 바꾸면 된다.
- 고급 메모리 관리 기술을 사용하려면 각 컨텍스트에 따라 추가 데이터를 전환해야 할 수도 있다.
프로세스의 생성
System call : fork(), exec
fork()
새로운 프로세스를 위한 메모리 공간 할당
- pid가 다른 또 하나의 프로세스가 생기는 것.
exec()
프로세스 실행 : 메모리 공간을 새로운 프로그램의 내용으로 덮어씌움
→ exec을 호출한 프로세스가 아니라, exec을 통해 호출된 프로세스만 메모리에 남게 된다.
- 생성되는 새로운 프로세스는 없고, 새로운 프로세스에 이 함수를 호출한 프로세스의 pid가 그대로 적용.
- 새로운 프로세스가 기존 프로세스를 덮어버린다.
프로세스 생성 과정
- 프로세스를 만드는 프로세스를 parent process, 생성되는 프로세스를 child process라고 한다.
- 이들 프로세스는 프로세스 tree를 구성한다.

- 일반적으로 프로세스가 자식 프로세스를 생성할 때, 자식 프로세스는 작업을 수행하기 위해 특정 리소스(cpu 시간, 메모리, 파일 , io장치)가 필요하다.
- 자식 프로세스는 운영체제에서 직접 리소스를 얻을 수 있거나 상위 프로세스 리소스의 하위 집합으로 제한될 수 있다. 부모는 자식 리소스를 분할해야 할 수도 있고 여러 자식 간 리소스를 공유 할 수도 있다.
- 하위 프로세스를 상위 리소스의 하위 집합으로 제한하면 하위 프로세스를 너무 많이 생성해 프로세스가 과부하 주는 것을 막을 수 있다.
- 상위 프로세스는 초기화 데이터를 하위 프로세스에 전달할 수 있다.
프로세스가 새 프로세스를 생성할 때 두 가지 가능성
- 상위 프로세스가 하위 프로세스와 동시에 계속 실행
- 상위는 하위 일부 또는 전체가 종료도리 때까지 기다림
새 프로세스에는 두 가지 주소공간의 가능성
- 자식 프로세스는 부모 프로세스와 중복(프로그램 및 데이터를 상위 것을 사용)
- 하위 프로세스에는 새 프로그램이 로드
- 유닉스에서는 각 프로세스가 고유한 정수인 프로세스 식별자로 식별된다. PID
- 새로운 프로세스는 fork() 시스템 호출에 의해 생성된다.
- 새 프로세스는 원래 프로세스의 주소 공간 복사본으로 구성된다.
- 이 메커니즘을 사용하면 상위 프로세스가 하위 프로세스와 쉽게 통신 가능하다.
- 두 프로세스는 모두 포크 이후의 명령에 계속 실행
- 포크 시스템 호출 후 두 프로세스 중 하나는 일반적으로 exec 시스템 호출을 사용해 프로세스의 메모리 공간을 새 프로그램으로 대체
- exec() 시스템 호출은 바이너리 파일을 메모리에 로드하고(ecec() 시스템 호출을 포함하는 프로그램의 메모리 이미지를 삭제함) 실행을 시작한다.
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t pid;
pid = fork();
if ( pid < 0 ){
fprintf(stderr, "Fork Failed")
return 1;
}
else if (pid == 0) {
execlp("/bin/ls" , "ls", NULL);
}
else {
wait(NULL);
printf("Child Complete");
}
return 0;
}
새로운 프로세스 생성 과정 예시
- 그림 3.8에 표시된 C 프로그램은 이전에 설명한 UNIX 시스템 호출을 보여줍니다. 이제 동일한 프로그램의 복사본을 실행하는 두 개의 서로 다른 프로세스가 있습니다. 유일한 차이점은 하위 프로세스(자식프로세스)의 pid 변수 값이 0인 반면 상위 프로세스의 변수 값은 0보다 큰 정수 값이라는 점입니다(실제로 이는 하위 프로세스의 실제 pid입니다). 하위 프로세스는 상위 프로세스뿐만 아니라 열린 파일과 같은 특정 리소스로부터 권한과 일정 속성을 상속받습니다.
- 그런 다음 하위 프로세스는 execlp() 시스템 호출(execlp()은 exec() 시스템 호출의 버전임)을 사용하여 UNIX 명령 /bin/ls(디렉토리 목록을 가져오는 데 사용됨)로 주소 공간을 오버레이합니다.
- 부모는 자식 프로세스가 wait() 시스템 호출로 완료될 때까지 기다립니다. 하위 프로세스가 완료되면(exit()를 암시적으로 또는 명시적으로 호출하여) 상위 프로세스는 wait() 호출에서 다시 시작되며, 여기서 종료() 시스템 호출을 사용하- 여 완료됩니다. 이는 그림 3.9에도 나와 있습니다.
- 물론 자식이 exec()를 호출하지 않고 대신 부모 프로세스의 복사본으로 계속 실행되는 것을 막을 수 있는 방법은 없습니다. 이 시나리오에서 상위 및 하위는 동일한 코드 명령을 실행하는 동시 프로세스입니다. 하위 항목은 상위 항목의 복사본이므로 각 프로세스에는 자체 데이터 복사본이 있습니다.
- 대안적인 예로, 다음에는 Windows에서 프로세스 생성을 고려합니다. 프로세스는 CreateProcess() 함수를 사용하여 Windows API에서 생성됩니다. 이는 부모가 새 자식 프로세스를 생성한다는 점에서 fork()와 유사합니다.
자식프로세스를 위한 메모리 공간 할당
- 커널은 자식 프로세스가 생성되면 프로세스 테이블에 등록하고 메모리를 할당한다.

- 일반적으로 프로세스가 생성되면, 자식 프로세스와 부모 프로세스는 텍스트(text, 프로그램 코드) 영역은 공유하고, 데이터, 힙, 스택 영역은 복사하는데, 보통 fork() 함수를 수행한 후 바로 exec 함수가 호출되기 때문에 대부분의 시스템에서 fork() 함수 수행 시 데이터, 힙, 스택 영역을 바로 복사하지 않는다.
- 자식 프로세스의 내용이 변경될 때 나머지 영역을 복사하는 방법을 통해서 실행 속도를 높일 수 있는데 이 를
변형 시에 복사(copy-on-write, COW)라고 한다
COW 란?
- 복사시 변경되기 전까지는 동일한 주소를 바라보다가 변경된 내용이 있을 경우 새로운 주소를 할당하는 기법
프로세스 종료
프로세스 종료
프로세스의 종료에는 크게 두 가지가 있다.
1. 자발적 종료
마지막 statement를 수행한 후에 부모에게 output data를 보내고, 운영체제에게 이를 알려준 후 종료된다. 프로세스의 각종 자원들이 운영체제에 반납된다. 이때 유닉스의 exit() 시스템 콜을 이용한다. 이는 프로그램에 명시적으로 넣지 않아도 main 함수가 리턴되는 위치에 컴파일러가 넣어준다.
2. 비자발적 종료
비자발적 종료에는 세 종류가 있다.
- 부모 프로세스가 자식 프로세스를 강제 종료시키는 경우
자식 프로세스가 한계치를 넘어서는 자원을 요청하거나 자식에게 할당된 태스크가 더 이상 필요하지 않은 경우 부모 프로세스는 자식의 수행을 종료시킨다. 이때 유닉스의 abort() 시스템 콜을 이용한다.
-
키보드로 kill, break을 친 경우
-
부모 프로세스가 종료하는 경우 : 운영체제는 부모 프로세스가 종료하는 경우 자식이 더 이상 수행되도록 허용하지 않는다. 부모가 종료하기 전에 자식들이 먼저 종료된다.
참고
프로세스 종료 : https://hyunah-home.tistory.com/entry/프로세스-관리-프로세스-생성과-종료-부모-프로세스와-자식-프로세스
https://woochan-autobiography.tistory.com/207
공룡책(운영체제)