Operating System Concepts, 10th Edition을 읽고 정리하기 위해 작성하는 글입니다.
운영 체제에서 동시에 실행되는 프로세스는 독립(independent) 프로세스이거나 협력(cooperating) 프로세스일 수 있다. 독립 프로세스는 다른 프로세스와 데이터를 공유하지 않지만, 협력 프로세스는 다른 프로세스에 영향을 미치거나 받을 수 있다.
프로세스 협력을 허용하는 환경을 제공하는 이유는 크게 세 가지로 나눌 수 있다.
협력 프로세스는 데이터를 교환할 수 있는 프로세스간 통신(interprocess communication, IPC) 매커니즘이 필요하다. IPC는 공유 메모리(shared memory)와 메시지 전달(message passing)이라는 두 가지 기본 모델이 존재한다.
공유 메모리 모델은 협력 프로세스가 공유하는 메모리 영역이 설정되고, 공유 영역에서 데이터를 읽고 쓰면서 정보를 교환할 수 있다.
메시지 전달 모델은 협력 프로세스 간에 교환되는 메시지를 통해 통신이 이루어진다.

위의 그림에서 (a)는 공유 메모리 모델을 나타내고, (b)는 메시지 전달 모델을 나타낸다.
공유 메모리, 메시지 전달 모델은 운영 체제에서 일반적이며, 많은 시스템이 둘다 구현한다.
메시지 전달 모델은 충돌을 피할 필요가 없기 때문에 적은 양의 데이터를 교환하는데 유용하며 공유 메모리 모델 보다 분산 시스템에서 구현하기 더 쉽다.
메시지 전달 모델은 시스템 콜을 사용하여 구현하기 때문에 커널 개입이 필요하여 더 많은 시간이 소요되는 작업이 필요하기 때문에 공유 메모리 모델이 메시지 전달 모델보다 더 빠를 수 있다.
공유 메모리를 사용하는 IPC는 공유 메모리 영역을 설정하기 위해 통신 프로세스가 필요하다. 공유 메모리 영역은 공유 메모리 세그먼트를 만드는 프로세스의 주소 공간에 존재하는데 이 세그먼트를 사용하여 통신하려는 다른 프로세스는 해당 주소 공간에 연결해야 한다.
운영 체제는 일반적으로 다른 프로세스의 메모리에 액세스하는 것을 방지하기 때문에 공유 메모리를 사용하여 통신하려면 두 개 이상의 프로세스가 이 제한을 제거하는 것을 동의해야한다. 데이터의 형식과 위치는 이러한 프로세스에 의해 결정되며 운영 체제의 제어를 받지않는다.
협력 프로세스의 일반적인 패러다임인 생산자-소비자(producer–consumer) 문제를 통해 협력 프로세스의 개념을 쉽게 설명할 수 있다.
생산자 프로세스는 소비자 프로세스에서 소비되는 정보를 생성한다.
생산자-소비자 문제에 대한 한 가지 해결책은 공유 메모리를 사용하는 것이다. 생산자와 소비자 프로세스가 동시에 실행되려면 생산자가 채우고 소비자가 비울 수 있는 항목 버퍼가 있어야 한다. 이 버퍼는 생산자와 소비자가 공유하는 메모리 영역에 상주한다. 생산자와 소비자는 동기화되어 있어야 한다.
버퍼는 두 가지 유형이 존재한다.
#define BUFFER_SIZE 10
typedef struct {
...
} item;
item buffer[BUFFER_SIZE];
int in = 0;
int out = 0;
in과 out이 있는 원형 배열로 구현된다.in은 다음 빈 위치를 가르키고, out은 첫 번째로 가득 찬 위치를 가르킨다.in == out일 때 버퍼가 비어있고, ((in + 1) % BUFFER SIZE) == out일 때 버퍼가 가득 차있다.item next_produced;
while (true) {
/* next_produced에 항목을 생산한다. */
while (((in + 1) % BUFFER SIZE) == out)
; /* do nothing */
buffer[in] = next_produced;
in = (in + 1) % BUFFER SIZE;
}
item next_consumed;
while (true) {
while (in == out)
; /* do nothing */
next consumed = buffer[out];
out = (out + 1) % BUFFER SIZE;
/* next_consumed의 항목을 소비한다. */
}
이 방식은 동시에 최대 BUFFER_SIZE − 1개의 항목이 버퍼에 존재할 수 있다. 상태 플러그나 버퍼 개수를 추적하는 count 변수를 사용하면 동시에 BUFFER_SIZE개의 항목이 버퍼에 존재할 수 있다.
메시지 전달은 프로세스가 동일한 주소 공간을 공유하지 않고도 통신하고 동작을 동기화할 수 있는 메커니즘을 제공한다. 메시지 전달은 특히 통신 프로세스가 네트워크로 연결된 다른 컴퓨터에 상주할 수 있는 분산 환경에서 유용하다.
메시지 전달은 send(message) 및 receive(message) 작업을 제공하는데 메시지의 크기는 고정이거나 가변일 수 있다. 고정 크기 메시지는 시스템 레벨의 구현은 간단하지만 이러한 제한으로 프로그래밍 작업이 어렵고, 가변 크기 메시지는 시스템 레벨의 구현이 더 복잡하지만 프로그래밍 작업은 더 간단하다.
프로세스가 통신하려면 서로 메시지를 주고 받아야 하기 때문에 두 프로세스 사이에 통신 링크가 필요하다. 통신 링크는 직간접 통신, 동기나 비동기 통신, 자동 또는 명시적 버퍼링을 통해 논리적으로 구현할 수 있다.
통신을 원하는 프로세스는 서로를 참조할 방법이 필요하는데, 이런 프로세스는 직간접 통신을 사용할 수 있다.
직접 통신(direct communication)는 각 프로세스는 수신자와 발신자를 명시적으로 지정해야 한다.
send(P, message)는 프로세스 P에게 메시지를 보낸다.receive(Q, message)는 프로세스 Q로부터 메시지를 받는다.직접 통신 방식의 통신 링크는 모든 프로세스 쌍 사이에 링크가 자동으로 설정되고 프로세스는 통신을 위해 서로의 ID만 알면된다. 링크는 두 개의 프로세스와 연결되며 각 프로세스 쌍 사이에는 하나의 링크만 존재한다.
위의 방식은 송신, 수신 프로세스 모두 통신을 위해 서로를 지정하는 주소 지정에서 대칭성을 보이는데 주소 지정에서 비대칭성을 사용하는 경우도 존재한다.
send(P, message)는 프로세스 P에 메시지를 보낸다.receive(id, message)는 특정 프로세스에서 메시지를 받고 id는 통신이 이루어진 프로세스의 이름으로 설정된다.직접 통신 방식의 단점은 프로세스 정의의 모듈성이 제한되어 있다는 점이다. 프로세스 식별자를 변경하려면 다른 프로세스의 정의를 검토해야하고 이전 식별자의 모든 참조를 찾아 새 식별자로 수정해야 한다.
간접 통신(indirect communication)는 메시지가 메일 박스(mailbox)나 포트(port)로 전송되고 수신된다. 메일 박스는 메시지를 넣고 제거할 수 있는 객체로 볼 수 있으며 각 메일 박스는 고유한 식별자가 존재한다.
send(A, message)는 메일박스 A로 메시지를 보낸다.receive(A, message)는 메일박스 A에서 메시지를 받는다.간접 통신 방식의 통신 링크는 두 프로세스가 공유 메일 박스를 갖고 있는 경우에만 통신이 가능하다. 링크는 두 개 이상의 프로세스와 연결될 수 있고, 각 프로세스 쌍 사이에는 여러 개의 서로 다른 링크가 존재할 수 있으며 각 링크는 하나의 메일 박스에 해당된다.
예시) 프로세스 , , 가 메일 박스 A를 공유하는 상황에서 이 A에 메시지를 보내고 , 는 A에서 receive()를 실행
receive()를 실행하도록 허용하는 방식메일 박스는 프로세스나 운영 체제가 소유할 수 있다.
프로세스가 소유한 경우는 보통 메일 박스가 프로세스의 주소 공간의 일부인 경우인데 이때 소유자와 사용자를 구별한다. 메일 박스를 소유한 프로세스가 종료되면 메일 박스는 사라지고, 사용자에게 메일 박스가 존재하지 않는 알림을 보내야한다.
운영 체제가 소유한 메일 박스는 자체적으로 존재하고 특정 프로세스에 연결되어 있지 않는다. 운영 체제는 프로세스가 새 메일 박스를 생성, 메일 박스를 통해 메시지를 주고 받기, 메일 박스 삭제 매커니즘을 제공해야한다. 새 메일 박스를 만드는 프로세스는 현재 해당 메일 박스의 소유자이지만 소유권과 수신 권한은 시스템 콜을 통해 다른 프로세스에 전달될 수 있다는 점에서 첫 번째 방식과 다르다.
프로세스 간 통신은 send() 및 receive() 기본형에 대한 호출을 통해 이루어지며 메시지 전달은 차단(blocking)이나 비차단(nonblocking) 또는 동기(synchronous)나 비동기(asynchronous)일 수 이다.
send()와 receive()가 모두 차단되면 송신자와 수신자 사이에 랑데부가 생기고, 생산자-소비자 문제에 대한 해결책이 간단해 진다.
send()와 receive()가 모두 비차단되면 생산자가 빠를 때, 버퍼가 넘쳐 데이터가 손실되고, 소비자가 빠를 때, 데이터가 없어 빈 값을 받을 수 있다.
message next_produced;
while (true) {
/* next_produced에 항목을 생산한다. */
send(next_produced);
}
message next_consumed;
while (true) {
receive(next_consumed);
/* next_consumed에 항목을 소비한다. */
}
통신이 직접이든 간접이든 상관없이 통신 프로세스가 교환하는 메시지는 임시 큐(temporary queue)에 상주한다. 이러한 큐는 세 가지 방법으로 구현할 수 있다.
제로 공간은 종종 버퍼링 없는 메시지 시스템이라고 하고, 나머지는 자동 버퍼링이 있는 시스템이라고 한다.