1. Program vs Process
- program : 디스크에 byte sequence로 저장되어 있는 passive entity
- process : 메모리에 execution sequence 로 load 되어 있는 active entity
2. Process Description
`프로세스` 를 설명할 때는 크게 3가지의 맥락을 고려한다.
(1) 시스템 수준에서 os 가 프로세스를 관리하기 위한 정보
(2) User Program level 에서의 정보 (text code, data, stack, ...)
(3) 프로세스를 실행하는 하드웨어의 수준의 정보
(1) System Level
OS의 관리 대상이라는 점에서 프로세스를 바라본다. OS 는 프로세스 전체 목록을 가지고 있다. 이를 process table 이라고도 하는데 Unix 초창기에는 이것이 array 형태로 구현이 되어있었다고 한다. 현재는 linked list 형태로 수정되었다. 프로세스를 관리한다는 것은 현재 cpu 를 점유할 프로세스를 고르는 `스케줄링`에 관련된 정보와 `State`, `메모리 정보`, `File Descripter table`, `Kernel Stack`, `Process Id`, `IPC` 등을 다루는 것을 모두 포함한다. 이 모든 정보를 프로세스 하나당 한개로 묶어서 관리하는데, 이를 PCB(Process Control Block) 이라고 한다. 언급한 세부항목들을 Identification, State Information, Control Information 세가지로 크게 나누어 PCB 에서 관리한다. PCB는 모두 메모리에 적재되어 관리된다.
(2) User Program Level
사용자의 프로그램을 메모리에 올려 실행시킨다는 관점에서 프로세스를 바라본다. 실제로 메모리에는 segment, page 단위로 쪼개져서 적재되기 때문에 논리적인 의미의 `가상메모리`의 형태로 생각한다. (1) text 영역 : 코드가 저장되는 메모리 영역, (2) data 영역 : 변수들이 저장되는 영역, (3) user stack 영역 : 지역변수를 저장하는 영역, (4) PCB
(3) Register Level
프로그램을 실행시킨다는 것은, 메모리에 적재된 instruction 집합을 하드웨어가 fetch->execute 를 반복하는 작업이다. 이 과정에서 하드웨어 지원이 필요한데 특히 cpu 내부의 register 들에 대한 정보로 프로세스를 바라보는 관점이다.
pc, psw, sp, bp 등의 특수목적 레지스터 뿐 아니라 caller callee saved register, reture address register 등의 일반적인 레지스터 정보를 모두 포함한다. PCB의 state information 에 해당하는 정보들이다.
3. Process Image
앞서, 프로세스의 논리적인 의미를 3가지 관점에서 살펴보았다. 3가지 관점(System Level/User Level/Register Level)에서 필요한 모든 정보를 포함하는 실제 물리적 메모리 구성을 Process Image 라고 한다. 일종의 스냅샷이라고 생각하면 될 듯하다.
4. Process State
프로세스의 상태는 os 마다 상이하게 정의하지만, william stalling 교재에서는 대략적인 os의 state의 개념을 3단계에 걸쳐서 설명한다.
(1) 2-state model
: 가장 단순한 모델이다. not running과 running 두 가지 상태만 존재한다. short-term scheduler의 향상을 위해 수정사항이 존재한다. 실행 중인 프로세스가 IO 작업요청으로 block 되면 Not running 상태로 전환된다. 나중에 스케줄러에 의해 프로세스가 다시 선택되었을때, 아직도 block 상태라면 선택되는 것이 부적절하다. 이를 구분하기 위해, ready 상태와 block 상태를 추가할 필요가 있다.
(2) 5-state model
: ready 와 block 상태가 추가된 형태다. mid-term scheduler의 향상을 위해 수정사항이 존재한다. swap out 되는 victim 프로세스를 선정할 때, 메모리에 있으면서 block 된 프로세스가 가장 유력한 후보이다. 따라서 메모리에 있는지 디스크에 swap out 된 상태인지 구분할 필요가 있다.
(3) 7-state model
: ready/suspend, block/suspend 두 상태가 추가된 최종 형태이다.
5. Mode switch vs Context switch
(1) Mode switch
OS는 자원 관리자로서, 두가지의 mode 를 지원한다. 하나는 user mode 이고, 다른 하나는 kernel mode이다. OS는 사용자로부터 자원을 안전하게 보호하기 위해 사용자는 사용할 수 없는 privileged instruction 을 수행할 수 있다. 즉, kernel mode 이어야만 접근할 수 있다. 이 외의 사용자 프로그램을 실행 중일 때는 user mode 상태로 수행한다.
user mode에서 kernel mode로 전환되는 경우는 3가지로 정리할 수 있다. 첫째로 user program을 수행하다가 exception이 발생하는 경우(segmentation fault, stack overflow, zero division 등). 둘째로 interrupt가 발생했을 경우. 셋째로 사용자가 syscall을 호출할 경우(read, write, listen 등). 이 3가지의 경우가 kernel mode로의 진입점이 된다.
위의 경우가 발생하면 kernel mode bit를 활성화(register level) 하고 현재 수행중이던 user context를 메모리 스택에 저장한다. 그 이후로는 kernel stack 을 이용하여 OS의 kernel code를 수행한다. os가 작업을 마치면 반대로 user mode bit를 활성화하고 이전에 수행중이던 user context를 메모리에서 cpu로 복원한다.
(2) Context switch (=Process switch)
context switch는 cpu를 점유하는 프로세스의 전환을 의미한다. context라는 말은 cpu 내부의 register 정보들을 의미한다. 프로그램을 실행하다보면 `현재 실행 중인 메모리 주소`, `프로세스 상태`, `지역변수 값`, `리턴 값`, `user stack pointer`, `thread pointer` 등의 정보들이 레지스터에 저장되어 사용된다. preemtion 될 때, 즉 cpu 점유하다가 우선순위에서 밀려날 때, cpu 내부의 레지스터 정보들을 메모리에 저장해야한다. 나중에 다시 cpu를 점유할 때 메모리에서 프로세스의 context를 cpu로 복원해서 그 지점과 상태로부터 다시 시작한다.
프로세스 간 전환이 일어나는 경우는, 첫째로 Blocking 되었을 때다. io device 에 작업을 요청했을 때가 그 예이다. 둘째로 time out이 발생했을 때다. 현대 OS는 time sharing system으로 한 프로세스가 cpu를 독점하지 못하도록 한다. 일정간격 cpu 내부 타이머에 의해 timer interrupt가 발생하는데, 이 때마다 timer interrupt handler를 통해 os는 프로세스가 할당된 시간을 다 소모했는지 검사한다. 다 소진했다면 time-out이 발생하게 되고, 스케줄 함수를 통해 다음으로 우선순위가 높은 프로세스에게 cpu가 배정된다. 셋째는 exception이 발생해서 더이상 진행할 수 없을 때다. 이 3가지 경우가 Context switch 주요 발생지점이다.
Context switch가 발생했을 때 내부 진행 상황은 아래와 같다.
- 현재 process context를 메모리에 저장
- PCB를 현재 process 상태에 맞게 업데이트
- PCB를 적절한 대기큐로 이동
- 스케줄함수 호출 O(logN) -> 우선순위가 가장 높은 프로세스 id 얻음
- 다음 process의 PCB 정보를 업데이트
- 다음 process의 context를 복원
프로세스 전환은 모드 전환에 비해 비용이 매우 비싸다. (스케줄 함수 호출 비용 + TLB 갱신으로 인한 cache collision)
6. Process 생성 및 종료
(1) 프로세스 생성
직접 생성하는 경우와 cloning을 하는 경우가 있다. UNIX 에서는 root process 만 직접 생성되고, 나머지 프로세스는 복제 후 변신 과정을 거쳐 생성된다. fork() -> execve(), 그래서 부모 프로세스와 자식 프로세스를 구분할 수 있다. fork() 해서 동일한 프로세스를 만들면, 부모와 자식은 코드 text 영역만 공유하고, user data는 복제해서 따로 소유한다. PCB는 복제 후 pid와 ppid를 자식 프로세스에 맞게 수정하고 대기큐에 넣는다.
복제의 cost를 줄이기 위해 COW(Copy On Write) 방식을 사용하기도 한다. 복제시 처음에는 모든 메모리 page 가 공유되었다고 표시만 하고, 실제 메모리에 변경이 일어나면 copy 가 진행되도록 한다.
(2) 프로세스 종료
자식 프로세스가 종료되면 바로 OS에 자원을 반납하지 않는다. 부모 프로세스가 자식 프로세스를 회수할 때까지 대기하는데, 이처럼 실제로는 작업이 완료되었지만 아직 OS에 자원을 반납하지 않아서 PCB가 살아있는 상태를 Zombie process 라고 한다. 자식 프로세스를 회수해야할 부모 프로세스가 먼저 죽으면 Orphan process 라고 하는데, root process 가 일정시간마다 고아들을 회수한다. 자세한 메커니즘은 POSIX IPC - signal 처리를 확인하면 된다.
'CS > Operating System' 카테고리의 다른 글
[전공생이 설명하는 OS] 동기화 - (1) 용어 및 개념정리 (0) | 2022.05.19 |
---|---|
[전공생이 설명하는 OS] 쓰레드(Thread)와 동기화 문제 (3) | 2022.04.28 |
[전공생이 설명하는 OS] 멀티 코어 프로세스 스케줄링 (0) | 2022.04.28 |
[전공생이 설명하는 OS] 프로세스 스케줄링(feat. 알고리즘 장단점 비교) (0) | 2022.04.28 |
[전공생이 설명하는 OS] 쉽게 읽는 OS의 발전 이야기 (0) | 2022.04.28 |