Devin.KR
로그인

리눅스 dmesg 명령어 사용법 - OOM Killer와 디스크 오류를 커널 로그에서 찾기

개발자 조회 1

이 명령어를 언제 쓰는가

애플리케이션이 아무 로그도 남기지 않고 사라졌을 때 가장 먼저 봐야 하는 곳이다. 자바 프로세스가 예외 없이 죽었고, 애플리케이션 로그 마지막 줄이 평범한 요청 처리라면 십중팔구 커널의 OOM Killer가 죽인 것이다. 그 증거는 애플리케이션 로그가 아니라 커널 링 버퍼, 즉 dmesg에만 남는다.

두 번째 용도는 하드웨어다. 디스크 I/O가 갑자기 느려지거나 파일시스템이 읽기 전용으로 바뀌었을 때, 네트워크가 끊겼다 붙었다 할 때 커널은 그 사실을 dmesg에 적어 둔다. "서버가 느리다"의 원인이 소프트웨어가 아니라 죽어가는 디스크인 경우가 실제로 있다.

기본 형식

dmesg [옵션]

dmesg는 커널이 메시지를 쌓아 두는 링 버퍼를 읽어 출력한다. 링 버퍼는 고정 크기라서 오래된 메시지는 새 메시지에 밀려 사라지고, 재부팅하면 통째로 초기화된다. 그래서 dmesg는 "최근 커널이 무슨 말을 했는가"를 보는 도구이지 영구 로그가 아니다.

자주 쓰는 옵션

옵션의미예시
-T부팅 후 초 대신 사람이 읽는 날짜·시각으로dmesg -T
-w새 메시지를 실시간으로 이어서 출력dmesg -Tw
-l 레벨지정한 심각도만 (emerg,alert,crit,err,warn,notice,info,debug)dmesg -T -l err,crit
-H사람이 읽기 좋게 색과 페이저를 적용dmesg -H
-x각 줄 앞에 시설/레벨을 문자로 표시dmesg -Tx | tail
--since / --until기간 지정(util-linux 2.30 이상)dmesg -T --since "1 hour ago"
-C링 버퍼를 비움(테스트 전 초기화용)sudo dmesg -C

실전 예제

1) 프로세스가 사라진 이유가 OOM Killer인지 확인한다. dmesg에서 가장 자주 쓰게 되는 한 줄이다.

sudo dmesg -T | grep -iE 'out of memory|oom-killer|killed process'

결과가 있으면 이런 모양이다.

[Tue Aug 25 03:12:44 2026] java invoked oom-killer: gfp_mask=0x100cca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0
[Tue Aug 25 03:12:44 2026] Out of memory: Killed process 4321 (java) total-vm:8388608kB, anon-rss:4194304kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:8704kB oom_score_adj:0

읽는 법이 중요하다. 첫 줄의 java invoked oom-killer는 "자바가 메모리를 요청하다가 부족해서 OOM Killer를 깨웠다"는 뜻이지 자바가 범인이라는 뜻이 아니다. 실제로 죽은 프로세스는 둘째 줄의 Killed process 4321 (java)이고, 커널은 메모리를 가장 많이 쓰는 쪽을 골라 죽이므로 요청자와 희생자가 다를 수 있다. anon-rss가 그 프로세스가 실제로 붙잡고 있던 물리 메모리다. 위 예에서는 약 4GB다.

2) 디스크가 죽어가는 중인지 본다.

sudo dmesg -T | grep -iE 'i/o error|ata[0-9]|nvme|medium error|ext4-fs error|xfs .*error|remount.*read-only'

EXT4-fs errorRemounting filesystem read-only가 보이면 애플리케이션이 쓰기에 실패하는 이유가 이미 확정된 것이다. 이때는 서비스 재시작이 아니라 파일시스템 점검과 디스크 교체를 준비해야 한다.

3) 최근 한 시간의 오류급 메시지만 추린다.

sudo dmesg -T -l err,crit,alert,emerg --since "1 hour ago"

4) 재현 테스트를 하면서 커널 메시지를 실시간으로 지켜본다.

sudo dmesg -Tw

USB 장치를 꽂거나 디스크를 마운트하거나 방화벽 로그를 켜고 트래픽을 흘려 볼 때 유용하다.

5) 네트워크 인터페이스가 끊겼다 붙는지 확인한다.

sudo dmesg -T | grep -iE 'link is (down|up)|NIC Link|eth[0-9]|ens[0-9]'

6) 이전 부팅에서 무슨 일이 있었는지 본다(dmesg로는 불가능하다).

journalctl -k -b -1 --no-pager | tail -50

커널 링 버퍼는 재부팅과 함께 사라지므로 서버가 죽었다 살아난 원인은 dmesg로는 절대 못 본다. systemd가 영구 저널을 쓰도록 설정돼 있다면 journalctl -k -b -1로 직전 부팅의 커널 로그를 볼 수 있다. 이것이 dmesg와 journalctl을 같이 알아야 하는 이유다.

함정과 주의점

-T의 시각은 정확하지 않을 수 있다. 커널 메시지에는 부팅 이후 경과 초만 기록된다. -T는 "부팅 시각 + 경과 초"로 역산해 날짜를 만들어 내는 것이라, 서스펜드/레주메가 있었거나 NTP가 시간을 크게 보정한 뒤에는 몇 분에서 몇 시간까지 어긋난다. 장애 시각을 분 단위로 특정해야 하는 상황이면 dmesg가 아니라 journalctl -k의 타임스탬프를 근거로 삼는다.

일반 사용자는 못 읽는다. Ubuntu를 비롯한 대부분의 배포판이 kernel.dmesg_restrict=1로 설정해 두어, 권한 없이 실행하면 dmesg: read kernel buffer failed: Operation not permitted가 뜬다. 버그가 아니라 보안 설정이므로 sudo dmesg로 실행한다. 현재 값은 sysctl kernel.dmesg_restrict로 확인한다.

중요한 메시지가 이미 밀려 나갔을 수 있다. 링 버퍼 크기는 커널 빌드 옵션과 log_buf_len 부팅 파라미터로 정해지며 보통 수백 KB~1MB다. 드라이버가 경고를 초당 수백 줄씩 쏟아 내는 상황이라면 정작 필요한 OOM 기록이 이미 사라졌을 수 있다. 이때도 journalctl 쪽에 남아 있을 가능성이 있으니 두 곳을 모두 본다.

OOM 기록을 찾았다면 그 다음이 본론이다. dmesg는 "죽었다"는 사실만 알려 준다. 왜 메모리가 부족했는지는 별도로 봐야 한다. 스왑이 실제로 오갔는지는 vmstat의 si/so 열로, 어떤 프로세스가 메모리를 점유하는지는 top이나 ps -eo pid,comm,rss --sort=-rss | head로 확인한다. 컨테이너라면 호스트 전체 OOM이 아니라 cgroup 메모리 한도 초과일 수 있고, 그 경우 memory cgroup out of memory라는 문구가 함께 찍힌다.

macOS의 dmesg는 다르다. 이름은 같지만 출력 형식과 옵션 체계가 달라 -T-l이 없고, 최근 macOS에서는 log show --predicate 'senderImagePath contains "kernel"' 쪽을 쓴다. 이 문서의 예제는 리눅스 기준이다.

함께 보면 좋은 명령어

  • journalctl - 재부팅 이전 커널 로그와 정확한 타임스탬프는 여기에만 있다.
  • vmstat - OOM 기록을 찾은 뒤 스왑과 메모리 추이를 확인해 원인을 좁힌다.
  • free - OOM 직후 남아 있는 메모리와 스왑 사용량을 즉시 확인한다.