[OS] IPC(프로세스간 통신)
IPC를 공부할 때 정말 어려웠는데🤯🤯🤯🤯 taegyunwoo.github 이 분이 작성하신 포스팅이 정말 많은 도움이 되었다. 한 번 보고오시길😼
▶ IPC란?
- IPC (Inter-Process Communication)란?
- 동일한 시스템에서 실행되는 두 개 이상의 프로세스 간에 데이터를 교환을 의미한다.
-
특징
- 컴퓨터 시스템에서 프로그램은 독립적인 공간을 가진다.
- 즉, OS는 메모리 공간에 대해서 다른 프로세스가 접근하지 못하도록 외부 접근을 차단한다.
- 이렇게 프로세스는 원래 독립적이지만, 상황에 따라서 프로세스끼리 협력해야하는 경우가 발생한다.
- 이럴 때 프로세스간 자원과 데이터를 공유할 수 있어야 하는데, 커널 영역에서 IPC라는 내부 프로세스간 통신을 제공하게 되고, 프로세스는 커널이 제공하는 IPC설비를 이용해서 프로세스간 통신을 할 수 있게 된다.
- 따라서, 프로세스는 독립적이고 협력적이라는 특징을 가지고 있다.
- 컴퓨터 시스템에서 프로그램은 독립적인 공간을 가진다.
▶ IPC 모델
종류 | Message passing(메세지 전달) | Shared memory(공유 메모리) |
---|---|---|
방식 | 커널을 통해 메세지를 전달(메세지 송신, 메세지 수신)하는 방식으로 자원이나 데이터를 주고 받는다. | 프로세스끼리 특정 공통의 메모리 영역인 세그먼트를 공유하며 자원이나 데이터를 공유하는 방식이다. |
장점 | ① 커널을 이용하기 때문에 구현이 비교적 쉽다. ② 보내는 쪽과 받는 쪽 사이의 동기화 문제나 충돌 상황을 처리하는 것이 상대적으로 간단해진다. |
커널 의존도가 낮기 때문에 속도가 빠르고 통신이 자유롭다. |
단점 | 시스템 콜이 필요하며 이로 인해 커널 오버헤드가 발생해 속도가 느릴 수 있다. | 자원과 데이터를 공유하기 때문에 동기화 문제가 발생한다. |
종류 | Pipe, Signal, Message Queueing, Socket 등 | Semaphores 등 |
▷ Shared memory
- 공유 메모리 원리
- 공유 메모리를 사용하는 프로세스들은 공유 메모리 영역인 세그먼트를 구축해야 한다.
▷ Message passing
① 동기화에 따른 분류
- 전역 변수를 사용하는 통신 방식의 문제는 언제 데이터를 보낼지 데이터를 받는 쪽에서 모른다는 것이다. 그러므로 데이터를 받는 쪽에서 반복적으로 전역 변수의 값을 점검하여 언제 데이터를 보냈는지 확인해야한다.
- 이처럼 상태 변화를 살펴보기 위해 반복문을 무한 실행하며 기다리는 것을 바쁜 대기라고 한다.
- 바쁜 대기 문제를 해결하기 위해서는 데이터가 도착했음을 알려주는 동기화(Synchronization)를 사용해야 한다. (ex. 메세지 도착 알림은 동기화의 예시다.)
-
따라서 프로세스 통신은 동기화 기능이 있느냐 없느냐에 따라 아래와 같이 나뉜다.
분류 동기화 통신 (대기가 있는 통신) 비동기화 통신 (대기가 없는 통신) 예시 파이프, 소켓 전역변수,파일
동기화는 봉쇄(Blocking)와 비봉쇄(Non-blocking) 방식이 있다.
- 봉쇄(Blocking)을 통해, 동기(Sync)를 할 수 있다.
-
하나의 프로세스나 스레드가 어떤 조건을 충족할 때까지 다른 프로세스나 스레드가 실행되지 않고 대기하는 방식이다.
- 장점 : 프로세스 간의 통신이 완전히 이루어 질 때까지 다른 작업 진행하지 않으므로 데이터 일관성과 순서를 보장하는 데 유용하다.
-
단점 : 프로세스 간의 통신이 완전히 이루어질때까지 해당 프로세스가 일시중단되므로 효율성이 저하될 수 있다.
종류 봉쇄형 송신 봉쇄형 수신 설명 sender는 보낸 메시지가 수신될 때까지 대기한다. receiver는 메시지가 도착할 때까지 대기한다.
-
비봉쇄형 (= Non-blocking)
-
비봉쇄(Unblocking)을 통해, 비동기(async)를 할 수 있다.
-
한 프로세스나 스레드가 대기 상태에 들어가지 않고 계속해서 다른 작업을 수행할 수 있도록 하는 방식이다.
종류 비봉쇄형 송신 비봉쇄형 수신 설명 sender는 메시지를 보내고, 할일을 계속한다. receiver는 ‘유효한 메시지를 받거나’,
‘Null 메시지라도 받아서’ 작업을 계속한다.
-
- 랑데뷰 현상
- 송신 프로세스가 메시지 보내고 수신 프로세스가 수신되고 처리될 때까지 대기하며 두 프로세스는 특정 시점(메시지 교환 지점)에서 서로를 기다리게 되는 시점을 말한다.
- 즉, send와 receive가 동시에 Block인 상태를 뜻한다.
- 한 번에 하나씩만 주고받을 수 있으므로 성능이 좋지 않다.
- 송신 프로세스가 메시지 보내고 수신 프로세스가 수신되고 처리될 때까지 대기하며 두 프로세스는 특정 시점(메시지 교환 지점)에서 서로를 기다리게 되는 시점을 말한다.
② 통신방향에 따른 분류
- 프로세스는 데이터를 주거나 받으면 통신을 한다.
-
동기화에 따른 분류 외에 데이터가 전송되는 방향에 따라 통신을 분류할 수 있다.
분류 양방향 단양방향 단방향 방식 데이터를 동시에 양쪽 방향으로 전송할 수 있는 구조로, 일반적인 통신은 모두 양방향 통신이다. 데이터를 양쪽 방향으로 전송할 수 있지만 동시 전송은 불가능하고 특정 시점에 한쪽 방향으로만 전송할 수 있는 구조이다. 모스 신호처럼 한쪽 방향으로만 데이터를 전송할 수 있는 구조이다. 예시 소켓 무전기 파이프, 전역 변수
▶ Message passing 종류
▷ Mach
- Mach 시스템
- 커널을 통해 Massage Passing을 한다. (커널을 통과)
- Mach는 Microkernels이다! ( 더 알고싶으면 Microkernels을 클릭! )
msg_send()
: 메시지를 메일박스에 보낸다.msg_receive()
: 메일박스에서 메시지를 수신한다.mag_rpc()
: 원격 프로시저 콜port_allocate()
: 새로운 메일박스 생성, 메일박스 메세지 큐를 위한 공간 할당
▷ Socket
- Socket(소켓)
- 여러 컴퓨터가 네트워크로 연결되어 있을 때 프로세스는 소켓을 이용하여 데이터를 주고받는다. 이처럼 소켓을 이용하는 프로세스 간 통신을 네트워킹이라고 한다.
- 네트워킹 상에서의 통신은 원격 프로시저 호출이나 소켓을 이용한다.
- 소켓은 동기화를 지원하므로 바쁜 대기를 하지 않아도 되고, 하나만 사용해도 양방향 통신이 가능하다.
- 소켓의 구성요소
- ip
- port
- 1024번 이하 포트는 주로 사용되는 메인 포트이다
- 개발, 테스트 용으로 포트를 사용할 때는 1024번 이상 포트를 사용해야 한다.
- 돼지코가 소켓임!
- Loopback 주소
- 자기 자신으로 돌아오는 주소이다.
- localhost
- 127.0.0.1
▷ RPC
-
원격 프로시저 콜(RPC, Remote Procedure Call)
- 내가 아닌 원격지(remote)에 존재하는 프로시저를 호출하는 방법이다.
- 프로시저 : 특정한 로직을 처리하기만 하고 결과 값을 반환하지 않는 서브 프로그램
- 즉, 다른 컴퓨터에 있는 함수를 호출한다.
- 일반적으로 원격 프로시저 콜은 소켓을 이용해 구현한다.
- 클라이언트와 서버가 있다. (클라우드 컴퓨텅이랑 똑같은 구조)
- 클라이언트에서 ig라는 값을 k에 넣는 연산을 하고싶으면!! (간단해 보여도 무지하게 복잡한 연산임)
- 클라우드에 있는 서버한테 연산을 대신 시킨다.
▷ Pipe
-
Pipe(파이프)
- 두 프로세스가 통신할 수 있게 하는 통로이다.
- 하나의 프로세스가 파이프를 이용중 이라면 다른 프로세스는 접근할 수 없다.
-
일반 파이프(Ordinary Pipes) (= 익명 파이프)
- 부모, 자식 프로세스 간의 통신시 사용하는 파이프이다. (생산자-소비자 형태)
- 단방향 통신만 가능하며 양방향 통신을 위해 두 개의 파이프를 사용해야 한다.
- 값을 쓰거나 읽어야 하기 때문에 2bit가 필요하다.
- 프로세스들이 통신을 마치고 종료하면 일반 파이프는 없어진다.
-
지명 파이프(Named Pipe) (= FIFO)
- 양방향 통신이 가능해 부모-자식 관계여야만 사용할 수 있다는 제한이 존재하지 않는다.
- 관련 없는 프로세스 간에도 통신을 가능하게 한다.
- 프로세스 통신이 종료되어도 보통 파일처럼 존재한다.
▶ 임계 구역
- 임계 구역
- 공유 자원 접근 순서에 따라 실행 결과가 달라지는 프로그램의 영역을 말한다.
- 임계구역에서는 프로세스가 동시에 작업하면 안 된다.
- ex. 주방에서 가스레인지는 공유가 가능한 자원이지만, 믹서기를 사용할 때는 순서를 지켜야 하므로 공유가 불가능한 자원이므로 임계구역이다.
▷ 생산자-소비자 문제
임계구역과 관련된 전통적인 문제로 생산자 - 소비자 문제가 있다.
- 협력 프로세스에서는 생산자와 소비자 역할을 하는 두 개의 프로세스가 존재한다.
- 생산자 프로세스가 정보나 데이터를 생성, 이 정보는 소비자 프로세스에 의해 소비된다.
- 생산자는 계속 수를 증가시켜가며 버퍼에 넣고 :
input(buf)
- 소비자는 생산자를 쫓아가며 물건을 소비한다 :
output(buf)
- 또한 버퍼가 비었는지 가득 찼는지 확인하기 위해
sum
이라는 전역 변수를 사용한다. (sum에는 현재 버퍼에 있는 상품의 총 수 저장)
- 생산자와 소비자는 독립적이기 때문에 생산자 코드와 소비자 코드가 동시에 실행되면 문제가 발생한다. 생산자와 소비자가 전역 변수 sum에 접근하는 타이밍을 서로 맞추지 않기 때문이다.
생산자-소비자 문제 예시
- ① 생산자가 물건 하나를 buf 4에 저장
sum=sum+1;
하고 코드 실행을 하지 않은 상태이다. - ② 소비자가 물건 하나를 소비
sum=sum-1;
하고 코드 실행을 하지 않은 상태이다. - 이 상황에서 생산자와 코드를 거의 동시에 실행했을 때, 생산자와 소비자는 독립적이기 때문에 상대방이 sum을 바꾸려는 것을 모른 채 현재 상태인 sum=3을 읽어서 작업을 한다.
- 따라서 ①, ② 순서로 실행됐을 때 sum=2가 되고, ②, ① 순서로 실행됐을 때 sum=4가 되는 것이다.
▶ 버퍼⭐
-
버퍼의 종류
종류 무한버퍼 유한 버퍼 특징 ① 제한이 없는 크기를 갖는 버퍼
② 크기에 대한 제약이 없으므로 생산자가 계속해서 데이터를 생성하고, 소비자가 필요할 때마다 데이터를 가져갈 수 있다.① 고정된 크기를 갖는 버퍼
② 사전에 정해진 크기만큼만 데이터를 저장, 만약 버퍼가 가득 차거나 비어있으면, 생산자나 소비자 프로세스는 대기 상태에 들어간다.장점 손실이 안된다 딜레이를 줄일 수 있다. 단점 쌓인 데이터가 많을 시 속도가 느려진다. 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
개 만큼의 버퍼를 사용할 수 있다. - 그 이유는 마지막 공간까지 데이터가 채워진다면, full 상태와 empty 상태를 구분할 수 없기 때문이다.
(in+1)%4 == 0
이므로 buffer가 full 상태가 되어 더 이상 입력을 받지 않음
- 환영 버퍼 예시로 정말 그런지 확인해보자!
(in+1)%4==0
이므로in ==out
인 상태이다, 따라서 full과 empty 구분이 안된다.
📎참조
- 성결대학교 강영명 교수님 운영체제 (2023)
- 쉽게 배우는 운영체제 - 한빛 아카데미
- https://taegyunwoo.github.io/os/OS_Process2
- https://y-oni.tistory.com/77
- https://velog.io/@yanghl98/OS%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-IPC%EB%9E%80
- https://pwnkidh8n.tistory.com/160
댓글남기기