본문 바로가기

서버운영 (TA, ADMIN)/리눅스

[리눅스] IPC의 종류와 특징

아래 그림처럼 Process는 완전히 독립된 실행객체입니다. 서로 독립되어 있다는 것은 다른 프로세스의 영향을 받지 않는다는 장점이 있습니다. 그러나 독립되어 있는 만큼 별도의 설비가 없이는 서로 간에 통신이 어렵다는 문제가 있게 됩니다. 이를 위해서 커널 영역에서는 IPC라는 내부 프로세스간 통신(Inter Process Communication)을 제공하게 되고, 프로세스는 커널이 제공하는 IPC설비를 이용해서 프로세스간 통신을 할 수 있게 됩니다.




IPC의 2가지 표준(System V IPC와 POSIX IPC)

: System V IPC는 오래된 버전이고 POSIX IPC는 비교적 최근에 개발된 표준입니다. System V IPC는 오랜 역사를 가진만큼 이기종간 코드 호환성을 확실히 보장해 주지만, API가 오래되었으며, 함수명도 명확하지 않습니다. POSIX IPC는 직관적으로 API가 구성되어 있어서 상대적으로 조금 더 사용하기 쉽다고 보여집니다.



IPC 설비들


현실에서도 필요에 따라 다양한 통신 설비들이 존재하는 것처럼 IPC에도 다양한 설비들이 존재합니다. 각각의 필요에 따라서 적당한 통신 설비들이 준비되어야 하는 것과 마찬가지로 내부 프로세스간 통신에도 그 상황에 맞는 IPC 설비를 선택할 필요가 있게 됩니다.


상황에 맞는 IPC의 선택은, 특히 fork()를 이용해서 만들어진 멀티 프로세스의 프로그램에 있어서 중요합니다. 잘못된 IPC 설비의 선택은 코딩과정을 어렵게 만들거나 프로그램의 작동을 효율적이지 못하게 만들 수 있기 때문입니다.



1) PIPE (익명 PIPE)

위 그림은 PIPE의 작동원리를 보여줍니다. 파이프는 두 개의 프로세스를 연결하게 되고, 하나의 프로세스는 데이터를 쓰기만, 다른 하나는 데이터를 읽기만 할 수 있씁니다. 한쪽 방향으로만 통신이 가능한 파이프의 특징 때문에 Half-Duplex(반이중) 통신이라고 부르기도 합니다.


PIPE와 같은 반이중 통신의 경우 하나의 통신선로는 읽기나 쓰기 중 하나만 가능하므로 만약 읽기와 쓰기, 즉 송/수신을 모두 하기 원한다면 두 개의 파이프를 만들어야만 가능해집니다.


PIPE는 매우 간단하게 사용할 수 있다는 장점이 있습니다. 만약 한쪽 프로세스가 단지 읽기만 하고 다른 쪽 프로세스는 단지 쓰기만 하는, 단순한 데이터 흐름을 가진다면 고민 없이 PIPE를 사용하면 됩니다. 단점은 반이중 통신이라는 점으로 만약 프로세스가 읽기와 쓰기 통신 모두를 해야 한다면 PIPE를 두개 만들어야 하는데, 구현이 꽤나 복잡해 질 수 있습니다. 만약 전이중 통신을 고려해야될 상황이라면 PIPE는 좋은 선택이 아니라고 보여집니다.



2) Named PIPE(FIFO)

익명 파이프(PIPE)는 통신을 할 프로세스가 명확하게 알 수 있는 경우 사용합니다. 예를 들어 자식과 부모 프로세스 간 통신의 경우에 사용할 수 있으며, Named PIPE는 전혀 모르는 상태의 프로세스들 사이의 통신의 경우 사용합니다. Named PIPE는 PIPE의 단점 중 같은 PPID(같은 부모 프로세스)를 가지는 프로세스들 사이에서만 통신이 가능하지만, Named PIPE는 그 부분을 해결한, PIPE의 확장이라고 할 수 있을 것입니다. Named PIPE는 부모 프로세스와 무관하게 전혀 다른 모든 프로세스들 사이에서 통신이 가능한데, 그 이유는 프로세스 통신을 위해 이름이 있는 파일을 사용하기 때문입니다. Named PIPE의 생성은 mkfifo를 통해 이뤄지는데, mkfifo가 성공하면 명명된 파일이 생성됩니다.


단점으로는, Named PIPE도 PIPE의 또 다른 단점인 읽기/쓰기가 동시에 가능하지 않으며, read-only, write-only만 가능합니다. 하지만 통신선로가 파일로 존재하므로 하나를 읽기 전용으로 열고 다른 하나를 쓰기전용으로 열어서 이러한 read/write 문제를 해결할 수 있습니다. 호스트 영역의 서버/클라이언트 간에 전이중 통신을 위해서는 결국 PIPE와 같이 두개의 FIFO파일이 필요하게 됩니다.



3) Message Queue

Queue(큐)는 선입선출의 자료구조를 가지는 통신설비로 커널에서 관리합니다. 입출력 방식으로 보자면 위의 Named PIPE와 동일하다고 볼 수 있을 것입니다. Named PIPE와 다른 점이라면 Named PIPE가 데이터의 흐름이라면 메시지 큐는 메모리 공간이라는 점입니다. 파이프가 아닌, 어디에서나 물건을 꺼낼 수 있는 컨테이너 벨트라고 보면 될 것입니다.


메시지 큐의 장점은 컨테이너 벨트가 가지는 장점을 그대로 가지게 됩니다. 컨테이너 벨트에 올라올 물건에 라벨을 붙이면 동시에 다양한 물건을 다룰 수 있는 것과 같이, 메시지 큐에 쓸 데이터에 번호를 붙임으로써 여러 개의 프로세스가 동시에 데이터를 쉽게 다룰 수 있습니다. 



4) Shared Memory(공유 메모리)

데이터를 공유하는 방법에는 크게 두 가지가 있습니다. 하나는 통신을 이용해서 데이터를 주고 받는 것이고 다른 하나는 데이터를 아예 공유, 즉 함께 사용하는 것입니다. PIPE, Named PIPE, Message Queue가 통신을 이용한 설비라면, Shared Memory는 공유메모리가 데이터 자체를 공유하도록 지원하는 설비입니다.


프로세스는 자신만의 메모리 영역을 가지고 있습니다. 이 메모리 영역은 다른 프로세스가 접근해서 함부로 데이터를 읽거나 쓰지 못하도록 커널에 의해서 보호가 되는데, 만약 다른 프로세스의 메모리 영역을 침범하려고 하면 커널은 침범프로세스에 SIGEGV(경고 시그널 - 할당된 메모리의 범위를 벗어나는 곳에서 읽거나, 쓰기를 시도할 때 발생)을 보내게 됩니다.


다수의 프로세스가 동시에 작동하는 Linux 운영체제의 특성상 프로세스의 메모리 영역은 반드시 보호되어져야 합니다. 그렇지만 메모리 영역에 있는 데이터를 다른 프로세스도 사용할 수 있도록 해야할 경우도 필요할 것입니다. PIPE 등을 이용해서 데이터 통신을 이용하여 데이터를 전달하는 방법도 있겠지만, Thread에서 처럼 메모리 영역을 공유한다면 더 편하게 데이터를 함께 사용할 수 있을 것입니다.


Shared Memory(공유 메모리)는 프로세스간 메모리 영역을 공유해서 사용할 수 있도록 허용합니다. 프로세스가 공유 메모리 할당을 커널에 요청하면 커널은 해당 프로세스에 메모리 공간을 할당해줍니다. 이후 어떤 프로세스건 해당 메모리영역에 접근할 수 있습니다.


공유 메모리는 중개자가 없이 곧바로 메모리에 접근할 수 있기 때문에 다른 모든 IPC들 중에서 가장 빠르게 작동할 수 있습니다.



5) Memory Map

Memory Map도 Shared Memory(공유메모리)공간과 마찬가지로 메모리를 공유한다는 측면에 있어서는 서로 비슷한 측면이 있습니다. 차이점은 Memory Map의 경우 열린파일을 메모리에 맵핑시켜서 공유한다는 점일 것입니다. 파일은 시스템의 전역적인(모두 공유할 수 있는) 자원이므로 서로 다른 프로세스들끼리 데이터를 공유하는데 문제가 없을 것임을 예상할 수 있습니다.



6) Socket

Socket은 프로세스와 시스템의 기초적인 부분이며, 프로세스들 사이의 통신을 가능하게 합니다. <sys/socket.h>라는 헤더를 이용하여 사용할 수 있으며, 같은 도메인에서의 경우에서 연결될 수 있습니다. 소켓을 사용하기 위해서는 생성해주고, 이름을 지정해주어야 합니다. 또한 domain과 type, Protocol을 지정해 주어야 합니다. 서버단에서는 bind, listen, accept를 해주어 소켓 연결을 위한 준비를 해주어야 하고, 클라이언트 단에서는 connect를 통해 서버에 요청하며, 연결이 수립된 이후에는 Socket을 send함으로써 데이터를 주고받게 됩니다. 연결이 끝난 후에는 반드시 Socket을 close()해주어야 하며, Socket과 관련해서는 이후 네트워크 부분에서 자세히 다루도록 하겠습니다.



7) Semaphore

Semaphore는 Named PIPE, PIPE, Message Queue와 같은 다른 IPC설비들이 대부분 프로세스간 메시지 전송을 목적으로 하는데 반해, Semaphore는 프로세스 간 데이터를 동기화하고 보호하는데 그 목적을 두게 됩니다. 프로세스간 메시지 전송을 하거나, 혹은 Shared Memory를 통해서 특정 데이터를 공유하게 될 경우 발생하는 문제가 공유된 자원에 여러개의 프로세스가 동시에 접근하면 안되며, 단지 한번에 하나의 프로세스만 접근 가능하도록 만들어줘야 할 것이며, 이때 사용되는 것이 Semaphore입니다. 자세한 부분은 이전에 포스팅했던 Semaphore와 Mutex 부분에서 다루고 있으므로 생략하도록 하겠습니다.