프로세스 간 통신은 하나의 컴퓨터에 존재하는 여러 프로세스끼리의 통신과 네트워크로 연결된 다른 컴퓨터에 있는 프로세스와의 통신이 있다. 프로세스 간 통신 종류는 다음과 같이 크게 세 종류로 나뉜다.
1. 프로세스 내부 데이터 통신
하나의 프로세스 내에 2개 이상의 스레드가 존재하는 경우의 통신이다. 프로세스 내부의 스레드는 전역 변수나 파일을 이용해 데이터를 주고받는다.
2. 프로세스 간 데이터 통신
같은 컴퓨터에 있는 여러 프로세스끼리 통신하는 경우다. 공용 파일 또는 OS가 제공하는 파이프를 사용해 통신한다.
3. 네트워크를 이용한 데이터 통신
여러 컴퓨터가 네트워크로 연결되 있을 때 프로세스는 소켓을 이용해 데이터를 주고받는다. 소켓을 이용하는 프로세스 간 통신을 네트워킹이라 한다. 다른 컴퓨터에 있는 함수를 호출해 통신하는 원격 프로시저 호출(Remote Procedure Call - RPC)도 여기에 해당한다.
프로세스 간 통신 분류
프로세스 간 통신은 동시에 실행되는 프로세스끼리 데이터를 주고받는 작업을 의미한다. 이 통신은 데이터가 전송되는 방향에 따라 다음과 같이 나뉜다.
1. 양방향 통신
데이터를 동시에 양방향으로 전송하는 구조. 일반적인 통신은 모두 양방향 통신이다(ex - 소켓 통신).
2. 반양방향 통신
데이터를 양쪽 방향으로 전송할 수 있지만 동시 전송은 불가능한 통신이다. 특정 시점에 한 방향만 데이터를 전송할 수 있다(ex - 무전기).
3. 단방향 통신
모스 신호처럼 한쪽 방향으로만 데이터를 전송할 수 있는 구조다. 프로세스 간 통신에서는 전역 변수와 파이프, 파일이 단방향 통신에 해당한다. 전역변수를 1개만 사용했을 때 양쪽에서 데이터를 보내면 둘 중 하나는 지워진다. 따라서 전역 변수로 양방향 통신을 하려면 두 개의 변수가 필요하다.
통신 구현 방식에 따른 분류
전역 변수를 사용하는 방식에서 데이터를 수신하는 쪽이 데이터가 왔는지를 알기 위해 수시로 전역 변수 값을 점검한다. 이렇게 상태 변화를 알기 위해 무한히 체크하며 기다리는 것을 바쁜 대기(busy waiting)라고 한다. Busy waiting은 리소스 낭비가 심하다. 이 문제를 해결하기 위해 데이터가 도착하면 알려주는 동기화(synchronization)를 사용한다.
프로세스간 통신은 동기화 기능 유무에 따라 다음과 같이 나뉜다.
- 대기가 있는 통신(blocking communication/synchronouse communication)
동기화를 지원하는 방식이다. 데이터 수신 측은 데이터 수신 시까지 대기 상태에 머물러있는다(ex - 파이프, 소켓).
- 대기가 없는 통신(non-blocking communication/asynchronous communication)
동기화를 지원하는 않는 통신 방식이다. 데이터 수신측은 busy waiting을 사용해 데이터 도착 여부를 확인한다(ex - 전역 변수, 파일).
프로세스 간 통신 종류
전역 변수를 이용한 통신
공동으로 관리하는 메모리를 사용해 데이터를 주고받는다. 데이터 송신 측은 전역 변수나 파일에 값을 쓰고, 수신 측은 전역변수 값을 읽는다. 전역 변수를 이용한 통신은 부모-자식 프로세스와 같이 직접적으로 관련 있는 프로세스 간에 사용한다. 아래 그림은 두 프로세스가 양방향 통신을 위해 전역 변수 R, L을 정의해 사용하는 그림이다. 두 프로세스 모두 전역 변수에 읽기/쓰기를 한다.
위 과정에는 문제가 있다. 하나의 프로세스가 전역 변수에 데이터를 언제 쓰는지 알기 위해 나머지 프로세스는 전역변수를 계속 활용해야 한다.
파일을 이용한 통신
파일 입출력 코드는 크게 읽기(open), 쓰기(write)/읽기(read), 닫기(close) 세가지로 나뉜다. 프로세스가 읽기/쓰기를 위해 입출력 관리 프로세스에게 의뢰하므로 파일 입출력도 통신이다.
파일 열기
open()에서는 사용하고자 하는 파일 존재 유무와 파일 접근 권한(읽기/쓰기 권한)이 있는지 체크한다. 유효하다면 file descriptor를 반환한다. 파일을 연 뒤에는 file descriptor만을 통해 파일에 접근 가능하다. 작업을 끝나면 file descriptor를 반환해야 한다.
읽기 또는 쓰기
file descriptor는 파일에 대한 접근 권한을 가지고 있다. 따라서 파일 읽기/쓰기를 위해 file descriptor를 사용한다.
파일 닫기
close 함수를 이용해 파일을 닫는다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main() {
int fileDescriptor;
char buffer[5];
fileDescriptor = open("com.txt", O_RDWR);
// 하드디스크로 쓰기
write(fileDescriptor, "Test", 5);
// 하드디스크로 읽기, buffer로 5바이트 읽기
read(fileDescriptor, buffer, 5);
close(fileDescriptor);
exit(0:
}
|
cs |
파일을 이용한 통신은 부모-자식 프로세스 간 통신에서 많이 사용된다. OS가 프로세스 동기화를 제공하지 않는다. 따라서 자체적인 동기화를 위해 wait() 함수를 이용해 자식 프로세스 작업이 끝날 때까지 기다렸다 작업을 시작한다.
파이프를 이용한 통신
OS가 제공하는 동기화 통신 방식이다. 파일 입출력과 마찬가지로 open()을 통해 file descriptor를 얻고 close() 함수로 작업을 끝낸다. 파이프를 이용한 통신은 단방향이므로 양방향 통신을 위해선 2개의 파이프를 사용해야 한다.
파이프 종류는 anonymouse pipe와 named pipe로 나뉜다
anonymouse pipe
부모와 자식 프로세스 또는 같은 부모를 가진 프로세스와 같이 서로 관련있는 프로세스 간 통신에 사용된다.
named pipe
FIFO라 불리는 특수 파일을 이용해 서로 관련 없는 프로세스 간 통신에 사용된다.
소켓을 이용한 통신
여러 컴퓨터에 있는 프로세스 간 통신을 네트워킹이라 한다. 네트워킹 상황에서의 통신은 원격 프로시저 호출이나 소켓을 이용한다. 원격 프로시저 호출은 다른 컴퓨터에 있는 함수를 호출하는 것이다. 일반적으로 원격 프로시저 호출은 소켓을 이용해 구현한다.
소켓은 프로세스 동기화를 지원하기 때문에 데이터 수신측이 busy waiting을 하지 않아도 된다. 소켓은 양방향 통신을 위해 한 개만 사용해도 된다.
정리
동기화를 지원하는프로세스 간 통신에는 open()과 close() 함수가 사용된다. 이런 구조를 open-read/write-close 구조라 한다. 프로세스 간 통신을 요약하면 다음과 같다.
종류 | 운영체제 동기화 지원 | open()/close() 사용 |
전역 변수 | X(busy waiting) | X |
파일 | X(wait() function) | O |
파이프 | O | O |
소켓 | O | O |
출처 - 쉽게 배우는 운영체제