IPC를 공부할 때 정말 어려웠는데🤯🤯🤯🤯
taegyunwoo.github 이 분이 작성하신 포스팅이 정말 많은 도움이 되었다.
한 번 보고오시길😼
특징
종류 | Message passing(메세지 전달) | Shared memory(공유 메모리) |
---|---|---|
방식 | 커널을 통해 메세지를 전달(메세지 송신, 메세지 수신)하는 방식으로 자원이나 데이터를 주고 받는다. | 프로세스끼리 특정 공통의 메모리 영역인 세그먼트를 공유하며 자원이나 데이터를 공유하는 방식이다. |
장점 | ① 커널을 이용하기 때문에 구현이 비교적 쉽다. ② 보내는 쪽과 받는 쪽 사이의 동기화 문제나 충돌 상황을 처리하는 것이 상대적으로 간단해진다. | 커널 의존도가 낮기 때문에 속도가 빠르고 통신이 자유롭다. |
단점 | 시스템 콜이 필요하며 이로 인해 커널 오버헤드가 발생해 속도가 느릴 수 있다. | 자원과 데이터를 공유하기 때문에 동기화 문제가 발생한다. |
종류 | Pipe, Signal, Message Queueing, Socket 등 | Semaphores 등 |
전역 변수를 사용하는 통신 방식의 문제는 언제 데이터를 보낼지 데이터를 받는 쪽에서 모른다는 것이다.
그러므로 데이터를 받는 쪽에서 반복적으로 전역 변수의 값을 점검하여 언제 데이터를 보냈는지 확인해야한다.
이처럼 상태 변화를 살펴보기 위해 반복문을 무한 실행하며 기다리는 것을 바쁜 대기라고 한다.
바쁜 대기 문제를 해결하기 위해서는 데이터가 도착했음을 알려주는 동기화(Synchronization)를 사용해야 한다.
(ex. 메세지 도착 알림은 동기화의 예시다.)
따라서 프로세스 통신은 동기화 기능이 있느냐 없느냐에 따라 아래와 같이 나뉜다.
분류 | 동기화 통신 (대기가 있는 통신) | 비동기화 통신 (대기가 없는 통신) |
---|---|---|
예시 | 파이프, 소켓 | 전역변수,파일 |
동기화는 봉쇄(Blocking)와 비봉쇄(Non-blocking) 방식이 있다.
봉쇄(Blocking)을 통해, 동기(Sync)를 할 수 있다.
하나의 프로세스나 스레드가 어떤 조건을 충족할 때까지 다른 프로세스나 스레드가 실행되지 않고 대기하는 방식이다.
장점 : 프로세스 간의 통신이 완전히 이루어 질 때까지 다른 작업 진행하지 않으므로 데이터 일관성과 순서를 보장하는 데 유용하다.
단점 : 프로세스 간의 통신이 완전히 이루어질때까지 해당 프로세스가 일시중단되므로 효율성이 저하될 수 있다.
종류 | 봉쇄형 송신 | 봉쇄형 수신 |
---|---|---|
설명 | sender는 보낸 메시지가 수신될 때까지 대기한다. | receiver는 메시지가 도착할 때까지 대기한다. |
비봉쇄형 (= Non-blocking)
비봉쇄(Unblocking)을 통해, 비동기(async)를 할 수 있다.
한 프로세스나 스레드가 대기 상태에 들어가지 않고 계속해서 다른 작업을 수행할 수 있도록 하는 방식이다.
종류 | 비봉쇄형 송신 | 비봉쇄형 수신 |
---|---|---|
설명 | sender는 메시지를 보내고, 할일을 계속한다. | receiver는 ‘유효한 메시지를 받거나’, ‘Null 메시지라도 받아서’ 작업을 계속한다. |
프로세스는 데이터를 주거나 받으면 통신을 한다.
동기화에 따른 분류 외에 데이터가 전송되는 방향에 따라 통신을 분류할 수 있다.
분류 | 양방향 | 단양방향 | 단방향 |
---|---|---|---|
방식 | 데이터를 동시에 양쪽 방향으로 전송할 수 있는 구조로, 일반적인 통신은 모두 양방향 통신이다. | 데이터를 양쪽 방향으로 전송할 수 있지만 동시 전송은 불가능하고 특정 시점에 한쪽 방향으로만 전송할 수 있는 구조이다. | 모스 신호처럼 한쪽 방향으로만 데이터를 전송할 수 있는 구조이다. |
예시 | 소켓 | 무전기 | 파이프, 전역 변수 |
msg_send()
: 메시지를 메일박스에 보낸다.msg_receive()
: 메일박스에서 메시지를 수신한다.mag_rpc()
: 원격 프로시저 콜port_allocate()
: 새로운 메일박스 생성, 메일박스 메세지 큐를 위한 공간 할당원격 프로시저 콜(RPC, Remote Procedure Call)
Pipe(파이프)
일반 파이프(Ordinary Pipes) (= 익명 파이프)
지명 파이프(Named Pipe) (= FIFO)
임계구역과 관련된 전통적인 문제로 생산자 - 소비자 문제가 있다.
input(buf)
output(buf)
sum
이라는 전역 변수를 사용한다.생산자-소비자 문제 예시
sum=sum+1;
하고 코드 실행을 하지 않은 상태이다.sum=sum-1;
하고 코드 실행을 하지 않은 상태이다.버퍼의 종류
종류 | 무한버퍼 | 유한 버퍼 |
---|---|---|
특징 | ① 제한이 없는 크기를 갖는 버퍼 ② 크기에 대한 제약이 없으므로 생산자가 계속해서 데이터를 생성하고, 소비자가 필요할 때마다 데이터를 가져갈 수 있다. | ① 고정된 크기를 갖는 버퍼 ② 사전에 정해진 크기만큼만 데이터를 저장, 만약 버퍼가 가득 차거나 비어있으면, 생산자나 소비자 프로세스는 대기 상태에 들어간다. |
장점 | 손실이 안된다 | 딜레이를 줄일 수 있다. |
단점 | 쌓인 데이터가 많을 시 속도가 느려진다. | packet 손실이 발생할 수 있다. |
유한 버퍼 소스코드⭐⭐⭐⭐
#define BUFFER_SIZE 4
typedef struct {
//...
} item;
item buffer[BUFFER_SIZE]; // 사이즈 4인 배열 선언
int in = 0; // 버퍼에서 다음으로 올라갈 프로세스 (쓸 곳의 index)
int out = 0; // 다음에 실행될 프로세스 (읽을 곳의 index)
버퍼 상태
in == out
: buffer empty((in+1)%BUFFER_SIZE) == out
: buffer full아래 코드는 버퍼에 생산자가 공유할 데이터를 생산하는 예시 코드이다.
item next_produced; // 다음 생성할 원소 저장
while (true) {
whlie(((in + 1) % BUFFER_SIZE == out) {// buffer full인 경우, 새로운 원소 저장할 수 없다면 대기 상태
/* do nothing */
}
buffer[in] = next_produced; // 비어 있는 곳 변수(in)에 item 삽입(생산)
in = (in + 1) % BUFFER_SIZE; // 변수 in이 다음 비어 있는 원소를 가리키도록 한다.
}
((in+1)%BUFFER_SIZE)==out
: 해당 조건은 원형 큐 형태인 버퍼가 full 상태인지 판단한다.while 문
을 통해, 버퍼가 꽉 차있으면 공유할 데이터를 삽입하지 않도록 한다. item next_consumed;
while (true) {
while (in==out) { // 공백 상태일 때
; /* do nothing */
next_consumed = buffer[out]; // 첫 번째 원소 가져옴
out = (out+1) % BUFFER_SIZE; // 변수 out이 다음 원소로 인덱스 이동
}
BUFFER_SIZE-1
개 만큼의 버퍼를 사용할 수 있다.(in+1)%4 == 0
이므로 buffer가 full 상태가 되어 더 이상 입력을 받지 않음(in+1)%4==0
이므로 in ==out
인 상태이다, 따라서 full과 empty 구분이 안된다.