프로세스 간 통신(IPC)

do_it·2025년 10월 16일

os

목록 보기
7/13

Inter Process Communication (IPC) - GeeksforGeeks

1. 프로세스 간 통신(IPC, Inter-Process Communication)

OS 내에서 실행 중인 독립된 프로세스들이 서로 데이터를 교환하고 정보를 공유하기 위해 사용하는 매커니즘을 말함

일반적으로 프로세스는 독립적인 메모리 공간을 가지고 있어서 다른 프로세스의 메모리에는 직접 접근할 수 없음
따라서 여러 프로세스가 협력하여 작업을 수행하거나 결과를 공유해야 할 경우 IPC 기법을 사용함

언제 IPC가 필요할까?

프로세스 간에 데이터를 교환하거나, 작업 순서를 맞추거나, 상태를 공유해야 하는 모든 상황

  1. 하나의 애플리케이션이 여러 프로세스로 구성된 경우
  • 웹 브라우저, 서버 애플리케이션, DB 엔진 …
  • e.g. 크롬 브라우저
    각 태빙 별도의 프로세스로 실행됨
    각 탭이 렌더링 프로세스(Renderer)이고, 브라우저 프로세스와 통신해야 하므로 IPC가 필요함
    (렌더링 프로세스 → 브라우저 프로세스: 새 탭 열기 요청_
  1. 클라이언트 - 서버 모델
  • 네트워크 서비스, API 서버, 백엔드 시스템
  • 클라이언트와 서버가 각각 다른 프로세스로 실행되며 요청 / 응답을 주고받음
    로컬 서버와 GUI 프로그램 간의 통신 (PostgreSQL 클라이언트 ↔ 서버)
    백엔드 서버 간 마이크로 통신 (Redis, RabbitMQ, gRPC)
  1. 프로세스 간 동기화(Synchronization) 필요할 때
  • 자원 경쟁(Resource Contention) 제어
    두 프로세스가 같은 파일, 디바이스, 데이터 구조에 접근할 경우, 동기화가 필요
    이를 위해 세마포어(semaphore), 뮤텍스(mutex), 공유 메모리 등이 활용

2. 주요 IPC 기법의 종류

1) 파이프 (Pipe)

물이 흐르는 관처럼 선입선출(FIFO, First-In, First-Out) 구조의 큐(Queue) 형태로 데이터를 전달

한쪽 끝은 데이터를 쓰는(Write) 용도이고, 다른 한쪽 끝은 데이터를 읽는(Read) 용도로 사용
한 프로세스의 출력을 다른 프로세스의 입력으로 직접 전달하는 데 사용

[주요 특징]

  • 단방향 통신 (Half-Duplex)
    가장 일반적인 파이프(익명 파이프)는 데이터 흐름이 한쪽 방향으로만 이루어짐
    양방향 통신을 하려면 읽기용 파이프와 쓰기용 파이프, 총 두 개를 만들어야 함
  • 파일 디스크립터 사용
    파이프의 양쪽 끝은 파일 디스크립터(File Descriptor, FD)를 통해 접근됨
    파이프를 생성하면 일반적으로 두 개의 FD(읽기용, 쓰기용)를 반환받음
  • 스트림 방식
    달되는 데이터는 경계가 없는 바이트 스트림 형태
    메시지 큐처럼 메시지 단위로 구분되지 않음
  • 동기화 (Blocking)
    • 읽기: 파이프에 데이터가 없으면 읽기 프로세스는 데이터가 도착할 때까지 대기(Block)
    • 쓰기: 파이프가 가득 차면 쓰기 프로세스는 파이프에 공간이 생길 때까지 대기(Block)

파이프는 크게 두 가지 종류로 나뉘는데, Anonymous Pipe & Named Pipe 두 경우 생성 방식과 수명 관리 방식에서 차이가 있음

구분익명 파이프Named Pipe (FIFO)
파일 시스템에 존재XO
접근 범위부모-자식 프로세스이름을 아는 모든 프로세스
생성 시스템콜pipe()mkfifo()open()
참조 시점pipe() 호출 시open() 호출 시 커널 버퍼 생성
수명모든 fd 닫히면 소멸열려 있는 프로세스가 모두 닫히면 버퍼 소멸, 이름은 남음
  • 위치: 커널 메모리 공간 (버퍼)
  • 실체: struct pipe_inode_info 구조체를 기반으로 한 파이프 버퍼
  • 접근방법: 파일 디스크립터 / 이름(FIFO file)을 통해 접근
  • 소멸 시점: 파이프를 참조하는 모든 파일 디스크립터가 닫히면 커널이 해제
    프로세스가 close()하거나 종료될 때 참조 카운트가 0이 되면 커널이 자동 정리함

익명 파이프 (Anonymous Pipe)

익명 → 파일처럼 접근할 수 있는 경로가 없음
오직 생성한 프로세스의 메모리 (파일 디스크립터) 안에서만 접근 가능
파일 시스템에 이름이 등록되지 않아 ls 명령어로 볼 수 없음

[ 왜 이름이 필요 없는가?]
익명 파이프는 동시에 존재하는 두 프로세스 사이에서만 유효하기 때문에
파일시스템을 통해 다른 프로세스가 접근할 필요가 없음
파이프는 프로세스 종료 시 자동으로 닫히고 사라짐

[위치] ****커널 메모리 공간 내에 임시 버퍼 형태로 존재

[접근] 두 개의 파일 디스크립터를 통해 접근

  • 부모-자식 관계처럼 서로 밀접하게 관련된 프로세스 사이에서만 통신 가능
  • 일반적으로 pipe() 시스템 호출을 통해 생성되며,
    fork() 시스템 호출로 자식 프로세스를 생성하면 파이프의 FD들이 복사되어 통신 경로가 확립됨
     // [c.f.]
     int fd[2];
     pipe(fd); // 익명 파이프 생성
     // fd[0] → 읽기용(read end)
     // fd[1] → 쓰기용(write end)
     // 이 시점에서 부모 프로세스 하나만 존재하고, 파이프를 통해 데이터를 읽고 쓸 수 있음
     
     if (fork() == 0) { // fork()는 새로운 자식 프로세스를 생성
         // 자식: 읽기 전용
         close(fd[1]);
         read(fd[0], buf, sizeof(buf));
     } else {
         // 부모: 쓰기 전용
         close(fd[0]);
         write(fd[1], "data", 4);
     }
     
     ```
     
  • pipe(fd)
    익명 파이프를 생성하고, 두 개의 파일 디스크립터를 반환
    `fd[0]` → 읽기용(read end)
    
    `fd[1]` → 쓰기용(write end)
    
    이 시점에서 부모 프로세스 하나만 존재하고, 파이프를 통해 데이터를 읽고 쓸 수 있음 (FIFO)
  • fork()
    자식 프로세스를 생성 (같은 파이프 객체를 공유)
    반환값이
    0 → 자식 프로세스
    >0 → 부모 프로세스(반환값은 자식 PID)
  • 자식 프로세스 부분
    읽기 전용으로 설정
    부모와 자식 모두 사용하지 않는 끝(fd)을 꼭 닫아야 데이터가 정상적으로 전달되고 EOF가 감지됨
    close(fd[1]);
    자식은 사용하지 않은 쓰기용 fd를 닫음
    이렇게 해야 EOF가 정상적으로 감지됨
    read(fd[0], buf, sizeof(buf));
    파이프 fd[0]에서 데이터를 읽음
    데이터가 아직 쓰여지지 않았다면 블록 상태가 되어,
    부모가 데이터를 쓸 때까지 기다림
  • 부모 프로세스 부분
    쓰기 전용으로 설정
    close(fd[0]);
    읽기용 fd 닫기
    ****write(fd[1], "data", 4);
    문자열 “data”를 파이프에 쓰기
    이 데이터는 FIFO 구조에 따라 자식이 먼저 읽음
    자식이 읽은 데이터는 버퍼 buf에 저장
  • 모든 프로세스가 close(fd[0]), close(fd[1])를 하면
    커널이 파이프 객체를 해제하고 버퍼 메모리를 반환

커널은 파이프 생성 시 내부적으로 커널 메모리 버퍼를 만들고, 이 버퍼에 연결된
struct file 구조체 두 개(읽기/쓰기)를 할당
이 두 개의 구조체가 각각 fd[0], fd[1]과 연결됨
→ 이름이 없어도 fd만 공유하면 통신이 가능
fd는 커널 메모리에 존재하는 같은 파이프 객체를 가리키기 때문에 두 프로세스는 해당 객체를 통해 데이터를 주고 받을 수 있음


명명된 파이프 (Named Pipe, FIFO)

파일 시스템에 이름(파일 시스템 내의 엔트리, pathname)이 존재하며,
여러 프로세스들이 이 이름을 통해 임의로 데이터를 읽고 쓸 수 있는 파이프

[위치] 파일 시스템 내에 특수 파일 형태로 존재
데이터 자체는 익명 파이프처럼 커널 버퍼에 저장됨

[접근] 파일 시스템의 경로를 사용

  • 서로 관계 없는 프로세스 간에도 통신이 가능함
  • 데이터가 한 번 읽히면 파이프에서 사라진다는 점을 제외하면 일반 파일처럼 open(), read(), write(), close() 등의 파일 입출력 함수를 사용할 수 있음
// c.f

[terminal] mkfifo /tmp/myfifo

mkfifo("/tmp/myfifo", 0666);  // FIFO 생성

// 부모 프로세스
int fd = open("/tmp/myfifo", O_WRONLY);
write(fd, "Hello", 5);
close(fd);

// 자식 프로세스
int fd = open("/tmp/myfifo", O_RDONLY);
char buf[100];
read(fd, buf, sizeof(buf));
close(fd);
  • 프로세스가 open()하면 커널 내부에 파이프 객체(pipe_inode_info)가 생성됨
  • 디스크에는 이름과 권한 정보만 존재
    실질적 데이터는 커널 메모리의 버퍼에 저장
  • fd를 통해 읽기/ 쓰기 가능 (FIFO)
  • FIFO 파일 이름(/tmp/myfifo)으로 프로세스 간 통신 가능
  • 참조하는 프로세스가 모두 종료되면 버퍼는 커널에서 해제됨

[c.f.] 파일 디스크립터 (file descriptor)

image.png

[파일 디스크립터 구조]

구성 요소위치설명

| 파일 디스크립터 번호
(fd: int)
| 유저 공간 | 정수값
커널의 테이블 인덱스 역할 |
| 파일 디스크립터 테이블
(File Descriptor Table)
| 커널 공간 (프로세스별) | 각 fd가 어떤 파일을 가리키는지 저장하는 포인터 배열 |
| Open File Table (struct file) | 커널 공간 (시스템 전역) | 실제 열린 파일의 상태 정보 (offset, flags 등) |
| Inode Table (struct inode) | 커널 공간 (파일시스템 계층) | 파일의 메타데이터 (권한, 크기, 디스크 블록 등) |

OS(주로 Unix / Linux 계열)에서 프로세스가 파일, 소켓, 파이프 등과 같은 입출력 자원에 접근하기 위해 사용하는 정수 기반의 식별자 (ID)

⇒ 프로세스가 어떤 파일이나 네트워크 자원과 연결되어 있는지를 구분하기 위한 번호표

  • 운영체제는 모든 입출력 자원을 파일로 취급함
    파일뿐 아니라 터미널, 네트워크 소케스 파이프, 장치 등 모두 파일처럼 다룸
  • 각 프로세스는 이런 자원에 접근할 때 파일 디스크립터 테이블을 가지고 있음
    이 테이블의 각 엔트리는 특정 자원과 연결되어 있고,
    그 엔트리에 접근하기 위해 사용하는 숫자가 파일 디스크립터(fd)
  • 파일 디스크립터가 가리키는 실제 데이터 구조체들은 모두 커널 메모리 안에 있음
    파일 디스크립터 값(int)는 유저 공간에서 사용되지만
    파일 디스크립터 관련 구조체들은 커널 메모리 공간에 존재함
    - 운영체제의 메모리 영역
    사용자 프로세스는 커널 공간에 직접 접근할 수 없음
    open(), read(), write() 같은 시스템 호출(System Call)을 통해서만 커널에 요청할 수 있음
        | 영역 | 접근 가능한 주체 | 내용 |
        | --- | --- | --- |
        | **유저 공간(User space)** | 애플리케이션 코드 
        (e.g. C 프로그램) | 코드, 변수, 스택, 힙 등 |
        | **커널 공간(Kernel space)** | 운영체제 커널 코드만 접근 가능 | 시스템 콜, 파일, 네트워크, 디바이스 관리  |

2) 메세지 큐 (Message Queue)

[메세지 큐]
커널 메모리에 존재하며, 메세지를 단위로 전송하고, 순서와 우선순위를 관리할 수 있는 데이터 구조

MSGMAX와 MSGMNB에 의해 크기가 제한되고, 큐가 꽉 차면 블로킹 혹은 에러 처리 방식으로 동작

파라미터의미
MSGMAX한 메시지 최대 크기
MSGMNB메시지 큐 전체 최대 크기 (큐에 저장 가능한 총 메시지 바이트)
MSGPOOL커널 전체 메시지 버퍼 총 용량

image.png

  • 메시지 단위로 저장 → 각 메시지의 크기와 타입이 별도로 관리됨
  • FIFO, 타입 우선순위, 큐 제한 크기 등 커널이 관리
  • 프로세스가 큐를 열거나 닫아도, 메시지는 커널 메모리에 유지

[FIFO와 다른점]

항목파이프 / Named pipe메시지 큐
단위바이트 스트림메시지 단위 (mtype + mtext)
데이터 순서FIFOFIFO 또는 메시지 타입 우선
파일 시스템 존재 여부Named pipe만 존재없음 (커널 메모리)
동기화쓰기/읽기 블로킹커널 내부 큐로 자동 동기화, 블록 가능
여러 프로세스가능하지만 데이터 섞임메시지 타입으로 선택적 수신 가능
크기 제한커널 버퍼 크기큐 최대 크기(MSGMAX 등)

파이프와 달리 바이트 스트림이 아니라
메시지 단위
, 우선순위 선택 가능, 파일 이름 불필요가 큰 특징

[특징]

  • 데이터가 독립된 메세지 단위로 관리 → partial read 걱정 없음
  • 우선순위 기반 처리 가능
  • 커널 메모리 관리
    메세지 큐 객체와 메세지는 모두 커널 메모리에 존재
    모든 프로세스가 종료되거나 제거 시 메모리 해
  • 다중 프로세스 지원
    커널 메모리 공간에 메세지를 저장하는 리스트(큐)를 두고,
    서로 다른 프로세스들이 동시에 독립적으로 메시지를 넣거나(send) 꺼내어(receive) 통신함
    (커널이 충돌 없이 관리)

3) 공유 메모리 (Shared Memory)

image.png

커널 메모리에 생성된 버퍼를 여러 프로세스가 가상 주소로 매핑하여 직접 읽고 쓰는 방식
속도는 빠르지만 동기화와 메모리 해제 관리가 중요함

[파이프 / 메세지 큐와 비교]

항목파이프 / 메시지 큐공유 메모리
데이터 전송커널을 통해 전달커널 메모리에 직접 매핑
속도중간 (시스템 콜 필요)매우 빠름
데이터 단위바이트 / 메시지자유 (버퍼 크기만 제한)
동기화자동 블로킹 가능프로세스 간 직접 관리 필요
여러 프로세스FIFO / 타입 순서모든 프로세스가 동시에 접근 가능
수명 관리커널이 관리프로세스가 분리 후 필요 시 IPC_RMID로 제거

[특징]

  • 커널을 거치지 않고 직접 읽기/쓰기 가능 → 속도 매우 빠름
  • 메시지 단위 제한 없음 → 원하는 크기와 구조로 데이터 관리 가능
  • 메시지 큐, 파이프와 비교 → 데이터 전달 단위는 자유, 크기 제한은 설정 가능
  • 동기화 필요 → 두 프로세스가 동시에 접근하면 race condition 발생 가능
    → semaphores, mutex 등을 활용
  • 다중 프로세스 사용 가능 → 모든 매핑 프로세스는 동일 물리 메모리 참조

4) 소켓 (Socket)

원래 네트워크 통신을 위해 고안된 기술이지만, 같은 컴퓨터 내의 서로 다른 프로세스 간 통신에도 활용 가능
프로세스가 같은 머신에 있어도, 다른 머신에 있어도 동일한 코드 구조로 통신 가능

특히 클라이언트-서버 모델을 기반으로 하는 양방향 통신에 매우 유용

[1] 소켓 기반 IPC

[소켓이란?]
네트워크 상에서 데이터를 송수신하기 위해 프로토콜, IP 주소, 포트 번호의 조합으로 정의되며
프로세스가 데이터를 주고받을 수 있는 인터페이스를 제공

  • IP + Port를 통해 통신 상대를 지정
  • IPC로 쓰일 때 주로 로컬 호스트(127.0.0.1)를 이용함
  • TCP/UDP 같은 프로토콜 선택 가능 → 신뢰성 보장(TCP) 또는 속도 우선(UDP)

[로컬 IPC에서의 소켓]
1. 인터넷 소켓 (Internet Socket)

AF_INET 주소 체계를 사용하여 IP 주소와 포트 번호를 기반으로 통신
데이터가 네트워크 스택(Network Stack)을 거치기 때문에 다른 IPC 방식에 비해 속도는 느릴 수 있지만, 통신 방식이 네트워크 통신과 동일하므로 확장성이 매우 높음

  1. 유닉스 도메인 소켓(Unix Domain Socket, UDS)

AF_UNIX 주소 체계를 사용하여 IP 주소 대신 파일 시스템의 경로(path)를 기반으로 통신
네트워크 스택을 거치지 않고 커널 내부에서 직접 처리되므로, 인터넷 소켓보다 훨씬 빠르며 로컬 IPC에서 효율적인 대안으로 사용됨

[장점]

  • 프로세스 위치 무관
    같은 머신 / 다른 머신 동일 코드
  • 원격 통신과의 일관성
    로컬 IPC에 소켓을 사용하면, 나중에 이 통신을 다른 컴퓨터의 프로세스로 확장할 때도 코드 구조를 크게 변경할 필요가 없어 확장성이 높음
  • 동시 다중 연결 가능
  • 표준화된 API
    거의 모든 OS에서 동일 인터페이스 제공
  • 확장성 높음
    단순한 파이프보다 복잡한 프로토콜 구현 가능

[2] TCP 소켓 vs Unix Domain Socket

구분TCP SocketUnix Domain Socket
범위로컬 + 네트워크로컬 호스트 전용
성능상대적으로 느림매우 빠름
설정IP + Port 필요파일 경로 필요
연결 방식connect → acceptconnect → accept
신뢰성TCP 사용시 보장스트림 기반, 커널에서 처리

Unix Domain Socket은 로컬 IPC에 최적화되어, TCP/IP보다 오버헤드가 적음

TCP 소켓을 쓰면 로컬 프로세스끼리도 TCP/IP를 통해 통신 가능하지만, 약간 느림

[3] 소켓 기반 IPC 동작 구조

  1. 소켓 생성 (Socket): 서버와 클라이언트 모두 통신을 위한 소켓을 생성
  2. 서버:
    • 주소 바인딩 (Bind): 소켓에 자신의 주소(IP 주소 및 포트 번호)를 할당
    • 리스닝 (Listen): 클라이언트의 연결 요청을 기다림
    • 수락 (Accept): 클라이언트의 요청이 오면 연결을 수락하고 새로운 통신 소켓을 생성하여 클라이언트와 1:1 연결을 맺음
  3. 클라이언트:
    • 연결 (Connect): 서버의 주소로 연결을 요청
  4. 데이터 교환:
    연결이 수립되면 양방향으로 데이터를 읽고(read/recv) 쓰는(write/send) 작업을 수행
  5. 연결 종료 (Close):
    통신이 끝나면 소켓을 닫아 연결을 해제

0개의 댓글