IPC: Interprocess Communication
- update date : 2020.06.03
- 본 포스팅은 고려대학교 컴퓨터학과 유혁 교수님의 2020년 1학기 Operating system을 수강하고 이를 바탕으로 작성했습니다. 수업 내용 복습을 목적으로 작성하였기 때문에 내용 중 부족한 점이 있을 수 있습니다.
Introduction
IPC, Interprocess Communication 란 말 그대로 프로세스 간에 의사소통을 하는 방법, 메커니즘에 관한 것이라고 할 수 있다. 여기서 말하는 의사소통이란 특정 데이터를 전달해주는 것이 될 수도 있고, 특정 작업을 명령하는 것이 될 수도 있다. 이러한 IPC는 컴퓨터의 성능을 높여주고, 실행 중인 프로세스를 통제할 수 있는 수단이 되기 때문에 중요하다. 보다 구체적으로 Operatint System Concepts에서는 IPC의 주요 목적으로 다음 세 가지를 꼽는다.
- information sharing : 여러 프로세스가 필요로하는 정보를 공유하기 위해
- computation speedup : 작업의 크기가 매우 큰 경우 이를 여러 개의 subtask로 나누어 수행할 수 있도록 하여 속도를 높이기 위해
- Modularity : 어떤 시스템을 여러 개의 모듈로 나누어 관리하기 위헤
2 ways of communication
IPC를 구현하는 방법은 크게 shared memory와 message passing이라는 두 가지 방식으로 나누어진다. 두 가지 방식 모두 대부분의 운영체제에 모두 구현되어 있으며 필요에 따라 달리 사용된다고 한다.
Shared Memory
Shared memory는 프로세스의 일정한 영역을 공유 영역으로 설정하여 다른 프로세스가 접근해 읽고 쓸 수 있도록 하는 방법이다. Shared memory 영역을 설정할 때에는 운영체제가 개입을 하지만 해당 영역에 다른 프로세스가 접근할 때에는 운영체제의 개입이 전혀 없는 방식이다. 따라서 어떤 프로세스가 쓰고 있는 공유 메모리 영역에 다른 프로세스가 접근하여 쓰는 것도 가능하며 이러한 점에서 동기화 문제가 있다.
Shared memory는 프로세스의 특징 중 하나인 protection domain에 대한 제한을 특정 영역, 특정 프로세스에 대해 해제하는 것이다. 따라서 해킹에 대한 위험에 노출되어 있고, 안전하게 사용하기 위해서는 공유 메모리 영역에 접근하기 전 access control이 필요하다.
Message Passing
주소 공간을 공유하지 않으면서 프로세스 간 데이터를 공유하는 방식으로, 이름에서도 알 수 있듯이 프로세스 간에 메시지를 주고 받는 형식으로 이루어진다. 구체적으로는 수신자가 보내고자 하는 데이터를 커널에 복사해두고 이를 수신자가 커널에서 읽어들이는 방식으로 동작한다. 따라서 한 번의 데이터 공유가 이뤄질 때마다 context switching이 발생하며, 이러한 점에서 Shared Memory에 비해 속도와 크기 면에서 제약이 있다.
Examples of IPC
POSIX shared memory
- Shared memory
shared memory의 대표적인 방법 중 하나가 POSIX shared memory이다. POSIX shared memory는 기본적으로 데이터를 제공하는 Provider와 데이터를 받는 Consumer 둘 간의 소통을 전제로 한다. Provider가 shm_open()
시스템 콜을 사용하여 자신의 프로세스 내부에 공유 메모리 영역을 설정하고 공유 영역에 대한 정보를 memory-mapped file로 만들어두면, Consumer가 memory-mapped file를 통해 해당 공유 영역에 접근하는 방식으로 동작한다.
위에서 언급한 것처럼 shared memory 방식은 kernel을 통하지 않으므로 빠르지만 복수의 프로세스가 동시에 memory를 쓰지 않도록 동기화가 요구된다.
Pipe
- One to One Message Passing
다른 프로세스로 직접 데이터를 전송하는 방식을 말한다. Pipe라는 이름에 맞게 한 쪽 방향으로만 데이터가 이동하며(half duplex) 따라서 양방향 소통을 위해서는 두 개의 파이프가 필요하다. 매우 기본적인 구조로 동작하며 초기 UNIX부터 사용되어온 방식이다.
Message Queue
- Many to Many Message Passing
Message Queue를 만들어두고 Sender가 보내는 모든 데이터를 하나의 Queue에 넣어두고 차례대로 데이터의 Receiver에게 전달해주는 방법이다. 요즘에는 많이 사용하지 않는다고 한다.
Socket
- Message Passing
Socket은 endpoint for communication으로 정의되며 IP 주소와 Port 번호를 통해 프로세스 간 통신하는 방법이다. 기본적으로 Server-Client 구조를 가지고 있으며 Host에 부여된 IP와 Host의 운영체제가 제공하는 Port를 통해 통신하고자 하는 프로세스의 Socket을 찾아가게 된다. 따라서 Socket을 사용하면 외부 기기의 프로세스와도 데이터를 주고 받는 것이 가능한데 이를 두고 machine/location independent
라고 표현한다. local 환경에서 프로세스간 소통에서는 IP 주소가 필요 없으므로 port 번호 만으로도 가능하다.
보다 구체적으로 Provider의 Port에서 나와 Consumer의 어떤 Port로 들어갈 것인지는 IP layer에서 담당하고 Comsumer의 Port에서 어떤 Socket으로 전달될 것인지는 Transfort layer에서 담당한다고 한다. Socket은 개별 프로세스마다 unique하게 존재하기 때문에 Socket이 특정되면 Consumer 프로세스도 특정되는 것이다. 이러한 점에서 Socket을 endpoint for communication 이라고 한다.
Signal
- One to Many
Signal은 어떤 프로세스가 다른 프로세스에게 특정한 이벤트가 발생했다는 것을 알리는 방법으로, 여러 signal에 대해 어떻게 대응할 것인지 미리 약속해둔다면 signal을 통해 다른 프로세스가 특정 동작을 하도록 하는 것이 가능하다. 빨간색 신호등을 보면 차로 위의 자동차들이 모두 멈추는 것과 유사하다. Signal을 사용하는 예시는 매우 다양한데 그 중 가장 대표적인 것이 리눅스에서 특정 프로세스를 강제로 종료하는 명령어인 $ kill -9 PID
이다. 해당 명령어를 입력하면 전달받은 PID를 가진 프로세스에 SIGKILL
signal이 전달되고, 전달받은 프로세스는 스스로 종료하게 된다.
시그널은 기본적으로 비동기적이다. 즉 시그널을 보낸다고 해서 즉시 수신 프로세스가 처리하는 것이 아니라 수신 프로세스가 scheduler로부터 CPU를 넘겨 받았을 때 비로소 시그널에 대한 처리가 이뤄지게 된다.