시스템 모니터링과 디버깅
프로그램이 이상하게 동작한다고 할 때, 로그만으로 원인을 찾지 못한다면 OS 수준의 디버깅 도구가 필요합니다. 프로세스가 어떤 시스템 콜을 호출하는지, 커널이 무슨 메시지를 출력하는지, 메모리와 CPU를 어떻게 사용하는지를 추적하면 대부분의 문제를 진단할 수 있습니다.
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가 병목이라는 것을 알 수 있습니다.
/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를 소비하는 함수입니다.
다음 절에서는 커널 파라미터 튜닝과 OS 관련 기술 면접 주제를 정리하겠습니다.