1. 프로세스 개념
프로세스란?
프로세스는 실행 중인 프로그램의 인스턴스다. 프로그램(디스크의 실행 파일)이 메모리에 로드되어 CPU에 의해 실행되면 프로세스가 된다. 각 프로세스는 독립된 가상 주소 공간, 파일 디스크립터, 환경 변수, 시그널 핸들러 등을 갖는다.
PID, PPID
| 용어 | 설명 |
|---|---|
| PID (Process ID) | 프로세스 고유 식별 번호. 커널이 할당하며, 1부터 시작 |
| PPID (Parent PID) | 부모 프로세스의 PID. 모든 프로세스는 부모가 있다 |
| PID 1 | init / systemd. 커널이 부팅 시 최초로 생성하는 사용자 공간 프로세스. 모든 프로세스의 최상위 조상 |
| PID 0 | 커널 자체(swapper/idle). 사용자 공간에서는 보이지 않는다 |
# 현재 셸의 PID
echo $$
# 부모 프로세스 PID
echo $PPID
# 마지막 백그라운드 프로세스 PID
echo $!프로세스 상태 (Process States)
| 상태 | 코드 | 설명 |
|---|---|---|
| Running | R | 실행 중 또는 실행 가능(run queue에서 대기) |
| Sleeping (Interruptible) | S | 이벤트 대기 중 (I/O, 시그널 등). 시그널로 깨울 수 있음 |
| Sleeping (Uninterruptible) | D | 디스크 I/O 등 대기 중. 시그널로 깨울 수 없음. 보통 매우 짧은 시간 |
| Stopped | T | 중지됨 (SIGSTOP 또는 Ctrl+Z). SIGCONT로 재개 가능 |
| Zombie | Z | 종료되었지만 부모가 아직 wait()하지 않은 상태. 프로세스 테이블 엔트리만 남아 있음 |
ps aux 출력의 STAT 열 추가 문자:
+ 포그라운드 프로세스 그룹
s 세션 리더
l 멀티스레드
< 높은 우선순위 (nice < 0)
N 낮은 우선순위 (nice > 0)
프로세스 트리
모든 프로세스는 fork() 시스템 콜로 생성된다. 부모 프로세스가 자식 프로세스를 생성하므로 트리 구조를 형성한다.
systemd (PID 1)
├── sshd
│ └── sshd
│ └── bash
│ └── vim
├── cron
├── nginx
│ ├── nginx (worker)
│ └── nginx (worker)
└── systemd-journald
# 프로세스 트리 확인
pstree
pstree -p # PID 표시
pstree -u # 사용자 전환 표시
pstree -a # 명령줄 인자 표시
pstree user # 특정 사용자의 프로세스 트리2. 프로세스 확인
ps - 프로세스 상태 스냅샷
BSD 스타일 (가장 많이 사용)
ps aux
# USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
# root 1 0.0 0.1 168936 11532 ? Ss Jan15 0:08 /sbin/init
# root 2 0.0 0.0 0 0 ? S Jan15 0:00 [kthreadd]
# user 1234 1.2 2.5 412356 98765 pts/0 Sl+ 10:30 0:15 python app.py| 열 | 설명 |
|---|---|
| USER | 프로세스 소유자 |
| PID | 프로세스 ID |
| %CPU | CPU 사용률 |
| %MEM | 메모리 사용률 |
| VSZ | 가상 메모리 크기 (KB) |
| RSS | 실제 사용 물리 메모리 (KB, Resident Set Size) |
| TTY | 연결된 터미널 (?: 터미널 없음) |
| STAT | 프로세스 상태 |
| START | 시작 시간 |
| TIME | 누적 CPU 사용 시간 |
| COMMAND | 실행 명령 |
System V 스타일
ps -ef
# UID PID PPID C STIME TTY TIME CMD
# root 1 0 0 Jan15 ? 00:00:08 /sbin/init
# user 1234 1100 1 10:30 pts/0 00:00:15 python app.py커스텀 포맷 (-o)
# 원하는 열만 선택
ps -eo pid,ppid,user,%cpu,%mem,stat,cmd
ps -eo pid,ppid,ni,pri,stat,cmd # nice값, 우선순위 포함
# 정렬
ps -eo pid,%cpu,%mem,cmd --sort=-%cpu # CPU 사용률 내림차순
ps -eo pid,%cpu,%mem,cmd --sort=-%mem # 메모리 사용률 내림차순
# 특정 프로세스
ps -p 1234 # PID로 조회
ps -u username # 특정 사용자의 프로세스
ps -C nginx # 명령 이름으로 조회
# 스레드 표시
ps -eLf # 모든 스레드 포함
ps -p 1234 -T # 특정 프로세스의 스레드top - 실시간 프로세스 모니터링
top화면 구성:
top - 10:30:45 up 15 days, 3:22, 2 users, load average: 0.52, 0.38, 0.25
Tasks: 312 total, 1 running, 310 sleeping, 0 stopped, 1 zombie
%Cpu(s): 5.2 us, 1.3 sy, 0.0 ni, 93.0 id, 0.3 wa, 0.0 hi, 0.2 si, 0.0 st
MiB Mem : 16384.0 total, 8192.0 free, 4096.0 used, 4096.0 buff/cache
MiB Swap: 4096.0 total, 4096.0 free, 0.0 used. 11264.0 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1234 user 20 0 412356 98765 12345 S 5.2 0.6 0:15.30 python
| 약어 | 설명 |
|---|---|
us | 사용자 공간 CPU |
sy | 커널 공간 CPU |
ni | nice로 변경된 프로세스 |
id | idle (유휴) |
wa | I/O 대기 |
hi | 하드웨어 인터럽트 |
si | 소프트웨어 인터럽트 |
st | steal time (가상화 환경) |
인터랙티브 명령:
| 키 | 동작 |
|---|---|
h | 도움말 |
q | 종료 |
Space | 즉시 갱신 |
k | 프로세스 종료 (kill) |
r | nice 값 변경 (renice) |
M | 메모리 사용률 순 정렬 |
P | CPU 사용률 순 정렬 (기본) |
T | CPU 시간 순 정렬 |
N | PID 순 정렬 |
1 | 각 CPU 코어별 사용률 표시 토글 |
c | 전체 명령줄 표시 토글 |
H | 스레드 표시 토글 |
V | 트리 형태 표시 |
u | 특정 사용자 필터 |
d / s | 갱신 간격 변경 |
W | 현재 설정 저장 (~/.toprc) |
# 배치 모드 (스크립트에서 사용)
top -bn1 # 1회만 출력
top -bn1 | head -20 # 상위 프로세스만
# 특정 프로세스만 모니터링
top -p 1234,5678
# 갱신 간격 지정
top -d 0.5 # 0.5초마다 갱신htop - 향상된 실시간 모니터링
top의 개선 버전. 색상, 스크롤, 마우스 지원, 트리 뷰 등.
htop
# F1: 도움말 F2: 설정 F3: 검색 F4: 필터
# F5: 트리 뷰 F6: 정렬 F9: 시그널 F10: 종료
# 설치
# Debian/Ubuntu: sudo apt install htop
# Red Hat/CentOS: sudo yum install htoppgrep - 패턴으로 PID 검색
pgrep nginx # nginx 프로세스 PID 목록
pgrep -u root # root 사용자 프로세스
pgrep -f "python app.py" # 전체 명령줄에서 매칭 (-f: full)
pgrep -l nginx # PID와 프로세스 이름 함께
pgrep -a nginx # PID와 전체 명령줄
pgrep -c nginx # 매칭 프로세스 수
pgrep -n nginx # 가장 최근 시작된 PID
pgrep -o nginx # 가장 오래된 PID3. 프로세스 제어 (시그널)
시그널 개요
시그널은 프로세스에 비동기적으로 전달되는 소프트웨어 인터럽트다. 프로세스 간 통신이나 커널에서 프로세스로 이벤트를 알리는 데 사용된다.
주요 시그널
| 번호 | 이름 | 기본 동작 | 설명 | 처리 가능 |
|---|---|---|---|---|
| 1 | SIGHUP | 종료 | 터미널 연결 끊김. 데몬의 설정 재로드에도 사용 | O |
| 2 | SIGINT | 종료 | 키보드 인터럽트 (Ctrl+C) | O |
| 3 | SIGQUIT | 코어 덤프 + 종료 | 키보드 종료 (Ctrl+\) | O |
| 9 | SIGKILL | 종료 | 강제 종료. 무시/처리 불가. 커널이 직접 처리 | X |
| 15 | SIGTERM | 종료 | 정상 종료 요청 (기본 시그널). 프로세스가 정리 후 종료 가능 | O |
| 18 | SIGCONT | 재개 | 정지된 프로세스 재개 | O |
| 19 | SIGSTOP | 정지 | 프로세스 정지. 무시/처리 불가. | X |
| 20 | SIGTSTP | 정지 | 터미널 정지 (Ctrl+Z). SIGSTOP과 달리 처리 가능 | O |
| 10 | SIGUSR1 | 종료 | 사용자 정의 시그널 1 | O |
| 12 | SIGUSR2 | 종료 | 사용자 정의 시그널 2 | O |
| 11 | SIGSEGV | 코어 덤프 + 종료 | 세그멘테이션 폴트 (잘못된 메모리 접근) | O |
| 13 | SIGPIPE | 종료 | 끊어진 파이프에 쓰기 | O |
| 14 | SIGALRM | 종료 | alarm() 타이머 만료 | O |
| 17 | SIGCHLD | 무시 | 자식 프로세스 종료/정지 시 부모에게 전달 | O |
시그널 번호는 아키텍처에 따라 다를 수 있다. 위 번호는 x86_64 Linux 기준.
kill - 시그널 전송
kill PID # SIGTERM(15) 전송 (기본)
kill -15 PID # SIGTERM 명시적
kill -SIGTERM PID # 이름으로 지정
kill -9 PID # SIGKILL - 강제 종료 (최후의 수단)
kill -KILL PID # 위와 동일
kill -1 PID # SIGHUP - 설정 재로드 (데몬)
kill -0 PID # 프로세스 존재 여부 확인 (시그널 전송 안 함)
# 여러 프로세스
kill 1234 5678 9012
# 시그널 목록
kill -l
# 1) SIGHUP 2) SIGINT 3) SIGQUIT ... 15) SIGTERM ...
# 프로세스 그룹 전체에 시그널 (음수 PID)
kill -TERM -1234 # PGID 1234 전체종료 순서 (권장):
kill PID(SIGTERM) - 프로세스에 정상 종료 요청- 잠시 대기 (프로세스가 정리 작업 수행)
- 여전히 살아있으면
kill -9 PID(SIGKILL) - 강제 종료
kill -9를 바로 사용하면 프로세스가 임시 파일 정리, 데이터 저장 등의 정리 작업을 수행할 수 없다. 항상 SIGTERM을 먼저 시도하라.
killall - 이름으로 종료
killall nginx # 'nginx'라는 이름의 모든 프로세스에 SIGTERM
killall -9 nginx # 강제 종료
killall -u user # 특정 사용자의 모든 프로세스
killall -i nginx # 확인 후 종료
killall -w nginx # 모두 종료될 때까지 대기pkill - 패턴으로 종료
pgrep과 동일한 패턴 매칭으로 시그널 전송.
pkill nginx # 이름 패턴 매칭
pkill -f "python app.py" # 전체 명령줄 매칭
pkill -9 -f "zombie_script" # 강제 종료
pkill -u user # 특정 사용자 프로세스
pkill -t pts/0 # 특정 터미널 프로세스
pkill -P 1234 # 특정 PPID의 자식 프로세스4. 작업 제어 (Job Control)
개념
셸에서 실행하는 명령을 작업(job)이라 하며, 포그라운드(전면)와 백그라운드(후면)에서 실행할 수 있다.
기본 조작
| 조작 | 설명 |
|---|---|
command & | 백그라운드에서 실행 |
Ctrl+Z | 현재 포그라운드 작업을 정지(SIGTSTP) |
Ctrl+C | 현재 포그라운드 작업에 인터럽트(SIGINT) - 보통 종료 |
jobs | 현재 셸의 작업 목록 |
fg | 가장 최근 백그라운드/정지 작업을 포그라운드로 |
fg %N | 작업 번호 N을 포그라운드로 |
bg | 가장 최근 정지된 작업을 백그라운드에서 재개 |
bg %N | 작업 번호 N을 백그라운드에서 재개 |
실전 예시
# 백그라운드 실행
sleep 300 &
# [1] 12345 <- 작업번호 1, PID 12345
# 작업 목록 확인
jobs
# [1]+ Running sleep 300 &
# [2]- Stopped vim file.txt
# jobs 옵션
jobs -l # PID도 표시
jobs -r # Running만
jobs -s # Stopped만
# 포그라운드로 가져오기
fg %1 # 작업 1을 포그라운드로
# 정지 후 백그라운드로
# (포그라운드에서 Ctrl+Z 누름)
# [1]+ Stopped python train.py
bg %1 # 백그라운드에서 계속 실행
# 작업 종료
kill %1 # 작업 번호로 종료disown - 셸로부터 작업 분리
셸 종료 시 SIGHUP이 전달되어 백그라운드 작업도 종료되는데, disown으로 이를 방지할 수 있다.
long_command &
disown # 가장 최근 백그라운드 작업을 작업 목록에서 제거
disown %1 # 특정 작업
disown -a # 모든 작업
# disown 후에는 jobs에서 보이지 않지만, 프로세스는 계속 실행
# 다만 stdout/stderr가 터미널에 연결되어 있으면 터미널 종료 시 에러 발생 가능
# -> nohup을 사용하는 것이 더 안전5. 우선순위: nice, renice
nice 값 개념
- 범위: -20 (최고 우선순위) ~ 19 (최저 우선순위)
- 기본값: 0
- 일반 사용자는 nice 값을 높이기(우선순위 낮추기)만 가능
- root만 nice 값을 낮출(우선순위 높이기) 수 있다
nice 값과 실제 우선순위(PR)의 관계:
PR = 20 + nice
nice -20 -> PR 0 (최고 우선순위)
nice 0 -> PR 20 (기본)
nice 19 -> PR 39 (최저 우선순위)
nice - 우선순위를 지정하여 프로세스 시작
nice command # nice 10으로 실행 (기본)
nice -n 15 command # nice 15로 실행
nice -n -5 command # nice -5로 실행 (root 필요)
nice -n 19 ./cpu_intensive_task.sh # 최저 우선순위로 실행
# 확인
ps -eo pid,ni,cmd | grep commandrenice - 실행 중인 프로세스 우선순위 변경
renice 10 -p 1234 # PID 1234의 nice를 10으로
renice -5 -p 1234 # nice를 -5로 (root 필요)
renice 15 -u username # 특정 사용자의 모든 프로세스
renice 10 -g groupname # 특정 그룹의 모든 프로세스
# top에서도 'r' 키로 renice 가능6. 백그라운드 실행
nohup - 셸 종료 후에도 실행 지속
SIGHUP을 무시하도록 하여, 터미널/SSH 세션 종료 후에도 프로세스가 계속 실행된다.
nohup command &
# nohup: ignoring input and appending output to 'nohup.out'
nohup command > output.log 2>&1 & # 출력을 지정 파일로
nohup python train.py > train.log 2>&1 &
# nohup은:
# 1. SIGHUP을 무시하도록 설정
# 2. stdout이 터미널이면 nohup.out으로 리다이렉트
# 3. stdin을 /dev/null로 리다이렉트nohup vs disown vs screen/tmux
| 도구 | SIGHUP 보호 | 출력 리다이렉트 | 다시 연결 가능 | 용도 |
|---|---|---|---|---|
nohup | O (시작 시) | nohup.out | X | 단순 백그라운드 실행 |
disown | O (실행 후) | X (수동 필요) | X | 이미 실행 중인 작업 분리 |
screen/tmux | O | O (가상 터미널) | O | 세션 유지, 다시 연결 |
screen/tmux 기본
# tmux (권장, 더 현대적)
tmux # 새 세션 시작
tmux new -s session_name # 이름 있는 세션
tmux ls # 세션 목록
tmux attach -t session_name # 세션에 다시 연결
tmux detach # 세션 분리 (Ctrl+B, d)
# tmux 내부 키 바인딩 (Ctrl+B가 prefix):
# Ctrl+B, d - detach (분리)
# Ctrl+B, c - 새 창
# Ctrl+B, n - 다음 창
# Ctrl+B, p - 이전 창
# Ctrl+B, % - 세로 분할
# Ctrl+B, " - 가로 분할
# Ctrl+B, 방향키 - 패인 이동
# screen
screen # 새 세션
screen -S name # 이름 있는 세션
screen -ls # 세션 목록
screen -r name # 세션에 다시 연결
# Ctrl+A, d - detach7. 데몬, 좀비, 고아 프로세스
데몬 프로세스 (Daemon)
터미널에 연결되지 않고 백그라운드에서 실행되는 시스템 서비스 프로세스. 관례적으로 이름 끝에 d가 붙는다.
# 데몬 예시: sshd, httpd, mysqld, crond, systemd
# 데몬의 특징:
# - 부모 프로세스가 보통 PID 1 (init/systemd)
# - 제어 터미널(TTY)이 없음 (ps 출력에서 '?')
# - 표준 입출력이 /dev/null이나 로그 파일로 리다이렉트
# - 보통 설정 파일 변경 후 SIGHUP으로 재로드
# systemctl로 데몬 관리 (systemd 기반)
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx
sudo systemctl reload nginx # 설정 재로드 (무중단)
sudo systemctl status nginx # 상태 확인
sudo systemctl enable nginx # 부팅 시 자동 시작
sudo systemctl disable nginx # 자동 시작 해제좀비 프로세스 (Zombie)
자식 프로세스가 종료되었지만, 부모 프로세스가 wait() 시스템 콜로 종료 상태를 회수하지 않은 프로세스.
# ps에서 'Z' 상태 또는 'defunct'로 표시
ps aux | grep 'Z'
# USER PID ... STAT ... COMMAND
# user 1234 ... Z ... [process_name] <defunct>
# 좀비 프로세스의 특징:
# - CPU나 메모리를 거의 사용하지 않음 (프로세스 테이블 엔트리만 차지)
# - kill -9로도 종료 불가 (이미 종료된 상태)
# - 부모 프로세스가 wait()하거나 종료되어야 사라짐
# 좀비 해결 방법:
# 1. 부모 프로세스에 SIGCHLD 전송 (wait() 유도)
kill -SIGCHLD PPID
# 2. 부모 프로세스 종료 (좀비가 init에 입양되어 자동 회수)
kill PPID
# 좀비 수 확인
ps aux | awk '$8 ~ /Z/ {count++} END {print count}'
# 또는 top 상단에서 확인: "0 zombie"고아 프로세스 (Orphan)
부모 프로세스가 자식보다 먼저 종료된 경우, 자식 프로세스는 고아가 된다. 커널이 자동으로 PID 1(init/systemd)을 새 부모로 재지정(re-parent)한다.
# 고아 프로세스는 문제가 되지 않음
# init/systemd가 자동으로 wait()하여 정리
# 확인: PPID가 1인 프로세스 (모두 고아는 아님, 정상 데몬도 포함)
ps -eo pid,ppid,cmd | awk '$2 == 1'8. /proc/[pid]/ 디렉토리 활용
각 프로세스는 /proc/PID/ 디렉토리에 다양한 런타임 정보를 노출한다.
주요 파일
PID=$$ # 현재 셸 PID (예시용)
# cmdline - 실행 명령줄 (인자를 NULL 문자로 구분)
cat /proc/$PID/cmdline | tr '\0' ' '
echo
# /bin/bash --login
# status - 프로세스 상태 요약 (사람이 읽기 좋은 형태)
cat /proc/$PID/status
# Name: bash
# Umask: 0022
# State: S (sleeping)
# Tgid: 1234 (Thread Group ID = PID)
# Pid: 1234
# PPid: 1100
# Uid: 1000 1000 1000 1000
# Gid: 1000 1000 1000 1000
# VmPeak: 12345 kB (가상 메모리 최대값)
# VmSize: 12000 kB (현재 가상 메모리)
# VmRSS: 5000 kB (물리 메모리)
# Threads: 1
# fd/ - 열린 파일 디스크립터
ls -la /proc/$PID/fd/
# lrwx------ 1 user user 0 ... 0 -> /dev/pts/0 (stdin)
# lrwx------ 1 user user 0 ... 1 -> /dev/pts/0 (stdout)
# lrwx------ 1 user user 0 ... 2 -> /dev/pts/0 (stderr)
# lr-x------ 1 user user 0 ... 3 -> /path/to/open/file
# l-wx------ 1 user user 0 ... 4 -> /path/to/log
# 열린 파일 디스크립터 수
ls /proc/$PID/fd/ | wc -l
# maps - 메모리 매핑 (가상 주소 공간 레이아웃)
cat /proc/$PID/maps
# 주소범위 권한 오프셋 장치 inode 경로
# 55a4b6c00000-55a4b6c28000 r-xp 00000000 08:01 1234 /bin/bash
# 7f8d12345000-7f8d12567000 r-xp 00000000 08:01 5678 /lib/x86_64-linux-gnu/libc-2.31.so
# 7ffd45600000-7ffd45621000 rw-p 00000000 00:00 0 [stack]
# 7ffd456fe000-7ffd45702000 r--p 00000000 00:00 0 [vvar]
# 7ffd45702000-7ffd45704000 r-xp 00000000 00:00 0 [vdso]
# environ - 환경 변수 (NULL 문자로 구분)
cat /proc/$PID/environ | tr '\0' '\n'
# HOME=/home/user
# PATH=/usr/local/bin:/usr/bin:/bin
# SHELL=/bin/bash
# ...
# exe - 실행 파일 심볼릭 링크
readlink /proc/$PID/exe
# /bin/bash
# cwd - 현재 작업 디렉토리 심볼릭 링크
readlink /proc/$PID/cwd
# io - I/O 통계
cat /proc/$PID/io
# rchar: 12345678 (읽은 바이트)
# wchar: 9876543 (쓴 바이트)
# read_bytes: 1234567 (실제 디스크 읽기)
# write_bytes: 987654 (실제 디스크 쓰기)
# limits - 리소스 제한
cat /proc/$PID/limits
# Max open files 1024 1048576 files
# Max processes 63338 63338 processes9. lsof, strace
lsof - 열린 파일 나열
List Open Files. Linux에서 “모든 것은 파일”이므로, 일반 파일뿐 아니라 소켓, 파이프, 장치 등도 포함한다.
# 모든 열린 파일 (매우 많음)
lsof
# 특정 프로세스
lsof -p 1234 # PID로
lsof -c nginx # 명령 이름으로
# 특정 사용자
lsof -u username
# 특정 파일을 사용하는 프로세스
lsof /var/log/syslog
# 특정 디렉토리 내 파일을 사용하는 프로세스
lsof +D /var/log/
# 네트워크 연결
lsof -i # 모든 네트워크 연결
lsof -i :80 # 포트 80 사용 프로세스
lsof -i :22 # SSH 포트
lsof -i TCP # TCP 연결만
lsof -i TCP:80 # TCP 포트 80
lsof -i @192.168.1.1 # 특정 IP와의 연결
# 삭제되었지만 아직 열려있는 파일 (디스크 공간 점유)
lsof +L1
# 이 파일들은 프로세스가 종료되거나 fd를 닫아야 디스크 공간이 해제됨
# 실전: 특정 포트를 점유한 프로세스 찾아서 종료
lsof -i :8080 | awk 'NR>1 {print $2}' | xargs kill
# 실전: 마운트 해제 불가 시 사용 중인 프로세스 확인
lsof +D /mnt/usb/strace - 시스템 콜 추적
프로세스가 호출하는 시스템 콜(커널 API)을 추적한다. 디버깅에 매우 유용하다.
# 명령어 실행과 함께 추적
strace ls /tmp
# execve("/bin/ls", ["ls", "/tmp"], ...) = 0
# openat(AT_FDCWD, "/tmp", O_RDONLY|O_NONBLOCK|O_DIRECTORY) = 3
# getdents64(3, ...) = 456
# write(1, "file1 file2 file3\n", 20) = 20
# close(3) = 0
# exit_group(0) = ?
# 실행 중인 프로세스에 연결
strace -p 1234
# 주요 옵션
strace -e trace=open,read,write ls # 특정 시스템 콜만 추적
strace -e trace=network command # 네트워크 관련만
strace -e trace=file command # 파일 관련만
strace -e trace=process command # 프로세스 관련만 (fork, exec, exit)
strace -c command # 시스템 콜 통계 요약
strace -f command # fork된 자식 프로세스도 추적
strace -t command # 타임스탬프 출력
strace -T command # 각 시스템 콜 소요 시간
strace -o output.txt command # 출력을 파일로
# 실전: 프로그램이 어떤 파일을 열려고 하는지 확인
strace -e trace=openat command 2>&1 | grep -v ENOENT
# 실전: 프로그램이 멈춘 원인 파악
strace -p $(pgrep stuck_process) 2>&1 | head -2010. 리소스 제한: ulimit
셸과 그 자식 프로세스의 리소스 사용 제한을 설정한다.
확인
ulimit -a # 모든 제한 값 확인 (soft limit)
ulimit -Ha # 하드 리밋 확인
# 주요 항목별 확인
ulimit -n # 열 수 있는 파일 수 (open files)
ulimit -u # 최대 프로세스 수
ulimit -s # 스택 크기 (KB)
ulimit -v # 가상 메모리 (KB)
ulimit -f # 파일 크기 제한 (blocks)
ulimit -c # 코어 덤프 크기 (blocks, 0=비활성)Soft Limit vs Hard Limit
| 구분 | 설명 |
|---|---|
| Soft limit | 현재 적용되는 제한. 사용자가 hard limit 이하로 변경 가능 |
| Hard limit | soft limit의 상한선. root만 올릴 수 있다 |
# soft limit 확인/설정
ulimit -Sn # 열린 파일 수 soft limit
ulimit -Sn 4096 # soft limit 변경
# hard limit 확인/설정 (root 필요)
ulimit -Hn # hard limit
ulimit -Hn 65536 # hard limit 변경 (root)영구 설정
# /etc/security/limits.conf
# 형식: <domain> <type> <item> <value>
#
# * soft nofile 4096 # 모든 사용자 soft limit
# * hard nofile 65536 # 모든 사용자 hard limit
# root soft nofile 65536 # root soft limit
# @admin hard nproc 1024 # admin 그룹 hard limit
# user1 soft core unlimited # user1 코어 덤프 무제한
# /etc/security/limits.d/ 디렉토리에 개별 파일로도 설정 가능
# systemd 서비스의 제한은 유닛 파일에서 설정
# [Service]
# LimitNOFILE=65536
# LimitNPROC=4096실전 활용
# 코어 덤프 활성화 (디버깅용)
ulimit -c unlimited
./buggy_program
# 코어 파일 생성됨 -> gdb로 분석 가능
# 웹 서버 등에서 파일 디스크립터 부족 시
ulimit -n 65536
# 또는 영구 설정 (/etc/security/limits.conf)
# 포크 폭탄 방지 (프로세스 수 제한)
ulimit -u 256
# 포크 폭탄 예시 (절대 실행 금지!)
# :(){ :|:& };:
# 위 명령은 자기 자신을 무한 fork하여 시스템을 먹통으로 만든다
# ulimit -u 로 사전 방지 가능