1. IPC
1.1. IPC의 정의와 필요성
IPC(프로세스 간 통신, Inter-Process Communication)는 독립적으로 실행되는 하나 이상의 프로세스 간에 데이터를 교환하거나, 작업을 동기화하고, 이벤트를 통지할 수 있도록 지원하는 메커니즘의 총칭입니다.
현대의 운영체제는 각 프로세스가 자신만의 독립적인 메모리 주소 공간을 갖도록 설계하여 안정성을 확보합니다. 즉, 하나의 프로세스가 다른 프로세스의 메모리 영역을 직접 침범할 수 없습니다. 이러한 메모리 격리(Memory Isolation) 원칙은 프로세스를 보호하는 핵심적인 역할을 하지만, 필연적으로 프로세스들이 협력해야 하는 상황에서 통신 문제를 야기합니다.
IPC는 바로 이 격리된 프로세스들 사이에 운영체제가 제공하는 '통로' 또는 '공유 공간'을 만들어 줌으로써, 프로세스들이 안전하게 협력할 수 있도록 합니다.
1.2. IPC의 분류
IPC 메커니즘은 크게 두 가지 목적으로 분류할 수 있습니다.
- 통신 (Communication): 프로세스 간에 데이터를 주고받는 것이 주 목적인 경우입니다.
- 예: 파이프, 메시지 큐, 소켓, 공유 메모리
- 동기화 (Synchronization): 여러 프로세스가 공유 자원에 동시에 접근하는 것을 막거나(상호 배제), 특정 작업의 실행 순서를 제어하는 것이 주 목적인 경우입니다.
- 예: 세마포어, 뮤텍스, 조건 변수
이 문서에서는 주요 IPC 메커니즘을 중심으로 각각의 이론, 구조, 장단점을 상세히 다룹니다.
2. 주요 IPC 메커니즘
2.1. 파이프 (Pipe)
파이프는 두 프로세스 간에 단방향으로 연속적인 데이터 스트림(Stream)을 전송하는 가장 고전적인 IPC 방식입니다. 커널에 의해 관리되는 FIFO(First-In, First-Out) 버퍼를 사용합니다.
2.1.1. 익명 파이프 (Anonymous Pipe)
부모-자식 프로세스처럼 연관된 프로세스 간 통신에만 사용되는 단방향 파이프입니다.
- 이론:
pipe()시스템 콜을 호출하면, 커널은 메모리 내에 버퍼 공간을 할당하고, 이 버퍼에 접근할 수 있는 두 개의 파일 디스크립터(File Descriptor)를 반환합니다. 하나는 쓰기 전용(Write End), 다른 하나는 읽기 전용(Read End)입니다.fork()를 통해 부모 프로세스의 파일 디스크립터가 자식에게 복사되면, 부모는 쓰기용 FD를, 자식은 읽기용 FD를 남기고 나머지를 닫아 단방향 통신 채널을 형성합니다. - 내부 구조 및 구현 방식:
- 커널 내부에
struct file과 연결된 임시 VFS(Virtual File System) inode가 생성됩니다. 이 inode는 특정 파일 시스템 경로와 연결되지 않고 메모리 상에만 존재합니다. - inode는 데이터 저장을 위한 물리 메모리 페이지(보통 4KB의 배수)를 가리킵니다. 이 공간이 파이프의 버퍼 역할을 합니다.
- 데이터는 이 버퍼에 순환 큐(Circular Queue) 형태로 저장됩니다.
- 커널 내부에
- 동작 계층 구조:
- [User Space] 프로세스 A가
pipe(fd)시스템 콜을 호출합니다. - [Kernel Space] 커널은
fd[0](읽기)와fd[1](쓰기) 파일 디스크립터를 생성하고, 내부적으로 이들을 연결하는 메모리 버퍼를 할당합니다. - [User Space] 프로세스 A가
fork()를 호출하여 프로세스 B(자식)를 생성합니다. B는 A의 파일 디스크립터 테이블을 상속받습니다. - [User Space] A는 쓰기 채널만 남기기 위해
close(fd[0])을, B는 읽기 채널만 남기기 위해close(fd[1])을 호출합니다. - [User Space] A가
write(fd[1], data, size)를 호출하면, 데이터는 User Space에서 Kernel Space의 파이프 버퍼로 복사됩니다. - [Kernel Space] 커널은 버퍼에 데이터가 채워졌음을 기록합니다. 만약 버퍼가 꽉 차면,
write호출은 블록(block)됩니다. - [User Space] B가
read(fd[0], buffer, size)를 호출하면, Kernel Space의 파이프 버퍼에서 User Space의buffer로 데이터가 복사됩니다. - [Kernel Space] 커널은 버퍼에서 데이터가 빠져나갔음을 기록합니다. 만약 버퍼가 비어있으면,
read호출은 데이터가 들어올 때까지 블록됩니다.
- [User Space] 프로세스 A가
- 장단점 및 사용 사례:
- 장점: 구현이 매우 간단하고 직관적입니다. 셸에서
|연산자가 익명 파이프의 대표적인 사용 예입니다. - 단점: 통신할 프로세스가 명확한 부모-자식 관계여야만 합니다. 단방향 통신만 지원하므로 양방향 통신을 위해서는 2개의 파이프가 필요합니다.
- 사용 사례: 셸에서 명령어의 표준 출력을 다른 명령어의 표준 입력으로 연결할 때 (
ls -l | grep .md)
- 장점: 구현이 매우 간단하고 직관적입니다. 셸에서
2.1.2. 명명된 파이프 (Named Pipe / FIFO)
파일 시스템에 파일 형태로 존재하는 파이프로, 연관 없는 프로세스 간 통신이 가능합니다.
- 이론:
mkfifo()또는mknod()시스템 콜을 통해 파일 시스템에 특수 파일(FIFO)을 생성합니다. 이후, 어떤 프로세스든 이 파일 경로를 알면open()을 통해 파이프에 접근하여 읽거나 쓸 수 있습니다. 익명 파이프와 달리 파일 시스템에 이름이 존재하므로 '명명된 파이프'라고 불립니다. - 내부 구조 및 구현 방식:
- 익명 파이프와 거의 동일한 커널 내부 구조(메모리 버퍼)를 사용합니다.
- 가장 큰 차이점은 VFS inode가 파일 시스템의 특정 경로(이름)와 영구적으로 연결된다는 점입니다. 프로세스가 종료되어도 FIFO 파일은 사라지지 않습니다.
- 동작 계층 구조:
- [User Space] 프로세스 A가
mkfifo("/tmp/myfifo", 0666)를 호출하여 FIFO 파일을 생성합니다. - [User Space] 프로세스 B가
open("/tmp/myfifo", O_WRONLY)를, 프로세스 C가open("/tmp/myfifo", O_RDONLY)를 호출합니다. - [Kernel Space] 커널은
open호출을 받아 해당 VFS inode와 연결된 파이프 버퍼에 접근할 준비를 합니다. (읽기/쓰기 프로세스가 모두open할 때까지open호출이 블록될 수 있습니다.) - 이후 데이터 송수신 과정은 익명 파이프와 동일하게
write,read시스템 콜과 커널 버퍼를 통해 이루어집니다.
- [User Space] 프로세스 A가
- 장단점 및 사용 사례:
- 장점: 전혀 관련 없는 프로세스 간에도 통신이 가능합니다.
- 단점: 익명 파이프와 마찬가지로 데이터 스트림 기반이며, 속도는 공유 메모리보다 느립니다.
- 사용 사례: 클라이언트-서버 모델에서 단일 서버 프로세스가 여러 클라이언트 프로세스로부터 간단한 요청을 순차적으로 받을 때.
2.2. 메시지 큐 (Message Queue)
메시지 단위의 데이터를 커널이 관리하는 큐에 넣고 빼는 방식의 IPC입니다. 파이프와 달리 구조화된 데이터(메시지)를 비동기적으로 전송할 수 있습니다.
- 이론:
msgget()으로 고유 ID를 가진 메시지 큐를 생성하거나 얻고,msgsnd()로 메시지를 큐에 보내고,msgrcv()로 큐에서 메시지를 받습니다. 각 메시지는 '타입(type)' 필드를 가질 수 있어, 수신 측에서 원하는 타입의 메시지만 선별적으로 읽을 수 있습니다. 메시지는 커널 공간에 저장되므로, 송신 프로세스가 종료되어도 큐에 남아있습니다. (System V와 POSIX 두 가지 표준이 존재) - 내부 구조 및 구현 방식:
- 커널은 메시지 큐 식별자(ID) 테이블을 유지합니다.
- 각 메시지 큐는
msqid_ds(System V 기준)와 같은 관리 구조체를 가집니다. 이 구조체는 큐에 대한 정보(권한, 메시지 수 등)와 실제 메시지들을 가리키는 포인터(연결 리스트 형태)를 포함합니다. - 메시지는
msgbuf구조체 형태로 커널 메모리에 복사되어 저장됩니다.
- 동작 계층 구조:
- [User Space] 프로세스 A가
msgget(key, flags)를 호출하여 메시지 큐를 생성하거나 ID를 얻습니다. - [Kernel Space] 커널은
key값을 기반으로 메시지 큐 ID를 반환하거나,IPC_CREAT플래그가 있으면 새로운 큐와 관리 구조체를 생성합니다. - [User Space] A가
msgsnd(msqid, &msg, size, flags)를 호출합니다.msg데이터가 User Space에서 Kernel Space로 복사됩니다. - [Kernel Space] 커널은 복사된 메시지를 해당
msqid의 큐 끝에 추가합니다. 큐가 꽉 찼다면msgsnd는 블록됩니다. - [User Space] 프로세스 B가
msgrcv(msqid, &msg, size, type, flags)를 호출합니다. - [Kernel Space] 커널은
msqid큐에서type에 맞는 메시지를 찾아 User Space의msg버퍼로 복사하고, 큐에서 해당 메시지를 제거합니다. 조건에 맞는 메시지가 없다면msgrcv는 블록됩니다.
- [User Space] 프로세스 A가
- 장단점 및 사용 사례:
- 장점: 메시지 타입으로 우선순위나 종류를 구분할 수 있습니다. 비동기 통신에 적합합니다.
- 단점: 데이터가 User Space -> Kernel Space -> User Space로 두 번 복사되어 오버헤드가 있습니다. 큐의 전체 크기에 제한이 있습니다.
- 사용 사례: 여러 클라이언트 프로세스가 보낸 다양한 종류의 요청(예: 읽기, 쓰기, 삭제)을 서버 프로세스가 타입별로 구분하여 처리해야 할 때.
2.3. 공유 메모리 (Shared Memory)
프로세스들이 자신들의 주소 공간 일부를 다른 프로세스와 공유하는 방식입니다. 가장 빠른 IPC 메커니즘입니다.
- 이론:
shmget()(SysV) 또는shm_open()(POSIX)으로 공유 메모리 세그먼트를 생성하고,shmat()(SysV) 또는mmap()(POSIX)을 통해 이 세그먼트를 자신의 가상 주소 공간에 매핑합니다. 일단 매핑이 완료되면, 해당 메모리 영역은 일반적인 메모리처럼 포인터로 직접 읽고 쓸 수 있습니다. - 내부 구조 및 구현 방식:
- 커널은 물리 메모리의 특정 영역을 공유 메모리 세그먼트로 할당합니다.
shmid_ds(SysV 기준)와 같은 관리 구조체를 통해 이 세그먼트의 상태(크기, 권한, 연결된 프로세스 수 등)를 관리합니다.- 프로세스가
shmat()/mmap()을 호출하면, 커널은 해당 프로세스의 페이지 테이블(Page Table)을 수정하여, 특정 가상 주소 범위가 이 공유 물리 메모리 영역을 가리키도록 매핑합니다.
- 동작 계층 구조:
- [User Space] 프로세스 A가
shmget(key, size, flags)를 호출하여 공유 메모리 세그먼트 ID를 얻습니다. - [Kernel Space] 커널은
key에 해당하는 공유 메모리 세그먼트가 없으면 새로 생성하고, 있으면 기존 세그먼트의 ID를 반환합니다. - [User Space] A와 B가 각각
shmat(shmid, addr, flags)를 호출하여 세그먼트를 자신의 주소 공간에 첨부(attach)하고, 그 시작 주소를 반환받습니다. - [Kernel Space] 커널은 A와 B의 페이지 테이블을 수정하여, 각기 다른 가상 주소가 동일한 물리 메모리(공유 세그먼트)를 가리키도록 설정합니다.
- [User Space] 이후 A가 공유 메모리 주소에 데이터를 쓰면, 별도의 시스템 콜 없이 CPU와 MMU(Memory Management Unit)에 의해 해당 물리 메모리에 즉시 반영됩니다. B는 자신의 주소 공간에 매핑된 동일한 메모리를 즉시 읽을 수 있습니다. 데이터 복사가 발생하지 않습니다.
- [User Space] 프로세스 A가
- 장단점 및 사용 사례:
- 장점: IPC 중 가장 빠릅니다. 커널을 거치지 않고 직접 메모리에 접근하므로 대용량 데이터를 빠르게 교환할 수 있습니다.
- 단점: 커널이 통신을 중재하지 않으므로, 여러 프로세스가 동시에 데이터를 쓸 때 발생하는 경쟁 상태(Race Condition)를 막기 위한 동기화 메커니즘(예: 세마포어, 뮤텍스)을 프로그래머가 직접 구현해야 합니다.
- 사용 사례: 고성능 비디오/오디오 처리, 대규모 데이터베이스 시스템, 실시간 데이터 분석 등 속도가 매우 중요한 애플리케이션.
2.4. 세마포어 (Semaphore)
공유 자원에 대한 접근을 제어하거나, 프로세스 실행 순서를 관리하기 위한 동기화(Synchronization) 도구입니다.
- 이론: 세마포어는 커널이 관리하는 정수 값(카운터)입니다. 이 값은 공유 가능한 자원의 개수를 나타냅니다.
- Wait 연산 (
P연산,sem_wait): 자원을 사용하려 할 때 호출. 세마포어 값을 1 감소시킵니다. 만약 값이 0이라면, 0보다 커질 때까지 프로세스는 블록됩니다. - Signal 연산 (
V연산,sem_post): 자원 사용을 마쳤을 때 호출. 세마포어 값을 1 증가시키고, 블록된 프로세스가 있다면 하나를 깨웁니다. - 바이너리 세마포어: 값이 0 또는 1만 가지며, 뮤텍스(Mutex)처럼 상호 배제에 사용됩니다.
- 카운팅 세마포어: 0 이상의 정수 값을 가지며, 여러 개 있는 자원에 대한 접근 제어에 사용됩니다.
- Wait 연산 (
- 내부 구조 및 구현 방식:
- 커널은 세마포어 ID와 연결된
semid_ds(SysV 기준) 관리 구조체를 유지합니다. - 이 구조체는 현재 세마포어 값, 마지막 연산 프로세스 ID, 그리고 세마포어를 기다리며 블록된 프로세스들의 대기 큐(Wait Queue)에 대한 포인터를 포함합니다.
- 커널은 세마포어 ID와 연결된
- 동작 계층 구조:
- [User Space] 프로세스가
semget(key, nsems, flags)로 세마포어 집합을 생성하거나 ID를 얻습니다. - [User Space] 임계 구역(Critical Section) 진입 전, 프로세스가
semop(semid, &op_wait, 1)(Wait 연산)을 호출합니다. - [Kernel Space] 커널은 해당 세마포어 값을 확인합니다.
- 값이 1 이상이면: 값을 1 감소시키고, 즉시 User Space로 제어권을 반환합니다.
- 값이 0이면: 해당 프로세스를 '대기' 상태로 전환하고, 세마포어의 대기 큐에 넣습니다. 그리고 스케줄러는 다른 프로세스를 실행합니다.
- [User Space] 다른 프로세스가 임계 구역 작업을 마치고
semop(semid, &op_signal, 1)(Signal 연산)을 호출합니다. - [Kernel Space] 커널은 세마포어 값을 1 증가시킵니다. 그리고 대기 큐에 프로세스가 있는지 확인하고, 있다면 하나를 깨워 '준비' 상태로 만듭니다.
- [User Space] 프로세스가
- 장단점 및 사용 사례:
- 장점: 공유 메모리와 같은 비동기화 IPC에 필수적인 동기화를 제공합니다. 복잡한 동기화 시나리오를 구현할 수 있습니다.
- 단점: 잘못 사용하면 데드락(Deadlock)에 빠지기 쉽습니다. 뮤텍스에 비해 상대적으로 무겁습니다.
- 사용 사례: 공유 메모리 데이터 구조에 대한 접근 제어, 생산자-소비자 문제 해결, 특정 작업의 최대 동시 실행 개수 제한.
2.5. 소켓 (Socket)
네트워크 통신을 위해 설계되었지만, 동일한 시스템 내의 프로세스 간 통신에도 사용될 수 있는 가장 범용적인 IPC 메커니즘입니다.
- 이론: 소켓은 통신의 양 끝점을 나타내는 '엔드포인트'입니다. 각 소켓은 IP 주소와 포트 번호(네트워크 소켓의 경우) 또는 파일 시스템 경로(유닉스 도메인 소켓의 경우)와 결합됩니다. 로컬 머신 내 통신에는 유닉스 도메인 소켓(UDS, Unix Domain Socket)이 사용되며, 이는 네트워크 스택을 거치지 않아 TCP/IP 소켓보다 훨씬 효율적입니다.
- 내부 구조 및 구현 방식:
- 소켓 역시 파일 디스크립터로 표현됩니다.
- 커널 내부에
struct sock이라는 복잡한 자료구조가 생성되어 소켓의 상태, 프로토콜 정보, 송수신 버퍼, 대기 큐 등을 관리합니다. - 유닉스 도메인 소켓은 VFS를 통해 파일 시스템 경로와 연결되지만, 실제 데이터는 커널 내 소켓 버퍼를 통해 교환됩니다. 네트워크 스택(TCP/IP 헤더 추가, 라우팅 등)을 우회합니다.
- 동작 계층 구조 (유닉스 도메인 소켓 기준):
- [User Space] 서버 프로세스가
socket(AF_UNIX, SOCK_STREAM, 0)으로 소켓을 생성합니다. - [User Space]
bind()를 호출하여 소켓을 특정 파일 경로(예:/tmp/mysocket)에 바인딩합니다. - [User Space]
listen()으로 클라이언트의 연결 요청을 기다릴 준비를 하고,accept()로 실제 연결 요청이 올 때까지 블록됩니다. - [User Space] 클라이언트 프로세스가
socket()으로 소켓을 생성하고,connect()를 호출하여 서버의 파일 경로로 연결을 요청합니다. - [Kernel Space] 커널은
connect요청을 받아 서버의 대기 큐에 넣습니다. 서버의accept()는 새로운 파일 디스크립터(연결된 소켓)를 반환하며 깨어납니다. - [User Space] 이후 서버와 클라이언트는 각각의 소켓 디스크립터를 통해
write()와read()를 호출하여 데이터를 주고받습니다. 데이터는 커널 내 소켓 버퍼를 통해 복사됩니다.
- [User Space] 서버 프로세스가
- 장단점 및 사용 사례:
- 장점: 로컬 통신과 원격(네트워크) 통신 모두 동일한 API를 사용할 수 있어 범용성이 매우 높습니다. 양방향 통신이 기본입니다.
- 단점: 다른 로컬 IPC 메커니즘(특히 공유 메모리)에 비해 설정이 복잡하고 오버헤드가 큽니다.
- 사용 사례: 대부분의 클라이언트-서버 애플리케이션, 웹 서버, 데이터베이스 서버 등. 로컬에서는 데스크톱 환경의 विभिन्न 서비스들(예: D-Bus)이 소켓 기반으로 통신하는 경우가 많습니다.
3. IPC 메커니즘 비교
| 특징 | 익명 파이프 | 명명된 파이프 (FIFO) | 메시지 큐 | 공유 메모리 | 세마포어 | 소켓 (UDS) |
|---|---|---|---|---|---|---|
| 속도 | 빠름 | 빠름 | 보통 | 매우 빠름 | (통신 목적 아님) | 보통 |
| 데이터 단위 | 스트림 (Stream) | 스트림 (Stream) | 메시지 (Message) | 바이트 블록 (Block) | 정수 카운터 | 스트림 또는 데이터그램 |
| 통신 방향 | 단방향 | 단방향 (사실상) | 양방향 | 양방향 | (동기화 도구) | 양방향 |
| 사용 범위 | 부모-자식 관계 | 동일 시스템 내 | 동일 시스템 내 | 동일 시스템 내 | 동일 시스템 내 | 동일 시스템 및 네트워크 |
| 동기화 필요 | 불필요 (자체 블로킹) | 불필요 (자체 블로킹) | 불필요 (자체 블로킹) | 필수 | 동기화 자체 | 불필요 (자체 블로킹) |
| 지속성 | 프로세스 종료 시 소멸 | 파일 유지 시 영속 | 커널 재부팅 전까지 유지 | 커널 재부팅 전까지 유지 | 커널 재부팅 전까지 유지 | 파일 유지 시 영속 |
| 주요 사용 사례 | 셸 파이프라인 | 간단한 클라이언트-서버 | 구조화된 데이터 교환 | 고성능 데이터 공유 | 상호 배제, 순서 제어 | 범용 클라이언트-서버 |
4. 요약
IPC, 프로세스 간 통신은 운영체제가 안정성을 위해 각 프로세스의 메모리 공간을 격리하기 때문에 필요합니다. 이렇게 격리된 프로세스들이 서로 데이터를 주고받거나 작업을 동기화하기 위해 커널이 제공하는 통신 메커니즘입니다.
대표적인 IPC 방식으로는 파이프, 메시지 큐, 공유 메모리, 소켓 등이 있으며, 동기화를 위해 세마포어가 함께 사용됩니다.
- 파이프는 가장 간단한 방식으로, 부모-자식 프로세스 간에
ls | grep처럼 데이터 스트림을 전달할 때 주로 사용됩니다. 구현은 쉽지만 연관된 프로세스 간에만 통신할 수 있다는 한계가 있습니다. - 메시지 큐는 파이프와 달리 메시지 단위로 데이터를 주고받을 수 있고, 메시지에 타입을 부여해 수신 측에서 원하는 종류의 데이터만 골라 받을 수 있는 장점이 있습니다. 다만 커널을 거치므로 데이터 복사 오버헤드가 있습니다.
- 공유 메모리는 가장 빠른 IPC 방식입니다. 여러 프로세스가 물리 메모리의 특정 영역을 자신의 주소 공간처럼 직접 공유하므로, 커널을 거치지 않아 데이터 복사가 발생하지 않습니다. 속도가 매우 빠르지만, 여러 프로세스가 동시에 접근할 때 데이터가 깨질 수 있어 세마포어 같은 별도의 동기화 도구를 반드시 함께 사용해야 하는 단점이 있습니다.
- 소켓은 본래 네트워크 통신을 위해 설계되었지만, 유닉스 도대체 소켓을 이용하면 한 시스템 내에서도 통신이 가능합니다. 양방향 통신을 지원하고 API가 표준화되어 있어 가장 범용성이 높지만, 다른 로컬 IPC에 비해 설정이 복잡하고 속도가 느린 편입니다.
'Computer Science' 카테고리의 다른 글
| 뮤텍스 Mutex (1) | 2025.11.25 |
|---|---|
| TCP와 UDP (0) | 2025.11.24 |
| 프로세스와 스레드 Process and Thread (0) | 2025.11.19 |
| CPU 스케줄링 CPU Scheduling (0) | 2025.11.19 |
| 캐시 메모리 Cache Memory (0) | 2025.11.19 |
