Notice
Recent Posts
Recent Comments
05-21 07:17
«   2024/05   »
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
Archives
Today
Total
관리 메뉴

Byeol Lo

3.5 IPC in Shared-Memory Systems 본문

OS/OS Design

3.5 IPC in Shared-Memory Systems

알 수 없는 사용자 2024. 4. 12. 04:51

 공유 메모리를 사용한 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
Comments