프로세스 관리(Process Management)
제 2 부 프로세스 관리(Process Management)
4. 프로세스(Process)
▶ 현대의 컴퓨터 시스템: ‘다중 프로그래밍’ 또는 ‘시분할 시스템’
즉, 다수의 프로그램을 기억장치에 적재하고 중앙처리장치를 다중화하여 병행 수행
→ 프로세스 개념 필요
4.1 프로세스 개념(Process Concept)
▶ 프로세스의 예
- 일괄처리 시스템의 작업(job)
- 시분할 시스템의 사용자 프로그램 또는 태스크(task)
- 스풀링과 같은 운영체제 내부 프로그램
→ ‘작업(job)’이라는 말과 혼용
4.1.1 프로세스(The Process)
▶ 프로세스의 정의: 실행중인 프로그램
- 프로그램 그 자체는 프로세스가 아님
1) 프로그램: 디스크에 저장된 파일과 같은 수동적인 실체(passive entity)
2) 프로세스: 다음 실행할 명령어를 지정하는 프로그램 카운터(program counter)를 가진 능동적인 실체(active entity)
- 프로세스의 실행은 순차적인 방식(sequential fashion)으로 진행됨
- 프로세스는 프로그램 코드 이상의 의미를 가짐, 현재 프로그램 계수기 값이 나타내는 활동요소(activity)와 프로세서의 레지스터를 포함함
- 두 프로세스들이 동일한 프로그램에 관련되어 있어도, 서로 분리된 실행 순서를 가짐
- 프로세스를 실행하는 과정에서 일반적으로 많은 프로세스들을 생성함
4.1.2 프로세스 상태(Process State)
▶ 순차 프로세스의 상태: [그림 4.1] 프로세스 상태도
- 생성(new): 프로세스가 생성되고 있음
- 준비(ready): 프로세서(CPU)에게 할당되기를 기다림
- 실행(running): 명령어들이 실행되고 있음
- 대기(waiting): 입출력 완료나 신호 수신과 같은 사건이 일어나기를 기다림
- 종료(terminated): 프로세스의 실행이 종료됨
* 어느 한 순간에 프로세서 상에서 오직 하나의 프로세스만이 실행됨
4.1.3 프로세스 제어 블록(PCB; Process Control Block)
▶ 각 프로세스는 운영체제에 의해 프로세스 제어 블록(태스크 제어 블록)으로 표현됨
[그림 4.2] 프로세스 제어 블록
① 프로세스 상태: 생성(new), 준비(ready), 수행(running), 대기(waiting), 정지(halted)
② 프로그램 카운터: 프로세스의 다음 수행할 명령어 주소
③ 중앙처리장치 레지스터들
- 누산기(accumulator)
- 색인 레지스터(index register)
- 스택 레지스터(stack register)
- 범용 레지스터(general register)
- 상태 코드(condition code)
→ 인터럽트 발생시 프로그램 계수기와 더불어 저장됨으로써 추후 재개될 수 있게 함
[그림 4.3] 한 프로세스에서 다른 프로세스로의 CPU 교환
④ 중앙처리장치 스케줄링 정보 → “5장 참조”
- 프로세스의 우선 순위
- 스케줄링 큐를 위한 포인터: 각 PCB는 다음 프로세스의 PCB를 가리키는 포인터를 가짐
- 기타 스케줄링 매개변수
⑤ 기억장치 관리 정보 → “8장 참조”
- 기준 레지스터(base register)
- 한계 레지스터(bound register)
- 페이지 테이블 또는 세그먼트 테이블의 정보
⑥ 계정 정보(accounting information)
- 중앙처리장치 및 실제 사용 시간
- 시간 제한
- 계정 번호
- 작업 또는 프로세스 번호
⑦ 입출력 상태 정보(I/O status information)
- 이 프로세스에 할당된 입출력 장치의 목록
- 개방된 파일의 목록
4.2 프로세스 스케줄링(Process Scheduling)
4.2.1 스케줄링 큐(Scheduling Queues)
▶ 스케줄링 큐: [그림 4.4] 준비 큐와 다양한 입출력 장치 큐
1) 준비 큐(ready queue): 준비 상태에서 실행을 기다리는 프로세스의 대기 장소
- 연결 리스트 구조
- 큐의 헤더: 두 포인터로 구성
① head 포인터 - 첫 PCB를 가리킴
② tail 포인터 - 끝 PCB를 가리킴
2) 장치 큐(device queue): 입출력 장치를 기다리는 프로세스의 대기 장소
- 각 장치마다 자신의 장치 큐를 가짐
▶ 큐잉 도표(queueing diagram): 프로세스 스케줄링의 표현 방식
[그림 4.5] 프로세스 스케줄링을 표현하는 큐잉 도표
- 사각형: 큐(준비 큐, 장치 큐)
- 원: 큐를 서비스하는 자원
- 화살표: 시스템에서의 프로세스 흐름
4.2.2 스케줄러(scheduler)
▶ 스케줄러: 큐에서 대기하는 프로세스의 선택 - 세 가지 유형
▶ 장기 스케줄러(long-term scheduler): ‘작업 스케줄러(job scheduler)’
- 목적: 프로세스 저장소(디스크)에서 처리할 대상 프로세스를 결정하여 기억장치로 이동시킴
1) 다중 프로그래밍 차수(기억장치에 있는 프로세스 수)의 안정적 유지
→ 프로세스가 시스템을 떠날 때마다 호출하면, 평균 프로세스 생성률과 평균 프로세스 이탈률이 동일해짐
2) 시스템 성능을 위해 입출력 중심 작업과 중앙처리장치 중심 작업을 잘 혼합하는 것이 좋음
① 입출력 중심 프로세스: 연산보다 입출력 수행에 더 많은 시간을 소요하는 프로세스
② 중앙처리장치 중심 프로세스: 입출력보다 연산 수행에 더 많은 시간을 소요하는 프로세스
- 실행 빈도: 少 → 장기 스케줄러는 다소 느려도 무방함
▶ 단기 스케줄러(short-term scheduler): ‘중앙처리장치 스케줄러(CPU scheduler)’
- 목적: 주기억장치에 준비된 작업들 중에서 실행할 작업을 선택하여 중앙처리장치를 할당
- 실행 빈도: 多 → 단기 스케줄러는 매우 신속해야 함
▶ 중기 스케줄러(medium-term scheduler): ‘교체(swapping)’ - 8장 참조
[그림 4.6] 큐잉 도표에 중기 스케줄링의 추가
- 목적: 기억장치에서 중앙처리장치에 대해 경쟁이 심한 프로세스의 수를 줄여 다중 프로그래밍의 차수를 완화시킴. 그 밖에도,
1) 프로세스의 혼합 상태 개선
2) 기억장치의 공간 확보 등에 활용 가능함
ex) 시분할 시스템의 경우, 장기 스케줄러를 거의 사용하지 않고 중기 스케줄러를 많이 사용함
→ 새로운 프로세스는 즉시 단기 스케줄러에 넣으며, 중기 스케줄러에 의한 기다림을 참지 못하는 사용자는 자리를 뜨게 됨
4.2.3 문맥 교환(Context Switch)
▶ 문맥 교환: 중앙처리장치를 다른 프로세스로 교환하기 위해 이전 프로세스의 상태를 보관하고 새로운 프로세스의 보관 상태를 적재하는 것
▶ 문맥 교환 시간은 하드웨어 지원과 운영체제의 복잡도에 따라 다양함
→ 성능상의 병목현상을 초래할 수 있음: 스레드(thread) 구조에 의한 해결(4.5절 참조)
4.3 프로세스 연산(Operation on Processes)
▶ 프로세스들은 병행 수행될 수 있어야 하며, 동적으로 생성 또는 제거되어야 함
→ 운영체제에 의한 프로세스 생성 및 종료 기법 제공 필요
4.3.1 프로세스 생성(Process Creation)
▶ 부모 프로세스 → 자식 프로세스(부 프로세스)
‘생성’ 시스템 호출
[그림 4.7] 전형적인 UNIX 시스템의 프로세스 트리
▶ 부 프로세스의 자원 확보
- 운영체제로부터 직접 얻음
- 부모 프로세스의 자원을 이용:
1) 자식에게 자원을 분배
2) 자식과 자원을 공유
→ 너무 많은 부 프로세스를 만듦으로써 발생되는 시스템 부하를 줄일 수 있음
▶ 프로세스의 생성 시, 부모 프로세스에서 자식 프로세스로 초기 자료가 전달됨
ex) 파일 F1의 상태를 단말기 화면에 출력하는 부 프로세스
- 부모 프로세스로부터 F1이라는 파일 이름과 출력장치의 이름을 입력받고,
- 운영체제나 혹은 부모 프로세스로부터 자원을 할당받아 자료 전달을 수행함
▶ 프로세스의 운영 방식
- 수행 측면:
1) 부모 프로세스가 계속해서 자식 프로세스들과 함께 실행하는 경우
2) 부모 프로세스가 일부 혹은 모든 자식 프로세스가 종료될 때까지 기다리는 경우
- 주소 공간 측면:
1) 자식 프로세스가 부모 프로세스와 중복되는 경우
2) 자식 프로세스로 프로그램을 적재하는 경우
▶ 예:
1) UNIX 운영체제
- 프로세스마다 고유 정수로 된 프로세스 식별자를 가짐
- fork 시스템 호출에 의해 생성되는 부 프로세스는 부모의 주소 공간을 복사한 것으로 구성되며, 부모 프로세스와 함께 수행됨
- fork 직후의 두 프로세스 중 하나가 execve 시스템 호출을 하여 새로운 프로그램을 자신의 프로세스로 (execve 시스템 호출을 가진 프로그램을 파괴하면서) 적재하고 수행을 시작함
- 부모는 수행되면서 또 다른 자식을 생성할 수도 있고, 자식이 종료될 때까지 기다리기 위해 wait 시스템 호출을 행할 수도 있음
2) DEC사의 VMS 운영체제:
- 새로운 프로세스를 생성하고 특정 프로그램을 적재하여 수행함
3) MS사의 윈도우즈/NT 운영체제:
- 부모 프로세스의 주소 공간에 중복시키는 방법과 새로운 프로세스의 주소공간으로 적재되는 프로그램 이름을 명시하는 방법 모두 제공
4.3.2 프로세스 종료(Process Termination)
▶ 프로세스의 종료 1
- 프로세스는 마지막 문장을 실행하고 exit 시스템 호출로 운영체제에게 자신의 삭제를 요청함
- 이때, 프로세스는 wait 시스템 호출로 기다리고 있는 부모 프로세스에게 자료(출력)를 반환할 수 있음
- 운영체제는 프로세스의 모든 자원(물리적 기억장치, 가상기억장치, 개방 파일, 입출력 버퍼 등)을 회수함
▶ 프로세스의 종료 2
- 부모 프로세스는 abort 시스템 호출을 통해 자식 프로세스를 종단(kill)시킬 수 있음
또한, 사용자에 의한 프로세스 종단도 가능함
- 부모 프로세스가 자식을 식별할 수 있도록, 생성된 프로세스는 신원을 부모에게 전달함
- 부모 프로세스가 자식을 종료시키는 경우
1) 자식이 할당된 자원을 초과하여 사용하는 경우
→ 부모가 자식의 상태를 조사하는 메커니즘이 필요
2) 자식에게 할당된 업무(task)가 더 이상 필요 없는 경우
3) 운영체제에 의해 부모가 종료되면 더 이상 자식이 수행되는 것이 허용되지 않는 경우
→ “cascading termination”: 대부분의 운영체제에서 채택
▶ 예: UNIX
- 프로세스는 exit 시스템 호출을 하고 종료함
- 부모 프로세스는 wait 시스템 호출을 하고 기다림
- 자식 프로세스는 종료할 때 부모 프로세스에게 자신의 식별자를 알림
- 부모 프로세스가 종료되면 운영체제는 모든 자식을 종료시킴
4.4 상호 협조 프로세스들(Cooperating Processes)
▶ 병행 프로세스들의 유형
- 상호 독립적인 프로세스들: 상호 영향을 주고받지 않는 경우
→ 데이터를 공유하지 않는 경우는 명백히 독립적임
- 상호 협조적인 프로세스들: 상호 영향을 주고받는 경우
→ 데이터를 공유하는 경우는 명백히 협조적임
▶ 프로세스 협조를 허용하는 이유
- 정보 공유(information sharing): 여러 사용자가 동일한 정보에 동시 접근하는 경우
- 연산 속도 증가(computation speedup): 특정 태스크를 부태스크로 나누어 병행처리를 하는 경우
→ 다수의 처리 요소(중앙처리장치, 입출력 채널 등)를 갖는 경우에 가능
- 모듈성(modularity): 시스템 기능을 구분된 프로세스들로 나누어 모듈화 하는 경우
- 편의성(convenience): 한 사용자가 여러 태스크를 동시에 처리하는 경우
* 상호 협조적인 병행 수행을 위해서는 프로세스간의 통신(4.6절)과 동기화(6장)를 위한 메커니즘이 필요함
▶ 상호 협조적인 프로세스의 예: 생산자-소비자 문제
- 생산자 프로세스: 정보를 생산
소비자 프로세스: 정보를 소비
ex) 인쇄 프로그램 →(문자)→ 프린터 구동기
컴파일러 →(어셈블리 코드)→ 어셈블러 →(목적 모듈)→ 적재기(loader)
- 생산자와 소비자 프로세스의 병행 수행을 위해 버퍼의 저장소(pool)가 필요하고 동기화 되어야 함
1) 무한 버퍼: 버퍼의 수에 제한이 없는 경우
2) 유한 버퍼: 버퍼의 수가 제한되는 경우
- 기억장치에 의한 유한 버퍼를 구현한 생산자-소비자 프로그램: p.110-111
1) 공유 변수
- buffer: 순환 배열(circular array)
- in: 다음의 빈 버퍼를 가리키는 포인터
- out: 첫 번째의 찬 버퍼를 가리키는 포인터
2) 생산자 프로세스와 소비자 프로세스
- buffer가 빈 상태: in = out인 경우
- buffer가 가득 찬 상태: in+1 = out인 상태(실제로는 n-1개만 찬 상태임)
- no_op: 아무 것도 하지 않는 명령어
[프로그램]
const n = 100;
type item = ...;
var buffer: array[0..n-1] of item;
in, out: 0..n-1;
nextp, nextc: item;
in := 0;
out := 0;
parbegin
producer: begin
repeat
...
produce an item in nextp
...
while in+1 mod n = out do no-op;
buffer[in] := nextp;
in := in + 1 mod n;
until false;
end;
consumer: begin
repeat
while in = out do no-op;
nextc := buffer[out];
out := out + 1 mod n;
...
consume the item in nextc
...
until false;
end;
parend
4.5 스레드(Threads)
▶ 태스크를 구성하는 프로세스들간에 자원이 동시에 공유되고 접근되는 것이 유용할 수 있음
→ 스레드 기능
4.5.1 스레드 구조
▶ 스레드: 경량 프로세스(lightweight process; LWP)
- 중앙처리장치 사용의 기본 단위
- 프로그램 계수기, 레지스터 집합, 스택 공간으로 구성
- 동료 스레드들과 코드 구간, 데이터 구간, 운영체제 자원(개방된 파일, 신호 등)을 공유
→ 즉, 태스크를 공유
- 일반적인 의미의 프로세스인 중량 프로세스(heavyweight process; HWP)는 하나의 스레드를 가진 태스크에 해당
- 스레드 문맥 교환은 여전히 레지스터 집합의 교환을 필요로 하지만 기억장치관리 관련 작업은 필요로 하지 않으므로 프로세스 문맥 교환보다 신속함
- 병렬처리 환경과 유사하게, 다중 스레딩 프로세스는 임계영역이나 잠금을 필요로 하는 병행 제어 문제가 발생함
▶ 다중 스레드 제어 ↔ 다중 프로세스 제어
- 다중 프로세스의 경우, 각 프로세스는 독립적으로 수행함
즉, 각 프로세스는 자신의 프로그램 계수기, 스택 레지스터, 주소 공간을 가짐
→ 프로세스들에 의해 수행되는 작업들이 서로 무관할 때 유용
→ 프로세스들이 동일 코드를 수행할 수 있지만, 별도의 기억공간과 파일 자원을 소유해야 함
- 하나의 다중 스레드된 프로세스가 연관된 다중 프로세스들 보다 적은 자원(기억공간, 개방 파일, CPU 스케줄링 등)을 사용함
→ 효율을 극대화할 수 있음
▶ 스레드와 프로세스의 유사점과 상이점
1) 프로세스와의 유사점
- 생성(create), 준비(ready), 블록(blocked), 수행(running), 종료(terminated) 중 한 상태를 유지함
- CPU를 공유하고 한 순간에 하나의 스레드가 수행됨
- 순차적으로 수행되고, 각자의 레지스터와 프로그램 계수기를 가짐
- 자식 스레드를 생성할 수 있고, 시스템 호출이 완료될 때까지 기다리기 위해 블록할 수 있음
2) 프로세스와의 상이점
- 서로 독립적이지 않음
- 상호간의 보호가 제공되지 않음
→ 실제로 태스크내의 스레드들은 상호 지원하도록 설계되기 때문에 상호간의 보호는 필요하지 않음
[그림 4.8] 태스크내의 다중 스레드
▶ 스레드는 병행 처리뿐만 아니라 블로킹 시스템 호출에 의한 순차 처리도 허용함
▶ 공유 자원들에 대한 다중 스레드의 제어 방법
1) 커널 지원 스레드(kernel-supported threads) 방식: ex) Mach, OS/2
- 프로세스에서와 같은 시스템 호출 집합을 제공
2) 사용자 수준 스레드(user-level threads) 방식: ex) Andrew
- 사용자 수준에서의 라이브러리 호출 집합을 제공
- 스레드 교환은 운영체제 호출을 필요로 하지 않기 때문에 매우 신속
- 한 사용자 수준 스레드의 시스템 호출로 전체 프로세스(태스크)의 대기를 초래
→ 커널은 스레드에 대한 정보 없이 프로세스만을 스케줄하기 때문
- 스레드 입장에서는 불공정한 스케줄링 발생
ex) 프로세스 a: 1개의 스레드로 구성, 프로세스 b: 100개의 스레드로 구성
두 프로세스에 동일한 수의 타임 슬라이스를 할당받음
→ 프로세스 a의 스레드가 프로세스 b의 스레드에 비해 100배 빨리 수행됨
→ 단, 커널 지원 스레드의 경우에는, 스위칭에 보다 많은 시간이 소요되지만 프로세스 b가 프로세스 a에 비해 100배 더 많이 받음
3) 사용자 수준 스레드와 커널 지원 스레드의 혼합(hybrid) 방식: ex) Solaris 2 → 4.5.2절 참조
- 혼합 방식은 스레드가 중량 프로세스의 특성을 다소 가지면서도 보다 효율적으로 수행되기 때문에 많이 사용됨
ex) UNIX: 커널이 단일 작업인 경우 - 즉, 커널에서는 단 하나의 태스크만 수행됨
→ 자료 접근의 동기화 문제를 피할 수 있음
ex) Mach: 커널이 다중 스레드된 경우 - 즉, 커널이 동시에 여러 요청을 서비스함
→ 스레드들이 공유 자료 접근에 대해 동기화되어야 함 → 동기화: 6장 참조
4.5.2 예: Solaris 2
▶ 시스템 구성
[그림 4.9] Solaris 2의 스레드
1) 사용자 수준 스레드(user-level thread)
- 사용자 수준 스레드의 생성과 스케줄링은 라이브러리 형태로 지원받으며, 커널은 전혀 관여하지 않음
2) 중간 수준 스레드(intermediate-level thread): 커널 지원 LWP(kernel-supported LWP)
- 사용자 수준 스레드들과 커널 수준 스레드들 사이에 있는 경량 프로세스들
→ 커널과의 통신이 필요한 경우에 사용
- 각 태스크는 적어도 하나의 LWP를 가짐
- 스레드 라이브러리에 의해 조작됨
- 사용자 수준 스레드들은 해당 LWP상에서 다중화됨
즉, 현재 수행중인 사용자 수준 스레드가 LWP에 연결되고, 나머지는 블록되거나 기다림
3) 커널 수준 스레드(kernel-level thread)
- 커널내에서의 모든 수행은 커널 수준 스레드에 의해 수행됨
- 각 LWP를 위한 커널 수준 스레드와 LWP와 관계없이 커널 자신을 위한 커널 수준 스레드(ex. 디스크 요청 서비스를 위한 스레드)로 구분됨
- 어떤 커널 수준 스레드들은 처리기들 상에서 다중화되는 반면, 어떤 커널 수준 스레드들은 특별한 처리기에만 할당됨
▶ 시스템 동작
- 하나의 태스크는 많은 사용자 수준 스레드로 구성되고, 이들은 커널의 개입 없이 커널 지원 LWP에서 스케줄되고 교환됨
- 각 LWP는 정확히 하나의 커널 수준 스레드에 연결되며, 하나의 태스크에 여러 LWP가 존재할 수 있는데, 이들은 커널과의 통신을 필요로 하는 경우에만 존재함
- 커널 스레드는 커널의 스케줄러에 의해 스케줄되고 하나 혹은 여러 CPU 상에서 수행됨
- 커널 스레드가 (통상 I/O 연산이 완료되기를 기다리면서) 블록되면, 대응하는 LWP도 블록되고, 연쇄적으로 그 LWP에 부착된 사용자 수준 스레드들도 블록됨. 이때, 태스크가 소유한 LWP가 하나인 경우에는 그 태스크 전체가 블록됨
▶ 각 스레드 유형의 자원 요구
1) 커널 수준 스레드: 소규모의 데이터 구조 및 스택
→ 커널 스레드의 교환은 기억장치 접근 정보의 수정을 필요로 하지 않으므로 상대적으로 신속함
2) LWP: 프로세스 제어 블록(레지스터 데이터, 계정 정보, 기억 장치 정보 등)
→ LWP의 교환은 매우 많은 양의 일을 필요로 하기 때문에 상대적으로 느림
3) 사용자 수준 스레드: 스택과 프로그램 계수기(커널 자원 불필요)
→ 커널이 사용자 수준 스레드의 스케줄링에 개입하지 않으므로, 사용자 수준 스레드의 교환이 신속함
4.6 프로세스간 통신(Interprocess Communication; IPC)
▶ 프로세스간 통신(IPC): 프로세스간의 통신과 동기화를 제공하는 기법
- 메시지 시스템(message system): 좋은 효과를 보임
- 공유 기억장치(shared memory): 공용 버퍼 저장소에 대한 구현을 응용 프로그램에서 담당
→ 두 기법은 배타적이지 않으며 단일 운영체제 내에서 동시에 사용 가능
4.6.1 기본 구조(Basic Structure)
▶ 프로세스간의 통신을 위한 두 가지 기본 연산
- send (message)
- receive (message)
▶ 통신 연결(communication link)의 구현
1) 물리적 구현: 공유 기억장치, 하드웨어 버스, 네트워크 등 → 15장 참조
2) 논리적 구현: “고려 사항”
- 연결(link)의 설정 방법
- 하나의 연결에 연관될 수 있는 프로세스의 최대 수
- 각 프로세스 쌍에 존재할 수 있는 연결의 최대 수
- 연결의 용량(capacity): 버퍼 공간의 존재 여부 및 그 공간의 크기
- 메시지의 크기: 연결의 가변 길이 혹은 고정 길이 메시지 수용 여부
- 연결의 단방향(unidirectional) 또는 양방향(bidirectional): 메시지 흐름의 단방향/양방향 여부
▶ 연결(link)과 send/receive 연산의 논리적 구현 방법
① 직접 통신(direct communication) ↔ 간접 통신(indirect communication)
② 대칭 통신(symmetric communication) ↔ 비대칭 통신(asymmetric communication)
③ 자동적 버퍼링(automatic buffering) ↔ 명시적 버퍼링(explicit buffering)
④ 복사에 의한 전송(send by copy) ↔ 참조에 의한 전송(send by reference)
⑤ 고정 길이 메시지(fixed-sized message) ↔ 가변 길이 메시지(variable-sized message)
4.6.2 명칭 부착(Naming)
“통신을 원하는 프로세스들이 서로 참조할 수 있는 방법”
4.6.2.1 직접 통신(Direct Communication)
▶ 직접 통신: 메시지를 송신하거나 수신하는 프로세스가 상대 프로세스의 이름을 명시하는 방식
- send(P, message): 프로세스 P에게 메시지를 전송
- receive(Q, message): 프로세스 Q로부터 메시지를 수신
▶ 직접 통신 연결의 특성
① 통신을 원하는 프로세스 쌍 사이에 연결이 자동 설정됨
→ 프로세스들은 서로 상대방의 신원을 알아야 함
② 연결은 두 프로세스 사이에만 지정됨
③ 통신 프로세스 쌍 사이에는 정확히 하나의 연결이 존재함
④ 연결은 단방향일 수 있지만, 일반적으로 양방향임
▶ (예) 생산자/소비자 문제의 해결 방안: p.120의 프로그램
▶ 주소지정(addressing) 방식
1) 대칭적(symmetric): 송신자와 수신자 모두 상대방을 명명
2) 비대칭적(asymmetric): 송신자만 수신자를 명명하고, 수신자는 송신자를 명명할 필요 없음
- send(P, message): 프로세스 P에게 메시지를 전송
- receive(id, message): 임의의 프로세스로부터 메시지를 수신
(id: 통신을 일으킨 프로세스의 이름으로 설정됨)
▶ 직접 통신의 단점: 프로세스 정의의 모듈성(modularity) 제한
→ 즉, 이름 변경시 다른 모든 프로세스의 정의(옛 이름 포함)를 조사해야 함
4.6.2.2 간접 통신(Indirect Communication)
▶ 간접 통신: 메일박스(mailbox)를 통한 통신 → “port”라고도 함
- 프로세스에 의해 메시지가 놓여지거나 제거되는 객체
- 각 메일박스는 고유한 id를 가짐
- 두 프로세스는 공유 메일박스를 가질 때만 통신할 수 있음
1) send(A, message): 메일박스 A로 메시지를 송신
2) receive(A, message): 메일박스 A로부터 메시지를 수신
▶ 간접 통신 연결의 특성
① 한 쌍의 프로세스가 공유 메일박스를 가질 경우에만 연결이 설정됨
② 하나의 연결이 둘보다 많은 프로세스들과 연관될 수 있음
③ 각 통신 프로세스 쌍 사이에 여러 연결(메일박스)이 있을 수 있음
④ 연결은 단방향일 수도 양방향일 수도 있음
▶ 프로세스 P1, P2, P3가 메일박스 A를 공유할 때,
- P1: A에 메시지 송신 → P2와 P3가 A로부터 메시지를 수신하려는 경우
“누가 수신?”
(해결책)
① 하나의 연결이 기껏해야 두 프로세스에만 연관되도록 함
② 기껏해야 한번에 한 프로세스만이 receive 연산을 실행하도록 허용함
③ 시스템이 임의로 선택하고, 송신자에게 수신자를 알림
▶ 메일박스의 소유권: “프로세스” ↔ “시스템(운영체제)”
1) 프로세스가 메일박스를 소유하는 경우
- 소유자(메일박스로부터 메시지를 수신하는 프로세스)와 사용자(메일박스에 메시지를 송신하는 프로세스)를 구분할 수 있음
- 프로세스가 종료할 때 그 프로세스가 소유한 메일박스도 사라짐
- 소멸된 메일박스에 메시지를 송신하는 프로세스에게는 예외처리로 이 사실을 알려야 함
- 메일박스의 소유자와 사용자를 지정하는 방법:
프로세스가 메일박스 형태의 변수를 선언하면 소유자가 되고,
그 메일박스의 이름을 아는 모든 프로세스들이 사용자가 됨
2) 운영체제가 메일박스를 소유하는 경우
- 메일박스가 독립적으로 존재
- 운영체제가 다음의 기능을 제공
① 새로운 메일박스의 생성
② 메일박스를 통한 메시지의 송수신
③ 메일박스의 소멸
- 새로운 메일박스를 생성하는 프로세스가 일차적으로 소유자가 되고 수신할 수 있음
- 소유권/수신권은 적절한 시스템 호출에 의해 다른 프로세스에 이전할 수 있음
→ 다중 수신자 발생 가능
- 프로세스들은 프로세스 생성기능을 통해서 메일박스를 공유할 수 있음
ex) 프로세스 P가 메일박스 A를 생성한 후, 다른 프로세스 Q를 생성하면, Q는 A를 공유함
- 메일박스가 어떠한 프로세스에 의해서도 접근되지 않는 경우
→ 쓰레기 수집(garbage collection)으로 해결
4.6.3 버퍼링(Buffering)
▶ 연결(link)의 용량: 임시로 저장할 수 있는 메시지의 최대 수 → ‘메시지 큐’
① 무용량(zero capacity): 큐의 길이가 0인 경우
- 송신자는 수신자가 메시지를 수신할 때까지 기다려야 함
즉, 두 프로세스간에 메시지 전달을 위해 동기화가 필요 - “회합(rendezvous)”
→ “비버퍼링”
② 한계 용량(bounded capacity): 큐의 길이가 n>0인 경우
- 송신자는 큐가 만원이 아니면 메시지를 큐에 넣고 계속 수행하고, 그렇지 않으면 기다려야 함
→ “자동 버퍼링”
③ 무한 용량(unbounded capacity): 큐의 길이가 무한
- 송신자는 결코 기다림 없이 메시지를 큐에 넣고 계속 수행
→ “자동 버퍼링”
* 유용량(nonzero capacity)의 경우, 송신자의 입장에서 메시지의 도달이 확인되지 않음
→ 수신을 확인하기 위해서는 명시적인 통신 필요
P: send(Q, message); Q: receive(P, message) ;
receive(Q, message); send(P, "acknowledgement");
▶ 그 밖의 특별한 경우
① 송신 프로세스가 결코 지연되지 않는 경우: 수신자가 이전의 메시지를 받기 전에 다른 메시지를 보내면 처음의 메시지는 유실됨
→ 메시지가 상실되지 않고 전달되고 송신자와 수신자가 동시에 메시지 버퍼를 조작하지 않는다는 것을 보장하기 위해 명백한 동기화 필요
② 송신 프로세스가 회신을 받을 때까지 지연되는 경우: ex) Thoth 운영체제
P: send(Q, message) ↔ Q: reply(P, message)
↓ ↓
송신자 P의 봉쇄 송신자 Q의 계속 수행 및 수신자 P의 재개
4.6.4 예외 조건(Exception Conditions)
“메시지 기법의 측면에서 오류 발생시 처리해야 하는 예외 조건”
4.6.4.1 프로세스 종료(Process Terminates)
▶ 메시지가 처리되기 전에 송신자나 수신자가 종료될 수 있음
① 결코 송신되지 않을 메시지를 기다리는 프로세스를 남기는 경우
예로써, 수신 프로세스 P가 종료된 송신 프로세스 Q로부터 메시지를 기다리는 경우
- 프로세스 P가 무한 봉쇄됨
- 시스템이 P를 끝내거나 Q의 종료를 알림
② 결코 수신되지 않을 메시지를 남기는 경우
예로써, 송신 프로세스 P가 종료된 수신 프로세스 Q에 송신하는 경우
- 자동 버퍼링의 경우: P 봉쇄 안됨 → 아무 문제없음
- 비버퍼링의 경우: P 봉쇄됨 → ①과 동일한 해결책 가능
4.6.4.2 메시지의 유실(Lost Messages)
메시지 전송
프로세스 P → 프로세스 Q
↖
하드웨어 or 통신라인 고장 ⇒ “메시지 유실”
▶ 메시지 유실의 취급 방법
- 운영체제가 이 사건을 탐지하고 메시지를 재전송
- 송신 프로세스가 이 사건을 탐지하고 원하는 경우에 메시지를 재전송
- 운영체제가 이 사건을 탐지하여 송신 프로세스에 알리면, 송신 프로세스는 원하는 대로 수행
▶ 메시지 유실의 탐지 방법: “타임아웃(time-out)”
- 운영체제나 프로세스가 송신된 메시지에 대해 반드시 뒤따르는 회신에 소요되는 시간 간격을 규정하고, 이 시간을 초과하면 유실된 것으로 간주하고 재 전송하는 방법
- 그러나, 실제로 유실된 것이 아니라 조금 오래 걸릴 수 있는데, 그로 인해 다중 복사가 발생할 수 있음(16장 참조)
4.6.4.3 훼손 메시지(Scrambled Message)
▶ 메시지는 전송 도중에 훼손될 수 있음: ex) 통신 채널의 잡음 등
- 탐지: 오류 검사 코드
- 해결: 유실된 메시지의 경우와 유사 → 일반적으로 운영체제가 재 전송함
4.6.5 실례: Mach
▶ 내용
- 카네기 멜론 대학에서 개발한 분산시스템을 위한 운영체제
- 메일박스(포트)에 의한 메시지 전송
- 시스템 호출도 메시지로 수행
- 각 태스크가 생성될 때마다 두 메일박스 생성
1) 커널 메일박스(Kernel mailbox): 커널이 태스크와 통신하기 위해 사용
2) 통지 메일박스(Notify mailbox): 커널이 사건 발생을 통지하기 위해 사용
- 메시지 전송을 위한 시스템 호출
1) msg_send 호출: 메시지를 메일박스로 전송
2) msg_receive 호출: 메시지를 수신
3) msg_rpc 호출: 메시지 송신 후 회신 메시지를 기다리는 원격 프로시저 호출
- port_allocate 호출: 메일박스의 생성 및 메시지 큐를 위한 공간 할당
→ 소유권/수신권 발생: 양도 가능
- 메시지의 구성
1) 고정 길이의 헤더 부분: 메시지 길이, 수신 메일박스명, 송신 메일박스명(회신용)
2) 가변 길이의 자료 부분: 정형화된 자료의 리스트
- 메일박스가 만원일 때, 송신 스레드의 선택
1) 여유공간이 생길 때까지 무한정 대기
2) 최대 n 미리초 대기
3) 전혀 기다리지 않고, 즉시 복귀
4) 임시로 메시지를 캐시(cache)하는 방법
- 메일박스가 만원일 때, 하나의 메시지에 한해서 운영체제가 보관하도록 넘길 수 있음
- 그 메시지가 실제로 메일박스에 놓이게 되면, 송신자에게도 그 메시지를 되돌려 보내 알림
- port_status 호출: 주어진 메일박스의 메시지 수를 반환
- 송신자의 메시지를 포함하는 주소 공간을 수신자의 주소 공간에 매핑함으로써, 두 번의 메시지 전송(송신자 → 메일박스 → 수신자)을 피하고 있음
4.6.6 실례: Windows NT
▶ 내용
- 다중 운영 환경인 부 시스템을 지원: 응용 프로그램들이 메시지 전송 기법으로 부 시스템과 통신
→ 응용 프로그램들을 NT 부 시스템 서버의 클라이언트로 간주함
- NT의 메시지 전달 기능: 지역 프로시저 호출(Local Procedure Call; LPC) 기능
→ 두 프로세스간의 연결 설정을 위해 포트(메일박스) 객체를 사용
- 두 종류의 포트 사용: 연결 포트(connection port) & 통신 포트(communication port)
→ 실제로는 동일하나 사용 방법에 따라 다른 이름으로 사용
- 통신 작업:
1) 클라이언트는 부 시스템의 연결 포트 객체에 대한 핸들을 개방함
2) 클라이언트는 연결 요청을 보냄
3) 서버는 개인적인 두 통신 포트를 생성하고 클라이언트에게 그 중 하나에 대한 핸들을 반환
4) 클라이언트와 서버는 대응된 포트 핸들을 사용하여 메시지를 보내거나 회신(callback)하거나 응답을 기다림
- 세 가지 메시지 전달 기법
1) 포트의 메시지 큐를 사용하는 방법: 작은 메시지(256 바이트 이내) 전송
2) 섹션 객체(공유기억장치)를 사용하는 방법: 보다 큰 메시지 전송
3) quick LPC: 그래픽 함수와 같은 기본적인 함수의 실행시 사용
'시리즈 > OS' 카테고리의 다른 글
10. 파일 시스템 인터페이스(File System Interface) (0) | 2014.08.07 |
---|---|
가상 기억장치(Virtual Memory) (0) | 2014.07.27 |
기억장치 관리(Memory Management) (0) | 2014.07.25 |
운영체제 - 교착 상태(Deadlocks) (0) | 2014.07.24 |
중앙처리장치 스케줄링(CPU Scheduling) (0) | 2014.07.17 |