목적
- 프로세스 간 통신이 필요한 이유를 이해한다.
- 프로세스의 통신 방법을 다양한 측면에서 이해한다.
실천 목표
- 공유 메모리 및 메시지 전달을 사용하는 프로세스 간 통신을 설명하고 대조한다.
프로세스의 종류 (관계적 측면)
- 독립적인 프로세스
- 다른 프로세스들과 데이터를 공유하지 않는 프로세스
- 협력적인 프로세스
- 다른 프로세스들과 데이터를 공유하는 프로세스
- O/S에서 프로세스 협력을 제공하는 이유로는 다음과 같은 이유가 있음
- 정보 공유
- 계산 가속화
- 모듈성 향상
프로세스 간 통신 (IPC, InterProcess Communication)
- 협력적인 프로세스는 프로세스 간 통신(IPC) 기법을 필요로 함
- IPC에는 두 가지 모델이 존재
- 협력적인 프로세스의 일반적인 패러다임은 생산자-소비자 문제(Producer-Consumer Problem)이므로 이를 기반으로 살펴보기로 함
IPC의 모델 (1) Shared Memory
- 생산자와 소비자가 Concurrently하게 Run되도록 하기 위해서
- 그 사이에 Buffer(Shared Memory를 실제로 구현한 것)를 두고,
- 생산자는 채우고 소비자는 비우는 역할을 하는 모델
- 구체적인 코드로 살펴보면,
- Buffer (Shared Memory) 및 선언부
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
#define BUFFER_SIZE 10 typedef struct { /* 공유하고자 하는 데이터의 구조를 채움 */ } item; item buffer[BUFFER_SIZE]; // Shared Memory를 구축하는 것은 Shared Memory를 사용하는 프로세스들,을 만드는 애플리케이션 개발자 // 현재는 이 선언이 되어 있는 전역 변수의 영역이고, 이 전역 변수도 특정 프로세스에 속해 있으므로 사실상 이것은 Shared Memory가 아님 // Shared Memory는 공유 메모리 세그먼트를 생성하는 프로세스의 주소 공간에 위치하지만 // 이후에 해당 프로세스와 분리됨 // 하지만 이해를 돕기 위해 임의로 이렇게 만들어서 buffer가 어떤 역할을 하는지 살펴보고자 함 int in = 0; int out = 0;
- 생산자
1 2 3 4 5 6 7 8 9 10 11 12 13
item next_produced; while(true) { next_produced = (item*) malloc(sizeof(item)); while (((in + 1) % BUFFER_SIZE) == out) // (in + 1) == out 인 경우 즉, BUFFER가 가득 찬 경우 ; // 아무 것도 하지 않음 // (in + 1) == out 인 경우 즉, BUFFER가 가득 찬 경우인데, // in이 오른쪽 끝(BUFFER_SIZE)에, out이 왼쪽 끝(0)에 위치한 경우를 포괄하기 위해 BUFFER(>= in + 1)에 대한 나눗셈 나머지 처리를 한 것 buffer[in] = next_produced; in = (in + 1) % BUFFER_SIZE; }
- 소비자
1 2 3 4 5 6 7 8 9 10 11
item next_consumed; while(true) { while (in == out) // in == out인 경우 즉, BUFFER가 비어있는 경우 ; // 아무 것도 하지 않음 next_consumed = buffer[out]; buffer[out] = null; out = (out + 1) % BUFFER_SIZE; }
- Buffer (Shared Memory) 및 선언부
- 구체적인 코드에서처럼 애플리케이션 프로그래머들이 명시적으로 (알아서) Shared Memory에 접근하고 Shared Memory를 관리하는 코드를 작성해주어야 함
IPC의 모델 (2) Message Passing
- 근본적인 개념은 Shared Memory인데,
- 이 Shared Memory에 대한 접근과 Shared Memoroy 관리를 O/S가 하는 것
- O/S가 Shared Memory를 관리하며 프로세스로 하여금 Message Passing 방식으로 통신할 수 있게 함
- 간단한 코드로 살펴보면,
- 생산자
1 2 3 4 5 6 7
item next_produced; while(true) { next_produced = (item*) malloc(sizeof(item)); send(next_produced); // ☆ 간단하게 O/S에서 제공하는 API(시스템콜) 이용 }
- 소비자
1 2 3 4 5
item next_consumed; while(true) { receive(next_consumed); // ☆ 간단하게 O/S에서 제공하는 API(시스템콜) 이용 }
- 생산자
- 이때 생산자와 소비자 간 연결을 Communication Link라고 함
- 이 Communication Link는 다양한 구조로 구현 가능
- Direct Communication Link
- Direct Communication Link는 생산자가 소비자를 명시(예: send(Q, message))하고 소비자도 생산자를 명시(예: receive(P, message))
- 위와 같은 방법으로 Communication Link는 자동으로 성립
- 한 Link에는 정확히 2개의 Process가 연관되어 있음
- Link가 필요한 한 쌍의 Process에는 정확히 1개의 Link 존재
- Indirect Communication Link
- Indirect Communication Link는 Port(구 Mailbox)라는 Object를 통해 간접적으로 메시지를 주고 받음 (예: send(portNum, message), receive(portNum, message))
- 2개의 Process가 Port를 공유할 때에만 Communication Link 성립
- 한 Link에 2개 이상의 Process가 연관될 수 있음
- Link가 필요한 한 쌍의 Process에는 1개 이상의 Link가 존재할 수 있음 (한 Link는 한 Port와 대응)
- O/S는 Process로 하여금 Port를 (1) Create, (2) Delete하고 Port 내 Message를 (3) Send, (4) Receive하는 매커니즘(API) 제공
- Direct Communication Link
- Message를 주고 받는 방법도 다양한 디자인 옵션이 존재
- Synchronous (Blocking)
- Blocking Send : Message가 소비자에게 수신될 때까지 생산자가 Blocked된 채로 대기
- Blocking Receive : Message가 유효하게 받을 수 있는 상태가 될 때까지 소비자가 Blocked된 채로 대기
- Asynchronous (Non-blocking)
- Non-blocking Send : Message가 소비자에게 수신이 잘 되었는지 여부에 상관없이 생산자는 하던 일을 진행
- Non-blocking Receive : Message가 유효하게 받을 수 있는 상태인지 여부에 상관없이 소비자는 Message를 수신 (심지어 null인 경우에도)
- Synchronous (Blocking)
참고
- 운영체제 공룡책 강의 | 주니온 | 인프런 https://www.inflearn.com/course/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%EA%B3%B5%EB%A3%A1%EC%B1%85-%EC%A0%84%EA%B3%B5%EA%B0%95%EC%9D%98/dashboard
운영체제 제 10판 Abraham Silberschatz, Peter Baer Galvin, Greg Gagne 저/박민규 역 퍼스트북 2020년 02월 28일 - 운영체제 제 10판 솔루션 https://codex.cs.yale.edu/avi/os-book/OS10/practice-exercises/index-solu.html