Skip to main content

리눅스의 초고속 공유 메모리, /dev/shm 디렉토리

  • /dev/shm은 리눅스 시스템에서 파일처럼 보이지만 실제로는 RAM(메모리)을 사용하는 공간임.
  • /dev/shm은 물리적 하드디스크(SSD/HDD)가 아니라 가상 파일 시스템인 tmpfs로 마운트되어 있음.

일반 디스크 (/home, /var 등)

  1. 파일 쓰기 요청
  2. 파일 시스템 드라이버 (ext4, xfs)
  3. 디스크 컨트롤러
  4. SSD/HDD에 저장 (속도가 느리며 I/O 발생)

tmpfs (/dev/shm):

  1. 파일 쓰기 요청
  2. 리눅스 커널의 페이지 캐시 (Page Cache)
  3. RAM에 즉시 저장 (속도가 매우 빠름)

즉, /dev/shm에 1GB 짜리 파일을 생성하면 실제 RAM도 즉시 1GB만큼 사용되며 파일을 지우면 RAM도 즉시 반환됨.

단순히 빠른 저장소가 필요하다면 램을 사용하면 되지만 /dev/shm은 POSIX Shared Memory 표준을 구현하기 위해 만들어졌음. 리눅스에서 서로 다른 프로세스 A와 B는 보안 때문에 서로의 메모리를 침범할 수 없지만 서로 데이터를 주고 받아야 할 때가 있는데 이때 /dev/shm을 활용하여 데이터를 공유합니다.

/dev/shm을 사용하여 프로세스 간 데이터 공유

  1. 프로세스 A: /dev/shm/data라는 파일을 생성
  2. Memory Mapping (mmap): 프로세스 A는 이 파일을 자신의 메모리 공간인 것처럼 매핑
  3. 프로세스 B: 똑같이 /dev/shm/data를 열고 자신의 메모리에 매핑
  4. 통신: 이제 A가 메모리에 값을 쓰면, 디스크를 거치지 않고 즉시 B가 그 값을 읽을 수 있음

대표적인 사용 예시

  • 크롬 브라우저: 탭마다 별도 프로세스가 뜨는데, 탭끼리 데이터를 주고받을 때 /dev/shm을 씀
  • PostgreSQL: 병렬 처리 쿼리를 수행할 때 프로세스끼리 데이터를 교환하는 공간으로 사용

  • 동적 할당: 리눅스 커널은 /dev/shm을 아주 똑똑하게 관리하며 /dev/shm에 아무것도 없으면 RAM을 0byte만 사용하며 파일을 넣는 만큼만 증가함, 보통은 전체 RAM의 50%가 최대치로 설정
  • 스왑 가능: 만약 물리 RAM이 부족해지면, 커널은 /dev/shm에 있는 데이터를 잠시 디스크의 Swap 영역으로 내쫓으며 RAM 기반이지만 RAM이 터지기 직전에는 디스크를 빌려 써서 시스템이 다운되는 것을 방지함

속도 비교

  • Disk 쓰기: CPU -> 커널 -> 파일시스템 드라이버 -> 네트워크 -> EBS 스토리지
  • 메모리 쓰기: CPU가 메모리 컨트롤러를 통해 DRAM에 직접 접근
# 디스크 쓰기 / 약 7.3초 소요
[ec2-user@ip-10-180-10-47 shm]$ dd if=/dev/zero of=~/test_real_disk bs=1M count=1024 conv=fdatasync
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 7.31239 s, 147 MB/s

# 메모리 쓰기 / 약 0.6초 소요
[ec2-user@ip-10-180-10-47 shm]$ dd if=/dev/zero of=/dev/shm/test_ram_v2 bs=1M count=1024 conv=fdatasync
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 0.685891 s, 1.6 GB/s

도커 환경에서의 주의사항 (shm_size)

앞서 살펴본 것처럼 고성능 애플리케이션은 /dev/shm을 적극적으로 사용합니다. 하지만 도커(Docker) 컨테이너는 기본적으로 /dev/shm 사이즈를 64MB로 제한하여 생성합니다. 이로 인해 컨테이너 내부에서 공유 메모리 부족으로 인한 충돌이 발생할 수 있어 도커 컴포즈 등에서는 용량을 명시적으로 늘려주는 작업이 필요합니다. 그런데 도커 컴포즈에서 shm_size: '512m'으로 설정하더라도 실제 할당된 바이트 수가 미묘하게 다른데, 이는 도커가 기본적으로 MiB (Mebibyte) 단위를 사용하기 때문이다.

  • 설정값: 512m (512 MiB)
  • 512 x 1,024 x 1,024 = 약 536.8MB

실제로 docker inspect 명령어로 확인해보면 바이트 단위로 정확히 변환된 것을 볼 수 있습니다.

[ec2-user@ip-10-180-10-47 backend]$ docker inspect 3a6754d04ef7 | grep -i "shm"
"ShmSize": 536870912,