CHAPTER 3 - 프로세스(Process)

CH 3에서는,
- 첫째로, 프로세스의 개념과 다양한 특성들에 대해서 알 수 있다.
- 둘째로, Shared Memory와 Message Passing을 통한 프로세스 간 통신에 대해서 알 수 있다.
- 셋째로, Client-Server System에서의 통신에 대해 알 수 있다.


3.1 프로세스 개념(Process Concept)

 

* 프로세스(The Process)

- Informally, 프로세스는 실행 중인 프로그램이다. (process is a program in execution.)
- 프로세스는 program counter의 값과 같은 현재 활동을 나타내는 것들을 include 한다.
- 프로세스는 temporary data(function의 parameter, return address, local variable)를 가지는
   process stack을 include 한다.

- 프로세스는 global variable들을 contain 하는 data section을 include 한다.
- 프로세스는 프로세스 실행 중에 동적으로 할당되는 memory인 heap을 include 한다.

Process in Memory

- 프로그램은 디스크에 저장된 파일(executable file[실행파일])과 같이 수동적인 존재(Passive entity)
- 프로세스는 다음에 실행할 명령어를 지정하는 PC와 관련 resource의 set을 가진 능동적인 존재(Active entity)
- executable file가 memory에 load 될 때, 프로그램이 프로세스가 된다.

 

* 프로세스 상태(Process State)

- 프로세스는 실행되면서 상태(State)가 변화하며, 프로세스의 상태는 현재의 활동에 따라 정의된다.

Diagram of Process State

(1) New : 프로세스가 생성 중이다.
(2) Running : 명령어(Instructions)들이 실행되고 있다.
(3) Waiting : 프로세스가 어떤 event가 일어나기를 기다린다.
(4) Ready : 프로세스가 processor에 할당되기를 기다린다.
(5) Terminated : 프로세스의 execution이 종료되었다.

- 어느 한순간에 한 processor에서는 오직 하나의 process만이 실행된다.
- 이에 많은 프로세스가 Ready, Waiting 상태에 있을 수 있다.

 

* 프로세스 제어 블록(Process Control Block)

- PCB(Process Control Block)은 특정 프로세스와 연관된 여러 정보를 가지고 있다.

(1) Process State(프로세스 상태): new, ready, running, waiting, halted 등의 상태를 가진다.
(2) Program Counter(프로그램 카운터): 이 프로세스가 다음에 실행할 명령어의 주소를 가리킨다.
(3) CPU Registers: Accumlator, index register, stack register, general-purpose register 들과 condition code가 포함된다.
(4) CPU 스케줄링 정보: process priority, 큐에 대한 포인터 등과 같은 scheduling parameter들을 포함한다.
(5) Memory 관리 정보: base register와 limit register 값, page table과 segment table의 정보를 포함한다.
(6) Accounting 정보: CPU 사용시간, real time used, time limit, process number 등을 포함한다.
(7) 입출력 상태 정보: 해당 프로세스에게 할당된 I/O device 들과 open file 목록 등을 포함한다.

PCB(Process Control Block)

 

* 스레드(Threads)
- 현재, 대부분의 운영체제에서는 한 프로세스가 다수의 Thread를 가질 수 있도록 허용한다.
- 이에 한 프로세스가 한 번에 하나 이상의 task를 수행할 수 있도록 할 수 있다.
- 이러한 점은 여러 Thread가 병렬로 수행되는 multi-core system에서 이익을 얻을 수 있다.
- Thread를 지원하는 System에서, PCB는 각 Thread에 대한 정보를 포함하도록 한다.

 


3.2 프로세스 스케줄링(Process Scheduling)

- Multi-Programming의 목적은 CPU utilization을 최대화하기 위하여 항상 어떤 프로세스가 실행되도록 하는 것이다.

- Time-Sharing 방식의 목적은 각 프로그램이 실행되는 동안 user가 interact 할 수 있도록 프로세스들 사이에서 CPU를 자주 switching 해주는 것이다.

- 프로세스 스케줄러(Process Scheduler)는 CPU에서 실행 가능한 여러 프로세스들 중 한 프로세스를 선택한다.

- Single-Processor 환경에서, 실행 중인 프로세스는 한 개 이상 있을 수 없다.

- 만일, 프로세스들이 여러 개가 있다면, 나머지 프로세스들은 CPU가 자유로워질 때까지 대기해야 한다.

* 스케줄링 큐(Scheduling Queues)

- 프로세스가 시스템에 들어오면, 이들은 job queue에 놓이게 된다. job queue는 시스템의 모든 프로세스가 있다.
- Main Memory에 존재하며, Ready 상태에서 실행을 대기하는 process들은 ready queue라 불리는 리스트에 있게 된다.
- 특정 입출력 장치를 대기하는 프로세스들의 리스트를 device queue라고 한다.

* 스케줄러(Schedulers)

- 프로세스들은 life-time 동안 다양한 스케줄링 큐들 사이에 있게 된다.
- 프로세스들은 대용량 메모리에 저장되어 실행될 때까지 기다리게 된다.

- long-term Scheduler(Job Scheduler)은 대용량 메모리에서 프로세스들을 선택하여 실행하기 위해 Memory로 load 한다.
- short-term Scheduler(CPU Scheduler)은 실행 준비가 완료된 프로세스들 중에서 선택하여, 해당 프로세스에게 CPU를 할당한다.

- long-term Scheduler은 short-term Scheduler에 비해 실행 빈도수가 적은 편이다.
- long-term Scheduler은 degree of Multi-Programming(메모리에 있는 프로세스들의 수)를 control 한다.

- Short-term Scheduler은 CPU를 위해 자주 새로운 프로세스를 선택해야 한다. (빈도수가 높다.)
- 100ms마다 한 번씩 실행되기도 하므로, short-term Scheduler은 실행 속도가 매우 빨라야 한다.

- I/O Bound Process는 연산보다 입출력 실행에 더 많은 시간을 소요하는 프로세스이다.
- CPU Bound Process는 연산에 더 많은 시간을 소요하고, 입출력 요청을 드물게 발생시키는 프로세스이다.
- long-term Scheduler은 두 가지 Process들을 적절히 혼합하여 선택하는 것이 중요하다.

* 문맥 교환(Context Switch)

- interrupt가 발생하면, 시스템은 interrupt 처리가 끝난 후에 context를 복구할 수 있도록
  현재 실행 중인 프로세스의 context를 저장할 필요가 있다.

- context는 프로세스의 PCB에 표현된다. (CPU 레지스터 값, 프로세스 상태, 메모리 관리 정보 등)

- CPU를 다른 프로세스로 switch 하기 위해서는, 이전의 프로세스의 상태를 보관하고 새로운 프로세스의 보관된 상태를 복구하는 작업이 필요하다. 이 작업을 Context Switch라고 한다.

- Context Switch가 일어나면, 커널은 과거 프로세스의 context를 PCB에 저장하고 실행할 프로세스의 saved context를 복구한다.

- Context Switch가 진행되는 동안, 시스템은 어떤 useful work도 할 수 없기 때문에 이 시간은 overhead이다.

 


3.3 프로세스에 대한 연산(Operation on Processes)

 

* 프로세스 생성(Process Creation)

- 실행되는 동안 프로세스는 여러 개의 새로운 프로세스를 생성할 수 있다.
- 생성하는 프로세스를 '부모(parent) 프로세스', 새롭게 생성되는 프로세스를 '자식(child) 프로세스'라고 한다.
- 자식 프로세스는 또 새롭게 다른 프로세스를 생성할 수 있으며, 그 결과 '프로세스의 트리'를 형성한다.

- UNIX, LInux와 같은 대부분의 현대 운영체제들은 프로세스 식별자(pid)를 사용하여 프로세스를 구분한다.
- pid는 보통 정수이며, 시스템의 각 프로세스에게 고유한 값을 가지도록 할당된다.

- 프로세스가 새로운 프로세스를 생성할 때, 두 프로세스를 실행시키는 데 두 가지 가능한 방법이 존재한다.
(1) 부모는 자식과 병행하여 실행을 계속한다. (continues to execute concurrently)
(2) 부모는 일부 또는 모든 자식이 실행을 종료할 때까지 기다린다.

- 새로운 프로세스들(자식 프로세스들)의 주소 공간 측면에서 볼 때 아래와 같이 두 가지 경우로 나타날 수 있다.

(1) 자식 프로세스는 부모 프로세스의 복사본이다.
- UNIX에서 새로운 프로세스는 'fork()' system call을 통해 생성된다.
- 새로운 자식 프로세스는 부모 프로세스의 주소 공간의 복사본으로 구성된다.
- 기존의 부모 프로세스와 새롭게 생성된 자식 프로세스는 fork() 후의 명령어에서부터 실행을 계속한다.
- 한 가지 차이점은 부모 프로세스는 자식 프로세스의 pid, 자식 프로세스는 0이 return 된다.

(2) 자식 프로세스가 자신에게 load될 새로운 프로그램을 가지고 있다.
- fork() system call 다음에 생성되는 두 프로세스 중에 한 프로세스가 exec() system call을 사용하여
  자신의 memory 공간을 새로운 프로그램으로 교체할 수 있다.

* 프로세스 종료(Process Termination)

- 프로세스가 마지막 문장의 실행을 끝내고, exit() system call을 사용하여 프로세스를 종료할 수 있다.
- 이 시점에서 프로세스는 자신의 부모 프로세스에게 wait() system call을 통해 status를 return 할 수 있다.

- 부모는 여러 가지 이유로 인해 자식 프로세스들 중 하나의 실행을 종료할 수 있다.
(1) 자식이 자신에게 할당된 자원을 초과하여 사용하는 경우
(2) 자식에게 할당된 task가 더 이상 필요가 없는 경우
(3) 부모가 exit() 하는데, 운영체제는 부모가 exit 한 후에 자식이 실행하는 것을 허용하지 않는 경우

- 종료되었지만, 부모 프로세스가 아직 wait()를 호출하지 않는 프로세스를 '좀비(Zombie) 프로세스'라고 한다.
- 부모 프로세스가 wait()를 호출하지 않고, 종료해버린 프로세스를 '고아(Orphan) 프로세스'라고 한다.
- Linux와 UNIX는 Orphan Process의 새로운 부모 프로세스로 init 프로세스를 지정함으로써 문제를 해결한다.

 

 


3.4 프로세스 간 통신(Interprocess Communication)

- OS 내에서 concurrently 실행되는 프로세스들은 independent 하거나 cooperating 하다.
- 다른 프로세스에게 영향을 주거나 받지 않고, data를 공유하지 않는 프로세스는 independent 하다.
- 다른 프로세스들과 data를 공유하는 프로세스는 cooperating 하다.

process cooperation을 허용하는 이유는 아래 4가지와 같다.

1. 정보 공유(Information Sharing)
- 여러 사용자가 동시에 동일한 정보에 대해 접근하려고 할 수 있다.

2. 계산 가속화(Computation Speed-up)
- 특정 task를 빨리 실행시키기 위해, task를 sub-task들로 나누어 병렬로 실행되게 한다.

3. 모듈성(Modularity)
- 시스템 기능을 별도의 프로세스들(Thread들)로 나누어, 모듈식 형태로 구성하기를 원할 수 있다.

4. 편의성(Convenience)
- 한 사용자가 동시에 작업할 다수의 task들을 가질 수도 있다.

- Cooperating Process들은 data와 information을 공유하기 위해 IPC 기법이 필요하다.
(IPC : Inter-process communication, 프로세스 간 통신)
- IPC에는 공유 메모리(Shared Memory)와 메시지 전달(Message Passing) 모델이 있다.
- 대부분의 시스템에서는 두 가지 모델 모두 구현하고 있다.

- Message Passing 방식은 보통 System Call을 사용하여 구현되므로 부가적인 시간 소비 작업들이 많이 필요하게 된다. 이에 Shared Memory 방식이 Message Passing 방식보다 빠르다.
(Shared Memory 방식에서는 공유 메모리 영역을 구축할 때만 System call 호출.)

- 다수의 processing core를 가진 시스템에서는 Message Passing이 Shared Memory보다 더 나은 성능을 보인다.

* 공유 메모리 시스템 (Shared-Memory Systems)

- 공유 메모리(Shared Memory)를 사용하는 IPC에서는 통신하는 프로세스들이 Shared Memory 영역을 구축해야 한다.
- (생산자-소비자) 문제를 해결하기 위해 공유 메모리 시스템이 사용될 수 있다.

* 메시지 전달 시스템 (Message-Passing Systems)

- 메시지 전달(Message-Passing) 방식은 동일한 주소 공간을 공유하지 않고도 프로세스들이 통신하고, 동기화할 수 있도록 한다. (특히 분산 시스템에서 유용함.)

명명(Naming)
1. Direct Communications
- Direct Communication을 사용하여 메시지 전달 시스템을 구현할 수 있다.
(1) send(P, message) - 프로세스 P에게 메시지를 전송한다.
(2) receive(Q, message) - 프로세스 Q로부터 메시지를 수신한다.
- 이 기법에서 연결은 정확히 두 프로세스들 사이에서만 연관된다.

2. Indirect Communications
- Indirect Communication을 사용하여 메시지 전달 시스템을 구현할 수 있다.
(1) send(A, message) - 메시지를 메일 박스 A로부터 메시지를 송신한다.
(2) receive(A, message) - 메시지를 메일박스 A로부터 메시지를 수신한다.
- 이 기법에서 연결은 두 개 이상의 프로세스들과 연관될 수 있다.

▶동기화(Synchronization)
1. Blocking send : sending process는 메시지가 수신될 때까지 blocking 된다.
2. Non-Blocking send : sending process는 메시지를 보내고, 작업을 재시작한다.
3. Blocking receive : 메시지가 available 할 때까지 blocking 된다.
4. Non-Blocking receive : receiver가 유효한 메시지나 null을 받는다.

버퍼링(Buffering)
- 통신이 직접적이든, 간접적이든 간에 프로세스들 간에 교환되는 메시지는 큐 안에 들어가 있다.

- 큐를 구현하는 방법은 아래 세 가지와 같다.
1. 무용량(zero capacity)
  - 큐의 최대 길이가 0이다. 이 경우, sender는 receiver가 메시지를 수신할 때까지 기다려야 한다.

2. 유한 용량(bounded capacity)
  - 큐는 유한한 길이 n을 가진다. 큐가 꽉 찼을 때, sender는 큐가 이용 가능할 때까지 blocking 된다.

3. 무한 용량(unbounded capacity)
  - 큐는 무한한 길이를 가지며, sender는 어느 경우에도 blocking 되지 않는다.

 


3.5 IPC 시스템의 사례

1. Shared Memory를 위한 POSIX API
2. Mach OS의 Message Passing System
3. Message Passing을 사용하기 위해 Shared Memory를 사용하는 Windows

 


3.6 클라이언트 서버 환경에서의 통신

- 3.4에서 공유 메모리와 메시지 전달 기법을 사용하여 프로세스들이 통신하는 방법에 대해 알 수 있었다.
- 이 기법들은 클라이언트-서버 시스템의 통신에도 사용할 수 있다.
- 이 경우에 사용할 수 있는 통신 전략은 3가지로, '소켓(Socket)', 'RPCs', '파이프(Pipe)'이다.
   (RPCs : Remote Procedure Calls)

* 소켓(Socket)

- 소켓은 통신의 end-point이며, 두 프로세스가 네트워크 상에서 통신을 하기 위해 두 개의 소켓이 필요하다.
- 서버는 지정된 port에 클라이언트 요청 메시지가 도착하기를 기다리게 된다.
- 요청이 수신되면, 서버는 클라이언트 소켓으로부터 연결 요청을 수락함으로써 연결이 이루어진다.
- 소켓을 이용한 통신은 스레드들 간에 구조화되지 않은 바이트 스트림만 통신하기 때문에 너무 저 수준이다.
- 이에 대한 대안으로, 보다 고 수준인 RPC와 Pipes가 존재한다.

* 원격 프로시저 호출(Remote Procedure Calls, RPC)

- RPC는 네트워크에 연결되어 있는 두 시스템 사이의 통신을 연결하기 위한 프로시저 호출을 추상화하기 위한 방안으로 설계되었다.
- RPC는 분산 파일 시스템(Distributed File System)을 구현하는데 유용하다.

* 파이프 (Pipes)

- 파이프(Pipe)는 두 프로세스가 통신할 수 있게 하는 전달자(conduits)로서 동작한다.

▶ 일반 파이프(Ordinary Pipes)

- 일반 파이프는 생산자-소비자 형태로 두 프로세스 간의 통신을 허용한다.
- 생산자는 파이프의 한 end 단에서 write를 하고, 소비자는 반대 end 단에서 read를 한다.
- 결과적으로 한쪽으로만 데이터를 전송할 수 있는 단방향 통신만이 가능하다.
- 만일 양방향 통신이 필요하다면, 두 개의 파이프를 사용해야 한다.

▶ 지명 파이프(Named Pipes)

- 일반 파이프는 오직 프로세스들이 통신하는 동안에만 존재하지만, 지명 파이프는 통신이 종료돼도 계속 존재한다.
- 지명 파이프의 경우, 양방향으로도 통신이 가능하며 부모-자식 관계도 필요하지 않다.
- 지명 파이프가 구축되면, 여러 프로세스들이 이를 사용하여 통신할 수 있다.

 


-Reference-
Abraham Silberschatz, Peter B. Galvin, Greg Gagne의 『Operating System Concept 9th』