cd ../
Misc·2026-05-20·4 min read·# entry/043

UDS를 이해하기 위한 OS 기초: 프로세스, IPC, fd

docker.sock이 왜 빠르고 안전한지 이해하려면 OS가 프로세스를 격리하는 방식과 그 격리를 넘는 IPC, 그리고 Unix의 'Everything is a file' 철학까지 알아야 합니다.

docker.sock이 왜 TCP보다 빠른지, 왜 그 파일에 접근하는 것이 root 권한과 동등한지를 제대로 이해하려면 한 단계 아래 레이어부터 봐야 합니다.

프로세스는 격리되어 있다. 그 격리를 넘어 협력하는 방법이 IPC이고, UDS는 그 방법 중 하나다.


프로세스란 무엇인가

npm run dev로 띄운 하나의 애플리케이션이 하나의 프로세스입니다. OS 관점에서는 실행 중인 프로그램의 인스턴스입니다.

하나의 프로세스는 다음을 독점적으로 소유합니다.

프로세스 A                    프로세스 B
┌─────────────────┐          ┌─────────────────┐
│  자기만의 메모리  │          │  자기만의 메모리  │
│  자기만의 변수    │    ✗     │  자기만의 변수    │
│  자기만의 파일    │ ←직접접근 │  자기만의 파일    │
│  자기만의 핸들    │  불가능   │  자기만의 핸들    │
└─────────────────┘          └─────────────────┘
         OS가 강제 격리

프로세스가 서로의 메모리에 직접 접근할 수 없도록 OS가 격리합니다. 덕분에 어떤 프로세스가 충돌해도 다른 프로세스는 영향을 받지 않습니다.

DB의 트랜잭션과 유사한 개념

RDB에서 트랜잭션이 다른 트랜잭션의 중간 상태를 볼 수 없도록 격리하는 것과 같은 맥락입니다. 격리가 안정성과 보안의 기반입니다.

그런데 이 격리가 새로운 문제를 만들어냅니다. Docker CLI는 어떻게 dockerd에 접근해서 명령할 수 있을까요? 이 질문에 답하는 것이 IPC입니다.


IPC: 격리된 프로세스들의 협력 방법

IPC(Inter-Process Communication) 는 격리된 프로세스들이 협력할 수 있게 해주는 메커니즘의 총칭입니다. OS는 다양한 IPC 방식을 제공하며, 각각 특성이 다릅니다.

방식비유특징
Pipe일방향 컨베이어벨트부모-자식 프로세스 간 통신
FIFO일방향 우편함무관한 프로세스 간에도 가능
Signal알림 종데이터 전송 불가, 신호만 전달
Shared Memory공유 화이트보드가장 빠르지만 동기화가 어려움
Message Queue우편함 시스템메시지 단위로 전달
UDS양방향 메신저가장 강력하고 유연

UDS도 이 중 하나입니다. Docker CLI ↔ dockerd 통신에 UDS가 선택된 이유는 이후에 살펴봅니다.


fd: Everything is a File

Unix의 가장 유명한 철학 중 하나입니다.

모든 것은 파일이다. (Everything is a file)

이 철학 아래 프로세스가 다루는 모든 대상이 동일한 인터페이스로 추상화됩니다.

대상fd 여부
디스크 파일fd
네트워크 소켓fd
UDS 소켓fd
키보드 입력 (stdin)fd 0
화면 출력 (stdout)fd 1
에러 출력 (stderr)fd 2
파이프fd
디바이스 (USB, 프린터 등)fd

덕분에 모두 동일한 read(), write() 함수로 다룰 수 있습니다.

int file_fd = open("/tmp/data.txt", O_RDONLY);
int sock_fd = socket(AF_INET, SOCK_STREAM, 0);

char buf[100];
read(file_fd, buf, 100);  // 파일에서 읽기
read(sock_fd, buf, 100);  // 네트워크에서 읽기
// 같은 함수. 같은 인터페이스.
추상화의 힘

네트워크 소켓이든 파일이든 UDS든 코드 입장에서는 "fd에서 읽는다"는 동작이 동일합니다. 대상이 바뀌어도 코드 구조가 바뀌지 않습니다.


fd의 구조: 프로세스별 독립 테이블

fd는 단순한 정수(0, 1, 2, 3, …)입니다. 각 프로세스는 자신만의 fd 테이블을 갖습니다.

프로세스 A의 fd 테이블
  fd 0 → 표준 입력  (stdin)
  fd 1 → 표준 출력  (stdout)
  fd 2 → 표준 에러  (stderr)
  fd 3 → /tmp/data.txt
  fd 4 → 네트워크 소켓
  fd 5 → UDS 소켓

open(), socket() 같은 함수를 호출하면 OS가 다음 과정을 수행합니다.

1. 실제 객체(파일, 소켓 등) 생성
2. 프로세스 fd 테이블에서 빈 슬롯 탐색
3. 해당 슬롯에 객체 연결
4. 슬롯 번호(fd) 반환

이후 read(fd, ...), write(fd, ...) 를 호출할 때 이 번호를 사용합니다.

fd는 프로세스 내부에서만 유효하다

fd 번호는 프로세스 단위로 독립적입니다. 프로세스 A의 fd 5와 프로세스 B의 fd 5는 완전히 다른 객체를 가리킵니다. 다른 프로세스에 fd 번호를 전달해봤자 사용할 수 없습니다.


UDS란 무엇인가

UDS(Unix Domain Socket) 는 같은 컴퓨터 안의 프로세스끼리 통신하기 위한 소켓입니다.

다른 컴퓨터와의 통신을 포함한 범용 소켓입니다.

프로세스 A
  ↓
TCP/IP 스택
(패킷화 → IP 헤더 → TCP 헤더 → 체크섬 → 라우팅)
  ↓
네트워크 인터페이스
  ↓
목적지 (같은 컴퓨터라도 이 경로를 모두 거침)

주소 형식: 192.168.1.1:8080 (IP + Port)

왜 UDS가 Network Socket보다 빠른가

같은 컴퓨터 안에서 Network Socket으로 통신하면, 실제로는 외부 네트워크로 나가지 않더라도 전체 네트워크 스택을 통과합니다.

UDS가 생략하는 것들
  • TCP 헤더 생성 및 파싱 - IP 헤더 생성 및 파싱 - 체크섬 계산 - 라우팅 테이블 조회 - 네트워크 인터페이스 드라이버 호출 - 패킷 분할 및 재조립
UDS가 하는 것
  • 프로세스 A의 소켓 버퍼 → 프로세스 B의 소켓 버퍼 메모리 복사 한 번
얼마나 빠를까?

벤치마크 기준으로 UDS는 localhost TCP 대비 보통 2~5배 빠르고, 레이턴시 차이는 그보다 더 큽니다. 처리량보다 지연 시간 측면에서 이점이 두드러집니다.


정리: 왜 docker.sock은 UDS인가

프로세스 격리 (OS 보장)
  ↓ 격리를 넘어 협력이 필요할 때
IPC
  ↓ 같은 호스트, 양방향, 빠르고 안전해야 할 때
UDS (/var/run/docker.sock)
  ↓ fd로 추상화되어
read() / write() 로 통신

docker CLI와 dockerd는 같은 호스트 안에 있고, 양방향 통신이 필요하며, 파일 권한으로 접근 제어가 가능해야 합니다. UDS가 이 조건을 모두 만족하는 가장 적합한 선택입니다.