Inter-Process Communication
1. 설명
Inter-Process Communication
컴퓨터 프로세스간 통신.
운영체제 상에서 실행 중인 프로세스 간에 정보를 주고받는 것을 Inter Process Communication(IPC)라고 한다. 프로세스는 자신에게 할당된 메모리 내의 정보만 접근할 수 있고, 이를 벗어나서 접근할 경우, C언어를 배우는 사람이라면 누구나 한 번쯤은 봤을 Segmentation Fault 등의 오류가 발생하게 된다. 이는 안전성을 위해 운영체제에서 자기 프로세스의 메모리만 접근하도록 강제하고 있다는 것이다.
따라서 한 프로그램에서 병렬성을 키우면서 공유되는 데이터를 사용하기 위해 메모리 공간을 공유하는 스레드를 이용하는 경우가 많다 (물론 스레드를 쓰는 이유는 이것 말고도 매우 다양하다). 하지만 이것은 하나의 프로그램에서만 의미가 있는 것이고, 서로 다른 프로그램(즉, 서로 다른 프로세스)의 데이터를 공유하려면 결국 다른 프로세스의 메모리를 접근할 필요가 발생한다. 따라서 이 때는 IPC라는 것을 사용하게 된다.
다른 것도 있다. 바로 LPC(Local Inter-Process Communication, 로컬 컴퓨터 프로세스간 통신)라는 게 있는데 프로세스가 다른 프로세스와 정보를 주고 받을 때 빠른 속도로 데이터를 교환하기 위해 사용한다. 다만 이건 커널에서만 쓸 수 있으며 일반 프로그램은 LPC를 사용할 수 없다.
2. 종류
2.1. 메일슬롯
메일슬롯은 단방향 메시지 전송이 가능한 메모리 상의 가상파일이다. 크기 제한이 424바이트로 꽤 작지만, 쉽게 만들 수 있고, 디스크 파일과는 다르게 휘발성이라는 점, 그리고 브로드캐스팅이 가능하다는 것이 장점이다.
2.2. 파이프
파이프(Pipe)는 [1] 많이 쓰이는 IPC 구현 중 하나이다. 유닉스 호환 커널은 pipe(2)라는 시스템 콜로 파이프를 생성할 수 있다. 보통 많이 쓰이는 방식은 셸 내에서 한 프로그램의 출력 결과를 다음 프로그램으로 전달해서 데이터를 새롭게 가공시키는 것이다. 셸에서 " | " 앞뒤로 프로그램명이 나온다면 앞의 프로그램과 뒤의 프로그램 사이에 파이프가 생성되고, 앞의 결과가 뒤의 입력으로 들어간다. 이 기능은 윈도우에서는 PowerShell에서 지원하며, 전통적인 명령 프롬프트에서는 지원하지 않는다. 단방향 명명되지 않은 파이프를 생성하여 앞의 프로세스의 결과를 뒤의 프로세스로 전달하는 과정이다.
'''사용 예시'''
거의 모든 주요한 OS가 다 Pipe라고 부르는 IPC를 지원하지만, OS별로 다른 작동 방식을 가지고 있다.
2.2.1. 명명되지 않은(익명) 파이프
Anonymous Pipe
OS에서 제공하는 기본적인 파일 기반 단방향 IPC이다. 쓰기만 가능한 커넥션과 읽기만 가능한 커넥션 2개로 구성되며, 보통 상속으로 둘 중 하나의 커넥션을 전달하여 통신한다. 유닉스는 커넥션으로 파일디스크립터를 반환한다. 양방향 통신을 하고 싶으면 이런 익명 파이프를 2개 만들면 된다.
유닉스의 경우 명명된 파이프와의 가장 큰 차이점은 소멸 시점이다. 익명 파이프의 경우 프로세스가 종료되면 그 프로세스의 커넥션(핸들 혹은 파일 디스크립터)를 OS가 찾아서 다 죽여버린다.
윈도우의 익명 파이프는 '''아무 이름이나 가진''' 명명된 파이프이다. 사용자가 볼 수 없는 최상위 폴더에 파이프 전용 파일 시스템을 가진 특수한 파일을 생성하고, 커넥션으로 그 파일의 핸들의 API를 제공한다.
윈도우의 익명 파이프 API 작동 문서
2.2.2. 명명된 파이프
Named pipe
기본적으로 명명된 파이프는 익명 파이프에 이름을 붙여서 특정할 수 있게 만든다는 컨셉에서 시작한 것이다. 이름을 붙여서 특정을 하였기 때문에 파이프의 이름으로 공유할 수 있는데, 막상 해 보면 별 차이는 없다...
유닉스의 경우, 익명 파이프와의 가장 큰 차이점은, 프로세스가 죽어도 명명된 파이프는 회수하지 않는 한 남아있다. 마운트 할 수 있는 일반적인 파일 취급을 해서 그런 듯?
윈도우의 경우는 파이프의 기본이 명명된 파이프이고, 익명 파이프는 아무 이름이나 설정한 파이프라서, 모든 API는 기본적으로 명명된 파이프용 API이다.
2.3. 소켓
소켓(Socket)은 일반적으로 두 네트워크 프로그램 사이에서 데이터의 입출력의 관문, 또는 그를 위한 운영체제의 API의 이름으로써 많이 쓰인다. 이 소켓을 통해 TCP 연결이 수립되면 스트림 소켓(Stream socket), UDP를 사용하면 데이터그램 소켓(Datagram socket)이라고 불린다. 하지만 꼭 다른 네트워크로 가지 않고 소켓을 생성한 두 프로세스가 같은 컴퓨터 내의 같은 운영체제 상에서 실행되고 있다면 프로세스 간 통신이 가능하게 된다. 이에 더해, 아예 하나의 컴퓨터 상에서 IPC를 위해 유닉스 도메인 소켓(Unix Domain Socket)이란 것도 존재한다. 이것도 앞선 네트워크 소켓과 마찬가지로 같은 소켓 API로 사용 가능하다.
2.4. 시그널
시그널(Signal)은 유닉스 시스템에서 쓰이는 IPC 구현 중 하나이다. 소켓은 소켓을 생성해서 그것을 접근하기 위해 별도의 식별자(파일 디스크립터, file descriptor라고 한다)가 필요하지만, 시그널은 그러한 것들이 필요하지 않고, 바로 다른 프로세스로 정보를 보낼 수 있다 (관리자, 혹은 root 권한이 필요할 수는 있다). 하지만 소켓처럼 임의의 데이터를 보낼 수는 없고, 이름 그대로 일종의 신호만을 보낼 수 있다. 앞서 개요에서 말한 Segmentation Fault의 예가 바로 시그널과 관련이 있다. 이는 프로세스가 할당받지 않은 영역의 메모리를 접근할 때 발생한다고 했는데, 구체적으로 그러한 메모리 영역을 접근하면 운영체제 커널이 그것을 감지하고 프로세스에게 이를 알리는 시그널을 보낸다 (SIGSEGV라는 것을 보낸다). 그러면 프로세스가 이를 받고 강제로 자신을 종료시켜 버린다. 따라서 Segmentation Fault 시에는 프로세스가 죽어버리는 것이다. 이러한 시그널의 기본 동작을 바꿀 수도 있는데, 이것을 "시그널을 잡는다(catch)"라고 한다.
2.5. 공유메모리
말 그대로 메모리의 일정 영역을 공유하는 것이다. 일정 메모리 영역을 공유 가능하게 바꿔 주는 방식이랑, 하나의 파일을 열고 이를 메모리에 매핑하여(memory-mapped file), 이 영역을 공유 가능하게 하는 방식이 있다.
[1] 해석에 오해의 여지가 있는 문장이다. 유닉스 계열에서 많이 쓴다는 점은 논란의 여지가 없으나, 그렇다고 윈도우가 안 쓰는 것은 아니라서. 모든 시스템에서 다 두루두루 쓰인다.