리눅스의 초고속 공유 메모리, /dev/shm 디렉토리
- /dev/shm은 리눅스 시스템에서 파일처럼 보이지만 실제로는 RAM(메모리)을 사용하는 공간임.
- /dev/shm은 물리적 하드디스크(SSD/HDD)가 아니라 가상 파일 시스템인 tmpfs로 마운트되어 있음.
일반 디스크 (/home, /var 등)
- 파일 쓰기 요청
- 파일 시스템 드라이버 (ext4, xfs)
- 디스크 컨트롤러
- SSD/HDD에 저장 (속도가 느리며 I/O 발생)
tmpfs (/dev/shm):
- 파일 쓰기 요청
- 리눅스 커널의 페이지 캐시 (Page Cache)
- RAM에 즉시 저장 (속도가 매우 빠름)
즉, /dev/shm에 1GB 짜리 파일을 생성하면 실제 RAM도 즉시 1GB만큼 사용되며 파일을 지우면 RAM도 즉시 반환됨.
단순히 빠른 저장소가 필요하다면 램을 사용하면 되지만 /dev/shm은 POSIX Shared Memory 표준을 구현하기 위해 만들어졌음.
리눅스에서 서로 다른 프로세스 A와 B는 보안 때문에 서로의 메모리를 침범할 수 없지만 서로 데이터를 주고 받아야 할 때가 있는데
이때 /dev/shm을 활용하여 데이터를 공유합니다.
/dev/shm을 사용하여 프로세스 간 데이터 공유
- 프로세스 A:
/dev/shm/data라는 파일을 생성 - Memory Mapping (mmap): 프로세스 A는 이 파일을 자신의 메모리 공간인 것처럼 매핑
- 프로세스 B: 똑같이
/dev/shm/data를 열고 자신의 메모리에 매핑 - 통신: 이제 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,