일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- OOP
- X.org
- Binary Search
- Class
- Physical Scheme
- Inheritance
- spring
- 자바
- 백준
- literal
- descriptive statistics
- Mac
- Operator
- BFS
- 리눅스
- selenium
- Reference Type
- Entity Set
- X윈도우
- Java
- dbms
- Entity
- 리눅스 마스터 1급
- Unity
- External Scheme
- 셀레니움
- Polymolphism
- preprocessing
- systemd
- python
- Today
- Total
Byeol Lo
3.5 IPC in Shared-Memory Systems 본문
공유 메모리를 사용한 IPC(Inter-Process Communication)은 통신하는 프로세스가 공유 메모리 영역을 설정해야 한다. 일반적으로 공유 메모리 영역은 공유 메모리 세그먼트를 생성한 프로세스의 주소 공간에 존재한다. 이 공유 메모리 세그먼트를 사용하여 통신하려는 다른 프로세스는 해당 세그먼트를 자신의 주소 공간에 첨부해야 한다. 보통 운영 체제는 한 프로세스가 다른 프로세스의 메모리에 액세스하는 것을 막는데, 공유 메모리는 두 개 이상의 프로세스가 이 제한을 해제하기로 합의해야 한다. 그런 다음에 공유 영역에서 데이터를 읽고 쓰는 방식으로 정보를 교환할 수 있다. 데이터의 형식과 위치는 프로세스에 의해 결정되며 운영 체제의 제어 아레에 있지 않게 된다. 또한 프로세스는 동시에 동일한 위치에 쓰지 않도록 보장하는 책임이 있다.
먼저 producer-consumer 문제를 보자. 이는 협력하는 프로세스의 일반적인 패러다임인데, 어떤 프로세스에서 사용되는 정보(로드된 어셈블리 코드)를 생성한다고 해보자. 이때 이 프로세스를 소비자 프로세스라고 하자. 이때 어셈블러는 다시 로더에서 사용되는 객체 모듈을 생성한다. 일반적으로 서버를 생산자로, 클라이언트를 소비자로 생각하면 된다. 웹 서버는 HTML 파일과 이미지와 같은 웹 콘텐츠를 제공하고, 리소스를 요청하는 클라이언트를 소비자로 생각하면 된다.
이때 서로간의 교류가 발생하는데, 이 데이터들에 대한 일종의 동기화가 필요하게 된다. 이 문제는 다수의 생산자와 소비자가 있을 때 발생하는 복잡한 상황도 고려될 수 있다. 이를 위해 생산자가 데이터를 버퍼에 안전하게 넣을 수 있도록 버퍼의 용량을 관리하고, 소비자가 버퍼에서 안전하게 데이터를 제거할 수 있도록 동기화를 해주어야 한다. 이 동기화를 위해 공유 메모리를 사용하는 것이다. 생산자와 소비자 프로세스가 동시에 실행되도록 하려면, 생산자가 채우고 소비자가 비우는 항목의 버퍼가 있어야 한다. 이 버퍼는 생산자와 소비자 프로세스에 의해 공유되는 메모리 영역에 있을 것이다. 생산자는 한 항목을 생성하는 동안 소비자가 다른 항목을 소비할 수 있어야 한다. 생산자와 소비자는 동기화되어야 하며, 소비자가 아직 생성되지 않은 항목을 소비하지 않도록 해야 한다.
이때 두 종류의 버퍼를 사용할 수 있는데 unbounded buffer(무한 버퍼)는 버퍼의 크기에 대한 실질적인 제한이 없다. 소비자는 새 항목을 기다려야 할 수 있지만, 생산자는 항상 새 항목을 생성할 수 있다. bounded buffer(유한 버퍼)는 고정된 버퍼 크기를 가정한다. 이 경우에는 버퍼가 비어 있으면 소비자는 기다려야 하고, 버퍼가 가득 차 있으면 생산자는 기다려야 한다.
유한 버퍼가 어떻게 공유 메모리를 사용한 프로세스 간 통신을 보여주는지 보자.
#define BUFFER_SIZE 10
typedef struct {
...
} item;
item buffer[BUFFER_SIZE];
int in = 0;
int out = 0;
위는 생산자와 소비자가 사용하는 구조체다.
// PRODUCTOR WITH SHARED MEMORY
item next_produced;
while (true) {
// busy-waiting
while (((in+1) % BUFFER_SIZE) == out)
; /* do nothing */
buffer[in] = next_produced;
// Update next addr.
in = (in + 1) % BUFFER_SIZE;
/* =========================== *
* CONSUMER WITH SHARED MEMORY */
item next_consumed;
while (true) {
while (in == out)
;
next_consumed = buffer[out];
out = (out + 1) % BUFFER_SIZE;
}
공유 버퍼는 두 개의 논리적 포인터인 in과 out을 사용하여 원형 배열(circular_linked_list)로 구현된다. in 변수는 버퍼 내에서 다음 빈 위치를 가르키고, out은 버퍼 내에서 첫 번째로 채워진 위치를 가르킨다. in == out 일 때 버퍼는 비어있고, ((in+1) % BUFFER_SIZE) == out 일 때 버퍼는 가득 찬 상태가 된다.
생산자 프로세스에는 새롭게 생성할 항목이 저장되는 next_produced 라는 로컬 변수가 있고, 소비자 프로세스에는 소비될 항목이 저장되는 next_consumed 라는 로컬 변수가 있다. 이 방식을 통해 한번에 BUFFER_SIZE -1 개의 항목이 버퍼에 있을 수 있고, BUFFER_SIZE 개의 항목이 동시에 버퍼에 있을 수 있는 해결책을 찾는 것을 한번 머리 속으로 상상해보길 바란다(힌트는 busy-waiting이 걸릴 때). 여기서 다루지 않는 문제는 소비자와 생산자가 동시에 공유 버퍼에 액세스를 하려고 할 수 있다. 이는 추후에 논의하도록 하자.
'OS > OS Design' 카테고리의 다른 글
3.7 Examples of IPC Systems (1) | 2024.04.13 |
---|---|
3.6 IPC in Message-Passing Systems (0) | 2024.04.13 |
3.4 Interprocess Communication (1) | 2024.04.12 |
3.3 Operations on Processes (1) | 2024.04.12 |
3.2 Process Scheduling (0) | 2024.04.10 |