시스템 모니터링과 디버깅
strace·proc·dmesg·journalctl·코어 덤프·perf로 시스템 콜과 자원 사용을 추적해 장애 원인을 좁힙니다.
프로그램이 이상하게 동작한다고 할 때, 로그만으로 원인을 찾지 못한다면 OS 수준의 디버깅 도구가 필요합니다.
프로세스가 어떤 시스템 콜을 호출하는지, 커널이 무슨 메시지를 출력하는지, 메모리와 CPU를 어떻게 사용하는지를 추적하면 대부분의 문제를 진단할 수 있습니다.
시스템 콜, /proc 상태, 커널 로그, 코어 덤프, CPU 샘플을 같은 문제로 보지 말고 증상에 맞는 첫 도구를 고른다.
- 증상별 첫 확인 지점
파일을 못 찾거나 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 로 샘플링한다.
- 파일을 못 찾거나 DB 연결이 멈춘다
strace -e trace=file 이나 trace=network 로 실제 경로와 연결 대상, 에러 코드를 본다.
- 프로세스 상태와 자원 사용량이 궁금하다
/proc/[pid]/status , fd , maps , io 에서 스레드, 열린 파일, 메모리 맵을 확인한다.
- OOM, 디스크, 드라이버처럼 커널 이벤트가 의심된다
dmesg --level=err,warn 과 journalctl -p err 로 커널과 서비스 로그를 맞춰 본다.
- 세그먼테이션 폴트나 CPU 병목을 재현했다
코어 덤프는 gdb bt 로 콜 스택을 보고, 느린 구간은 perf record -g 로 샘플링한다.
- 도구가 말해 주는 증거
출력 예시 신호 해석 다음 확인 방향 open(...) = -1 ENOENT strace 파일 추적 경로 문제 설정 파일 위치, 상대 경로, 권한을 확인한다. socket:[12345] , VmRSS , read_bytes /proc 프로세스 정보 자원 사용 패턴 fd 누수, 메모리 증가, 실제 디스크 I/O를 분리한다. Out of memory: Kill process dmesg 커널 메시지 커널이 종료시킨 사건 메모리 제한, OOM score, 서비스 재시작 로그를 본다. 읽는 순서: 문제를 재현하고, 첫 도구로 관측한 증거를 로그와 맞춘 다음, 코드 수정이나 커널/서비스 설정 변경으로 이어간다.
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 출력 읽기
# 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 병목, 비정상 종료는 서로 다른 증거를 요구한다.
- 시스템 콜 경로
strace 파일 탐색 실패, 네트워크 타임아웃, 권한 문제를 호출 단위로 추적한다.
- 실시간 프로세스 상태
/proc 메모리, fd, cmdline, maps, io를 커널이 제공하는 현재값으로 읽는다.
- 커널 수준 이벤트
dmesg OOM Killer, 디스크 오류, 드라이버 실패처럼 애플리케이션 로그 밖의 원인을 본다.
- 코어 덤프
세그폴트 순간의 메모리와 콜 스택을 보존해 gdb로 원인 프레임을 찾는다.
- perf
CPU가 실제로 시간을 쓰는 함수와 커널 이벤트를 샘플링해 병목을 좁힌다.
/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 정보
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는 프로세스의 메모리 레이아웃을 보여줍니다.
코드 세그먼트, 힙, 스택, 공유 라이브러리의 가상 주소 범위와 권한을 확인할 수 있습니다.
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 -20journalctl (systemd 로그)
# 최근 부팅 이후 로그
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 이상 삭제전통적 로그 파일
# 시스템 로그
/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코어 덤프 분석
프로세스가 세그먼테이션 폴트로 비정상 종료되면 코어 덤프(Core Dump)가 생성될 수 있습니다.
코어 덤프는 프로세스가 죽는 순간의 메모리 스냅샷입니다.
# 코어 덤프 활성화
ulimit -c unlimited
# 코어 덤프 저장 경로 설정
echo "/var/crash/core.%e.%p.%t" > /proc/sys/kernel/core_pattern
# %e = 실행 파일 이름
# %p = PID
# %t = 타임스탬프# 코어 덤프 분석 (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가 어디서 시간을 소비하는지 정밀하게 측정합니다.
# 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_inputhash_lookup에서 45%의 시간을 소비하고 있다면, 해시 함수 최적화나 데이터 구조 변경을 검토합니다.
# 플레임 그래프 생성 (시각화)
perf record -g -p 1234 -- sleep 30
perf script | stackcollapse-perf.pl | flamegraph.pl > flamegraph.svg플레임 그래프는 콜 스택을 시각적으로 보여줍니다.
넓은 막대가 많은 CPU를 소비하는 함수입니다.
모든 도구를 한 번에 켜기보다, 실패한 경계가 파일인지 네트워크인지 CPU인지 먼저 가정하고 가장 직접적인 관측 도구부터 적용합니다.
- 1어디서 시간을 쓰는지 본다
느린 응답 어디서 시간을 쓰는지 본다 CPU 병목이면 perf, I/O 대기면 strace 통계와 /proc/io를 먼저 봅니다. 샘플링 perf record -g 시스템 콜 시간 strace -c
- 2프로세스가 실제로 요청한 경로를 추적
파일 또는 네트워크 실패 프로세스가 실제로 요청한 경로를 추적 설정 경로 오류, 권한 문제, 연결 타임아웃은 시스템 콜 결과에 직접 드러납니다. 파일 strace -e trace=file 네트워크 strace -e trace=network
- 3죽은 순간의 증거를 보존
비정상 종료 죽은 순간의 증거를 보존 코어 덤프와 커널 로그를 함께 보면 NULL 역참조, OOM, 디스크 오류를 구분할 수 있습니다. 콜 스택 gdb app core 커널 사건 dmesg --level=err
다음 절에서는 커널 파라미터 튜닝과 OS 관련 기술 면접 주제를 정리하겠습니다.
성능 진단은 CPU, 메모리, 디스크를 따로 보지 않습니다. run queue, major fault, iowait가 동시에 움직이는지 봐야 원인이 좁혀집니다.
- 01
top / ps
- 02
vmstat
- 03
iostat
- 04
trace
- top / ps CPU 사용률
run queue로 계산 대기인지 스레드 폭증인지 먼저 본다.
- vmstat free memory보다 si/so
major fault, context switch 변화가 압박 신호다.
- iostat util, await, queue
depth가 높으면 디스크 대기 때문에 CPU가 놀 수 있다.
- trace strace/perf
system call 반복과 hot path를 확인하는 마지막 확대경이다.