본문으로 건너뛰기

안동민 개발노트

본문 시작

시스템 모니터링과 디버깅

strace·proc·dmesg·journalctl·코어 덤프·perf로 시스템 콜과 자원 사용을 추적해 장애 원인을 좁힙니다.

프로그램이 이상하게 동작한다고 할 때, 로그만으로 원인을 찾지 못한다면 OS 수준의 디버깅 도구가 필요합니다.

프로세스가 어떤 시스템 콜을 호출하는지, 커널이 무슨 메시지를 출력하는지, 메모리와 CPU를 어떻게 사용하는지를 추적하면 대부분의 문제를 진단할 수 있습니다.

OS 관측 증거 추적

시스템 콜, /proc 상태, 커널 로그, 코어 덤프, CPU 샘플을 같은 문제로 보지 말고 증상에 맞는 첫 도구를 고른다.

  1. 증상별 첫 확인 지점

    파일을 못 찾거나 DB 연결이 멈춘다 strace -e trace=file 이나 trace=network 로 실제 경로와 연결 대상, 에러 코드를 본다. 2 프로세스 상태와 자원 사용량이 궁금하다 /proc/[pid]/status , fd , maps , io 에서 스레드, 열린 파일, 메모리 맵을 확인한다. 3 OOM, 디스크, 드라이버처럼 커널 이벤트가 의심된다 dmesg --level=err,warn 과 journalctl -p err 로 커널과 서비스 로그를 맞춰 본다. 4 세그먼테이션 폴트나 CPU 병목을 재현했다 코어 덤프는 gdb bt 로 콜 스택을 보고, 느린 구간은 perf record -g 로 샘플링한다.

  2. 파일을 못 찾거나 DB 연결이 멈춘다

    strace -e trace=file 이나 trace=network 로 실제 경로와 연결 대상, 에러 코드를 본다.

  3. 프로세스 상태와 자원 사용량이 궁금하다

    /proc/[pid]/status , fd , maps , io 에서 스레드, 열린 파일, 메모리 맵을 확인한다.

  4. OOM, 디스크, 드라이버처럼 커널 이벤트가 의심된다

    dmesg --level=err,warn 과 journalctl -p err 로 커널과 서비스 로그를 맞춰 본다.

  5. 세그먼테이션 폴트나 CPU 병목을 재현했다

    코어 덤프는 gdb bt 로 콜 스택을 보고, 느린 구간은 perf record -g 로 샘플링한다.

  6. 도구가 말해 주는 증거

    출력 예시 신호 해석 다음 확인 방향 open(...) = -1 ENOENT strace 파일 추적 경로 문제 설정 파일 위치, 상대 경로, 권한을 확인한다. socket:[12345] , VmRSS , read_bytes /proc 프로세스 정보 자원 사용 패턴 fd 누수, 메모리 증가, 실제 디스크 I/O를 분리한다. Out of memory: Kill process dmesg 커널 메시지 커널이 종료시킨 사건 메모리 제한, OOM score, 서비스 재시작 로그를 본다. 읽는 순서: 문제를 재현하고, 첫 도구로 관측한 증거를 로그와 맞춘 다음, 코드 수정이나 커널/서비스 설정 변경으로 이어간다.


strace: 시스템 콜 추적

strace는 프로세스가 호출하는 모든 시스템 콜을 추적합니다.

"프로그램이 내부에서 무엇을 하고 있는지"를 들여다보는 도구입니다.

strace 기본 사용
# 프로그램의 모든 시스템 콜 추적
strace ./myprogram

# 실행 중인 프로세스에 붙이기
strace -p 1234

# 파일 관련 시스템 콜만 추적
strace -e trace=file ./myprogram

# 네트워크 관련 시스템 콜만 추적
strace -e trace=network ./myprogram

# 시스템 콜별 소요 시간 통계
strace -c ./myprogram

# 타임스탬프와 함께 출력
strace -t ./myprogram

# 멀티스레드 프로그램 추적 (모든 스레드)
strace -f ./myprogram

프로그램이 특정 파일을 찾지 못해 실패한다면, strace -e trace=file로 어떤 경로를 탐색하는지 확인할 수 있습니다.

strace 출력 읽기

strace 출력 예시
# open("/etc/config.json", O_RDONLY) = -1 ENOENT (No such file or directory)
# → 파일이 존재하지 않음. 경로 확인 필요

# connect(3, {sa_family=AF_INET, sin_port=htons(3306), sin_addr=inet_addr("10.0.0.5")}, 16) = -1 ETIMEDOUT
# → DB 연결 타임아웃. 네트워크 문제 또는 방화벽

# write(1, "Hello\n", 6) = 6
# → stdout에 6바이트 성공적으로 기록

# mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f...
# → 익명 메모리 매핑 (malloc 내부)

strace -c 출력은 각 시스템 콜의 호출 횟수와 소요 시간을 요약합니다.

어떤 시스템 콜에서 병목이 발생하는지 파악하는 데 유용합니다.

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- --------
 65.21    0.123456          12     10238           read
 20.15    0.038123           8      4762           write
  8.33    0.015789         157       100        10 open
  ...

위 출력에서 read가 전체 시간의 65%를 차지하므로, I/O가 병목이라는 것을 알 수 있습니다.

로그만으로 부족할 때 도구를 고르는 기준

프로그램 내부 동작, 커널 이벤트, CPU 병목, 비정상 종료는 서로 다른 증거를 요구한다.

  1. 시스템 콜 경로

    strace 파일 탐색 실패, 네트워크 타임아웃, 권한 문제를 호출 단위로 추적한다.

  2. 실시간 프로세스 상태

    /proc 메모리, fd, cmdline, maps, io를 커널이 제공하는 현재값으로 읽는다.

  3. 커널 수준 이벤트

    dmesg OOM Killer, 디스크 오류, 드라이버 실패처럼 애플리케이션 로그 밖의 원인을 본다.

  4. 코어 덤프

    세그폴트 순간의 메모리와 콜 스택을 보존해 gdb로 원인 프레임을 찾는다.

  5. perf

    CPU가 실제로 시간을 쓰는 함수와 커널 이벤트를 샘플링해 병목을 좁힌다.


/proc 파일 시스템

/proc는 커널이 제공하는 가상 파일 시스템입니다.

실제 파일이 아니라 커널이 실시간으로 생성하는 정보입니다.

/proc 시스템 정보
# CPU 정보 (코어 수, 모델)
cat /proc/cpuinfo | grep "model name" | head -1
cat /proc/cpuinfo | grep processor | wc -l  # 코어 수

# 메모리 정보
cat /proc/meminfo | head -10

# 시스템 부하 (1분, 5분, 15분 평균)
cat /proc/loadavg
# 0.75 0.62 0.58 2/456 12345
# → 코어 수가 4이면, 1.0 이하는 여유 있음

# 커널 버전
cat /proc/version

# 부팅 후 경과 시간
cat /proc/uptime

프로세스별 /proc 정보

/proc/[pid] 활용
PID=1234

# 프로세스 상태 요약
cat /proc/$PID/status
# Name, State, Pid, PPid, Threads, VmSize, VmRSS 등

# 메모리 맵 (코드, 힙, 스택, 라이브러리 주소)
cat /proc/$PID/maps | head -20
# 7f1234560000-7f1234580000 r-xp ... /lib/libc.so.6
# → r-xp = 읽기/실행 가능, 쓰기 불가

# 열린 파일 디스크립터 목록
ls -la /proc/$PID/fd/
# 0 → stdin, 1 → stdout, 2 → stderr
# 3 → socket:[12345] (네트워크 연결)
# 4 → /var/log/app.log (로그 파일)

# 실행 명령어
cat /proc/$PID/cmdline | tr '\0' ' '

# 환경 변수
cat /proc/$PID/environ | tr '\0' '\n'

# I/O 통계
cat /proc/$PID/io
# read_bytes, write_bytes → 실제 디스크 I/O

/proc/[pid]/maps는 프로세스의 메모리 레이아웃을 보여줍니다.

코드 세그먼트, 힙, 스택, 공유 라이브러리의 가상 주소 범위와 권한을 확인할 수 있습니다.

proc_monitor.py
import os

def process_info(pid):
    """특정 프로세스의 상태를 /proc에서 읽기"""
    proc_dir = f"/proc/{pid}"

    if not os.path.exists(proc_dir):
        print(f"PID {pid} 프로세스가 존재하지 않습니다.")
        return

    # 상태 정보 파싱
    with open(f"{proc_dir}/status") as f:
        status = {}
        for line in f:
            parts = line.strip().split(":\t")
            if len(parts) == 2:
                status[parts[0]] = parts[1].strip()

    print(f"이름: {status.get('Name', 'N/A')}")
    print(f"상태: {status.get('State', 'N/A')}")
    print(f"스레드 수: {status.get('Threads', 'N/A')}")
    print(f"가상 메모리: {status.get('VmSize', 'N/A')}")
    print(f"물리 메모리: {status.get('VmRSS', 'N/A')}")

    # fd 수 확인
    fd_dir = f"{proc_dir}/fd"
    try:
        fd_count = len(os.listdir(fd_dir))
        print(f"열린 fd 수: {fd_count}")
    except PermissionError:
        print("fd 확인 권한 없음 (root 필요)")

process_info(1)

dmesg와 시스템 로그

dmesg는 커널이 출력하는 메시지를 보여줍니다.

하드웨어 오류, 드라이버 문제, OOM Killer 동작 등 커널 수준의 이벤트가 기록됩니다.

커널 로그
# 커널 메시지 (에러와 경고만)
dmesg --level=err,warn | tail -20

# OOM Killer가 동작했는지 확인
dmesg | grep -i "oom"
# → "Out of memory: Kill process 1234 (java) score 950 or sacrifice child"
# → score가 높을수록 먼저 죽임

# 디스크 에러 확인
dmesg | grep -iE "error|fail|I/O"

# 실시간 커널 메시지 모니터링
dmesg -w

# 사람이 읽기 쉬운 타임스탬프
dmesg -T | tail -20

journalctl (systemd 로그)

journalctl 활용
# 최근 부팅 이후 로그
journalctl -b

# 특정 서비스의 로그
journalctl -u nginx.service --since "2 hours ago"

# 실시간 로그 추적
journalctl -f

# 에러 이상 심각도만
journalctl -p err

# 특정 시간 범위
journalctl --since "2024-01-15 10:00" --until "2024-01-15 11:00"

# 디스크 사용량 확인 및 정리
journalctl --disk-usage
journalctl --vacuum-size=500M  # 500MB 이상 삭제

전통적 로그 파일

log_files.sh
# 시스템 로그
/var/log/syslog          # Ubuntu/Debian
/var/log/messages        # CentOS/RHEL

# 인증 로그 (SSH 로그인 시도 등)
/var/log/auth.log        # Ubuntu
/var/log/secure          # CentOS

# 커널 로그
/var/log/kern.log

# 실패한 로그인 시도 확인 (보안)
grep "Failed password" /var/log/auth.log | tail -20

# 특정 IP의 SSH 로그인 시도 횟수
grep "Failed password" /var/log/auth.log | awk '{print $11}' | sort | uniq -c | sort -rn | head -10
계층별 로그 분석

`dmesg`, `journalctl`, 전통적 로그 파일은 같은 시간을 다른 층에 배치합니다. 장애 원인을 좁히려면 시간 범위와 계층을 맞춰 대조해야 합니다.

  1. 커널 링 버퍼

    하드웨어 오류, OOM Killer, I/O 에러를 확인합니다. dmesg -T

  2. systemd 저널

    부팅, 서비스 단위, 심각도, 시간 범위로 필터링합니다. journalctl -u

  3. 전통 로그 파일

    인증 실패, 웹 접근, 앱 오류처럼 파일별 증거를 모읍니다. /var/log/*

  4. 실시간 추적

    재현 중인 장애는 새 로그가 생기는 순간을 따라갑니다. journalctl -f

  5. Correlation
    같은 시간대의 다른 계층 로그를 붙여 본다

    Correlation 같은 시간대의 다른 계층 로그를 붙여 본다 앱 로그의 타임아웃, `journalctl`의 재시작, `dmesg`의 I/O error가 같은 분에 겹치면 애플리케이션 버그보다 하부 자원 문제일 가능성이 커집니다.


코어 덤프 분석

프로세스가 세그먼테이션 폴트로 비정상 종료되면 코어 덤프(Core Dump)가 생성될 수 있습니다.

코어 덤프는 프로세스가 죽는 순간의 메모리 스냅샷입니다.

코어 덤프 설정
# 코어 덤프 활성화
ulimit -c unlimited

# 코어 덤프 저장 경로 설정
echo "/var/crash/core.%e.%p.%t" > /proc/sys/kernel/core_pattern
# %e = 실행 파일 이름
# %p = PID
# %t = 타임스탬프
GDB 코어 덤프 분석
# 코어 덤프 분석 (gdb 사용)
gdb ./myprogram /var/crash/core.myprogram.1234.1705312800

# gdb 프롬프트에서:
(gdb) bt           # 콜 스택 (backtrace) 확인
#0  0x00007f... in strlen () from /lib/libc.so.6
#1  0x00401234 in process_request (req=0x0) at server.c:142
#2  0x004015ab in handle_client (fd=5) at server.c:89
#3  0x00401800 in main () at server.c:42

(gdb) frame 1      # 문제 프레임으로 이동
(gdb) list         # 소스 코드 확인
(gdb) print req    # 변수 값 확인 → 0x0 (NULL!)
(gdb) info locals  # 모든 로컬 변수 출력

위 예시에서 req=0x0이므로 NULL 포인터 역참조가 원인입니다.

bt만으로도 대부분의 원인을 파악할 수 있습니다.

운영 환경에서는 코어 덤프 크기를 제한하거나 별도의 디렉토리에 저장하도록 설정합니다.

코어 덤프에 민감한 데이터(비밀번호, 토큰)가 포함될 수 있으므로 접근 권한 관리에도 주의합니다.


perf: 성능 프로파일링

perf는 리눅스 커널의 성능 분석 도구입니다.

CPU가 어디서 시간을 소비하는지 정밀하게 측정합니다.

perf 사용
# CPU 프로파일링 (30초간 샘플링)
perf record -g -p 1234 -- sleep 30

# 결과 확인
perf report

# 시스템 전체 통계 (5초간)
perf stat -a sleep 5
# → cache-misses, context-switches, page-faults 등

# 특정 이벤트 카운트
perf stat -e cache-misses,cache-references ./myprogram
# perf report 출력 예시
  45.23%  myapp  myapp       [.] hash_lookup
  22.11%  myapp  myapp       [.] parse_json
  15.67%  myapp  libc.so.6   [.] malloc
   8.44%  myapp  myapp       [.] validate_input

hash_lookup에서 45%의 시간을 소비하고 있다면, 해시 함수 최적화나 데이터 구조 변경을 검토합니다.

flame_graph.sh
# 플레임 그래프 생성 (시각화)
perf record -g -p 1234 -- sleep 30
perf script | stackcollapse-perf.pl | flamegraph.pl > flamegraph.svg

플레임 그래프는 콜 스택을 시각적으로 보여줍니다.

넓은 막대가 많은 CPU를 소비하는 함수입니다.

OS 장애 분석 흐름

모든 도구를 한 번에 켜기보다, 실패한 경계가 파일인지 네트워크인지 CPU인지 먼저 가정하고 가장 직접적인 관측 도구부터 적용합니다.

  1. 1
    어디서 시간을 쓰는지 본다

    느린 응답 어디서 시간을 쓰는지 본다 CPU 병목이면 perf, I/O 대기면 strace 통계와 /proc/io를 먼저 봅니다. 샘플링 perf record -g 시스템 콜 시간 strace -c

  2. 2
    프로세스가 실제로 요청한 경로를 추적

    파일 또는 네트워크 실패 프로세스가 실제로 요청한 경로를 추적 설정 경로 오류, 권한 문제, 연결 타임아웃은 시스템 콜 결과에 직접 드러납니다. 파일 strace -e trace=file 네트워크 strace -e trace=network

  3. 3
    죽은 순간의 증거를 보존

    비정상 종료 죽은 순간의 증거를 보존 코어 덤프와 커널 로그를 함께 보면 NULL 역참조, OOM, 디스크 오류를 구분할 수 있습니다. 콜 스택 gdb app core 커널 사건 dmesg --level=err

다음 절에서는 커널 파라미터 튜닝과 OS 관련 기술 면접 주제를 정리하겠습니다.

성능 관찰 지표 연결

성능 진단은 CPU, 메모리, 디스크를 따로 보지 않습니다. run queue, major fault, iowait가 동시에 움직이는지 봐야 원인이 좁혀집니다.

  1. 01

    top / ps

  2. 02

    vmstat

  3. 03

    iostat

  4. 04

    trace

  5. top / ps CPU 사용률

    run queue로 계산 대기인지 스레드 폭증인지 먼저 본다.

  6. vmstat free memory보다 si/so

    major fault, context switch 변화가 압박 신호다.

  7. iostat util, await, queue

    depth가 높으면 디스크 대기 때문에 CPU가 놀 수 있다.

  8. trace strace/perf

    system call 반복과 hot path를 확인하는 마지막 확대경이다.