Giao diện
Process Management & Signals — Quản Lý Tiến Trình Linux
Bạn deploy một bản cập nhật lúc 4 giờ chiều. 20 phút sau, monitoring báo memory tăng liên tục. Bạn SSH vào server, chạy htop, thấy hàng trăm process <defunct> đang nuốt tài nguyên. Đồng nghiệp hét qua Slack: "Kill -9 hết đi!". Bạn biết đó là sai — nhưng bạn có biết tại sao nó sai không?
Bài này dạy bạn hiểu process từ gốc: process là gì, kernel quản lý chúng ra sao, signal hoạt động thế nào, và tại sao kill -9 thường gây hại nhiều hơn lợi. Đây là kiến thức nền tảng mà mọi kỹ sư phải nắm vững trước khi đụng đến systemd, Docker, hay Kubernetes.
🎯 Mục tiêu
- Hiểu process, PID, PPID và mối quan hệ parent-child trong process tree
- Nắm vững 6 signal quan trọng nhất: SIGTERM, SIGKILL, SIGHUP, SIGINT, SIGSTOP, SIGCONT
- Biết dùng
ps,top,htopvà/procfilesystem để inspect process - Phân tích và xử lý được zombie process incident trong production
- Tránh anti-pattern phổ biến nhất: kill -9 reflex
Process — Đơn Vị Thực Thi Của Linux
Mỗi chương trình đang chạy trên Linux là một process (tiến trình). Khi bạn gõ python myapp.py, kernel tạo ra một process mới với:
- PID (Process ID) — số định danh duy nhất trong hệ thống tại thời điểm đó
- PPID (Parent Process ID) — PID của process cha đã tạo ra nó
- UID — user nào sở hữu process này
- State — trạng thái hiện tại (Running, Sleeping, Zombie, Stopped...)
Process tree — cây gia phả của mọi tiến trình
Toàn bộ process trên Linux đều bắt nguồn từ PID 1 — tiến trình đầu tiên mà kernel khởi động (trước đây là init, ngày nay là systemd). Mọi process khác đều là con, cháu, chắt... của PID 1.
systemd (PID 1)
├── sshd (PID 500)
│ └── bash (PID 1200)
│ └── python myapp.py (PID 1201)
├── nginx (PID 600)
│ ├── nginx worker (PID 601)
│ └── nginx worker (PID 602)
└── postgres (PID 700)
├── postgres writer (PID 701)
└── postgres worker (PID 702)Điểm then chốt: khi một parent process bị kill, kernel phải quyết định số phận của tất cả child process. Đây chính là gốc rễ của nhiều production incident — và cũng là lý do systemd quan trọng (bài sau).
Xem process tree và thông tin chi tiết
bash
# Xem process tree dạng cây — thấy ngay quan hệ parent-child
pstree -p
# systemd(1)─┬─sshd(500)───bash(1200)───python(1201)
# ├─nginx(600)─┬─nginx(601)
# │ └─nginx(602)
# └─postgres(700)─┬─postgres(701)
# └─postgres(702)
# List tất cả process với chi tiết (user, CPU, memory, command)
ps aux | head -20
# USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
# root 1 0.0 0.3 169396 13200 ? Ss 09:00 0:02 /usr/lib/systemd/systemd
# root 500 0.0 0.1 15432 5400 ? Ss 09:00 0:00 /usr/sbin/sshd -D
# www-data 600 0.0 0.2 55124 8640 ? S 09:00 0:01 nginx: master process
# deploy 1201 2.3 1.5 312400 61200 ? Sl 14:00 0:45 python myapp.py
# Tìm process cụ thể
ps aux | grep nginx
# www-data 600 0.0 0.2 55124 8640 ? S 09:00 0:01 nginx: master process
# www-data 601 0.1 0.3 56200 12800 ? S 09:00 0:12 nginx: worker process
# www-data 602 0.1 0.3 56100 12600 ? S 09:00 0:11 nginx: worker process
# Real-time monitoring
top # có sẵn trên mọi Linux — đủ dùng cho troubleshooting nhanh
htop # giao diện đẹp hơn, dễ dùng hơn (cài: sudo apt install htop)Đọc thông tin process từ /proc
Mỗi process có một thư mục riêng trong /proc/PID/. Đây là "cửa sổ" mà kernel mở cho bạn nhìn vào bên trong process:
bash
# Thông tin tổng quan: tên, state, memory, threads
cat /proc/1201/status | head -10
# Name: python
# Umask: 0022
# State: S (sleeping)
# Tgid: 1201
# Pid: 1201
# PPid: 1200
# Threads: 4
# VmRSS: 61200 kB
# Full command line đã dùng để khởi chạy process
cat /proc/1201/cmdline | tr '\0' ' '
# python myapp.py --port 8080 --workers 4
# Danh sách file descriptor đang mở (socket, file, pipe)
ls -la /proc/1201/fd/ | head -10
# lrwx------ 1 deploy deploy 64 ... 0 -> /dev/pts/0
# lrwx------ 1 deploy deploy 64 ... 1 -> /dev/pts/0
# lrwx------ 1 deploy deploy 64 ... 2 -> /dev/pts/0
# lr-x------ 1 deploy deploy 64 ... 3 -> /home/deploy/myapp/config.yaml
# lrwx------ 1 deploy deploy 64 ... 4 -> socket:[45678]💡 Pro Tip — Khi nào dùng ps vs top vs htop
ps aux — snapshot tĩnh, dùng khi cần pipe qua grep, awk, hoặc lưu vào log. top — real-time, có sẵn mọi nơi, dùng khi SSH vào server lạ. htop — real-time với giao diện tốt hơn, dùng khi có quyền cài package. Trong production incident, bắt đầu với top vì nó luôn có sẵn.
Signals — Ngôn Ngữ Giao Tiếp Với Process
Signal là cách kernel và các process "nói chuyện" với nhau. Khi bạn nhấn Ctrl+C trong terminal, bạn đang gửi signal SIGINT tới process đang chạy. Khi bạn gõ kill PID, bạn đang gửi signal SIGTERM.
Hiểu signal là hiểu cách Linux dừng, tạm dừng, và reload process — kỹ năng sống còn khi vận hành server.
6 signal mà mọi kỹ sư phải thuộc
| Signal | Số | Hành vi | Use Case |
|---|---|---|---|
SIGTERM | 15 | Graceful shutdown | Mặc định khi gõ kill PID — process có cơ hội dọn dẹp |
SIGKILL | 9 | Force kill ngay lập tức | Phương án cuối cùng — process KHÔNG THỂ bắt hoặc bỏ qua signal này |
SIGHUP | 1 | Reload config | Nginx, HAProxy, syslog reload mà không cần restart |
SIGINT | 2 | Interrupt | Ctrl+C trong terminal |
SIGSTOP | 19 | Pause (đóng băng) process | Tạm dừng để debug — process KHÔNG THỂ bỏ qua signal này |
SIGCONT | 18 | Resume process | Tiếp tục chạy sau khi bị SIGSTOP |
SIGTERM vs SIGKILL — mental model quan trọng nhất
Đây là sự khác biệt mà rất nhiều kỹ sư hiểu sai:
SIGTERM (kill PID):
Process nhận signal
→ Chạy cleanup handlers (atexit, signal handlers)
→ Đóng kết nối database đang mở
→ Flush log buffer ra file
→ Trả HTTP response cho in-flight requests
→ Giải phóng lock files
→ Exit gracefully
SIGKILL (kill -9 PID):
Kernel GIẾT process NGAY LẬP TỨC
→ Không chạy cleanup handlers
→ Kết nối DB bị bỏ rơi (connection leak)
→ Log chưa flush bị mất
→ In-flight requests mất tín hiệu
→ Lock files không được xóa
→ Temp files nằm lại trên disk
→ CÓ THỂ gây data corruption🔥 KHÔNG BAO GIỜ dùng kill -9 làm bước đầu tiên
Luôn bắt đầu với kill PID (SIGTERM). Đợi 10-30 giây. Kiểm tra process còn sống không bằng ps -p PID. Nếu vẫn không chết, lúc đó mới dùng kill -9 PID. Dùng kill -9 ngay từ đầu giống như rút phích điện để tắt máy tính — đôi khi bạn buộc phải làm, nhưng không bao giờ nên là lựa chọn đầu tiên.
Gửi signal trong thực tế
bash
# Bước 1: Graceful stop (SIGTERM — mặc định)
kill 1201
# Bước 2: Kiểm tra process còn sống không
ps -p 1201
# Nếu không có output → process đã thoát thành công
# Bước 3: CHỈ khi SIGTERM thất bại sau 30 giây
kill -9 1201
# Reload config nginx mà KHÔNG cần restart
# (SIGHUP → nginx đọc lại config, tạo worker mới, graceful shutdown worker cũ)
kill -HUP $(cat /var/run/nginx.pid)
# Hoặc dùng systemctl (cách chuẩn hơn):
systemctl reload nginx
# Tạm dừng process để debug
kill -STOP 1201 # Process đóng băng
# ... kiểm tra memory, connections, v.v. ...
kill -CONT 1201 # Process tiếp tục chạy
# Gửi signal tới tất cả process của một user
killall -u deploy -SIGTERM
# Hoặc theo tên process
pkill -f "python myapp.py"⚠️ Cạm bẫy
Sai lầm 1: Luôn dùng kill -9 vì "nhanh hơn"
bash
# ❌ Phản xạ nguy hiểm
kill -9 $(pgrep python)Hậu quả thật sự:
- Database transaction đang ghi dở → data corruption
- Connection pool không được trả lại → các process khác hết connection
- File lock không được giải phóng → process mới không start được
- PID file cũ còn nằm lại → systemd nghĩ service vẫn đang chạy
bash
# ✅ Workflow đúng
kill $(pgrep -f "python myapp.py")
sleep 15
if ps -p $(pgrep -f "python myapp.py") > /dev/null 2>&1; then
echo "SIGTERM failed, escalating to SIGKILL..."
kill -9 $(pgrep -f "python myapp.py")
fiSai lầm 2: Không hiểu tại sao graceful shutdown quan trọng
Khi deploy phiên bản mới, orchestrator (systemd, Docker, K8s) gửi SIGTERM cho process cũ. Nếu app không handle SIGTERM → in-flight request bị cắt ngang → user thấy lỗi 502. Kỹ sư giỏi viết signal handler cho app của mình:
python
# Python — handle SIGTERM gracefully
import signal
import sys
def graceful_shutdown(signum, frame):
print("Received SIGTERM, cleaning up...")
# Đóng DB connections
db.close()
# Flush logs
logger.flush()
# Hoàn thành in-flight requests (trong web framework)
server.shutdown(timeout=30)
sys.exit(0)
signal.signal(signal.SIGTERM, graceful_shutdown)/proc — Cửa Sổ Nhìn Vào Process
/proc không phải thư mục thông thường trên disk — nó là virtual filesystem mà kernel tạo ra trong RAM. Mỗi file trong /proc là một "giao diện" để bạn đọc thông tin trực tiếp từ kernel mà không cần cài bất kỳ tool nào.
Đây là X-ray machine của system administrator.
Thông tin hệ thống
bash
# CPU — model, cores, tốc độ
cat /proc/cpuinfo | grep "model name" | head -1
# model name : Intel(R) Xeon(R) CPU E5-2686 v4 @ 2.30GHz
# Số CPU cores
grep -c "processor" /proc/cpuinfo
# 4
# Memory — tổng, free, buffers, cached
cat /proc/meminfo | head -5
# MemTotal: 8167848 kB
# MemFree: 524288 kB
# MemAvailable: 4096000 kB
# Buffers: 312400 kB
# Cached: 3200000 kB
# Load average — system đang bận cỡ nào
cat /proc/loadavg
# 0.45 0.38 0.32 1/245 1201
# (1 min) (5 min) (15 min) (running/total) (last PID)
# Uptime (giây)
cat /proc/uptime
# 864000.45 3200000.12
# (system uptime) (idle time across all cores)Thông tin theo process
bash
# Tên, state, memory, threads của process cụ thể
cat /proc/1201/status | grep -E "^(Name|State|PPid|VmRSS|Threads)"
# Name: python
# State: S (sleeping)
# PPid: 1200
# VmRSS: 61200 kB ← physical memory thực tế đang dùng
# Threads: 4
# Biến môi trường của process (hữu ích khi debug config)
cat /proc/1201/environ | tr '\0' '\n' | head -5
# HOME=/home/deploy
# PATH=/usr/local/bin:/usr/bin:/bin
# DATABASE_URL=postgres://localhost/mydb
# WORKER_COUNT=4
# LOG_LEVEL=info
# File descriptor đang mở — xem process đang giữ những file/socket nào
ls -la /proc/1201/fd/ | wc -l
# 47 ← process đang mở 47 file descriptors
# Network connections của process
cat /proc/1201/net/tcp | head -5
# (output ở dạng hex — dùng ss hoặc netstat sẽ dễ đọc hơn)
# Readable hơn:
ss -tnp | grep 1201
# ESTAB 0 0 10.0.0.5:8080 10.0.0.100:54321 users:(("python",pid=1201,fd=4))💡 Dùng /proc thay monitoring tool
Dùng /proc/PID/status để kiểm tra memory usage của process mà KHÔNG cần cài monitoring tool. VmRSS (Resident Set Size) cho biết actual physical memory đang dùng — đây là con số bạn quan tâm nhất khi debug memory issue. VmSize là virtual memory (thường lớn hơn nhiều nhưng ít ý nghĩa hơn).
🔥 Production Incident — "Zombie Processes Ăn Hết Resource"
🔥 Real Production Incident
Tình huống
Thứ Tư, 14:30. Monitoring alert: server memory 95%. API response time tăng từ 200ms lên 3 giây. Team SSH vào server.
Điều tra
bash
# Bước 1: Kiểm tra top processes
htop
# → Không có process nào dùng nhiều CPU hay memory bất thường
# → Nhưng total process count: 847 (bình thường: ~250)
# Bước 2: Tìm thủ phạm
ps aux | grep defunct
# deploy 1234 0.0 0.0 0 0 ? Z 14:00 0:00 [worker] <defunct>
# deploy 1235 0.0 0.0 0 0 ? Z 14:01 0:00 [worker] <defunct>
# deploy 1236 0.0 0.0 0 0 ? Z 14:01 0:00 [worker] <defunct>
# ... hàng trăm dòng tương tự
# State 'Z' = Zombie
# Bước 3: Đếm zombie
ps aux | awk '$8 ~ /Z/' | wc -l
# 583Phản xạ sai (rất phổ biến)
bash
# ❌ Đồng nghiệp Junior chạy:
kill -9 1234 1235 1236 ...
# Kết quả: Không có gì xảy ra. Zombie vẫn còn.
# 5 phút sau: Thêm zombie mới xuất hiện.Tại sao kill -9 không hoạt động?
Zombie process đã chết rồi. Chúng không còn code đang chạy, không chiếm CPU, không chiếm memory thật sự. Chúng chỉ là entry trong process table — một dòng metadata mà kernel giữ lại, chờ parent process gọi wait() để thu thập exit status.
Bạn không thể kill thứ đã chết. Đó là lý do kill -9 vô dụng với zombie.
Nguyên nhân gốc
bash
# Bước 4: Tìm parent process
ps -o ppid= -p 1234
# 1100
# Xem parent là gì
ps -p 1100 -o pid,ppid,user,command
# PID PPID USER COMMAND
# 1100 1 deploy python task_scheduler.py
# Bước 5: Đọc code task_scheduler.py
# → Parent dùng subprocess.Popen() để spawn worker
# → Nhưng KHÔNG BAO GIỜ gọi .wait() hoặc .communicate()
# → Child hoàn thành → trở thành zombie → parent tiếp tục spawn thêmRoot cause code
python
# ❌ Code gây zombie — thiếu wait()
import subprocess
def run_workers():
while True:
tasks = get_pending_tasks()
for task in tasks:
# Spawn worker nhưng KHÔNG chờ kết quả
subprocess.Popen(["python", "worker.py", task.id])
# → worker hoàn thành → trở thành zombie
time.sleep(60)Fix đúng
bash
# Bước 6: Fix ngay trên production — restart parent
kill 1100 # SIGTERM parent → systemd restart nó
# Parent mới khởi động → zombie cũ được kernel dọn (re-parent về PID 1)
# Kiểm tra zombie đã biến mất
sleep 5
ps aux | awk '$8 ~ /Z/' | wc -l
# 0 ✅python
# ✅ Code đúng — properly handle child process lifecycle
import subprocess
def run_workers():
while True:
tasks = get_pending_tasks()
processes = []
for task in tasks:
proc = subprocess.Popen(["python", "worker.py", task.id])
processes.append(proc)
# Chờ TẤT CẢ workers hoàn thành
for proc in processes:
proc.wait() # Thu thập exit status → không còn zombie
time.sleep(60)
# Hoặc đơn giản hơn — dùng subprocess.run() (blocking, tự wait)
def run_worker_simple(task_id):
result = subprocess.run(
["python", "worker.py", task_id],
capture_output=True,
timeout=300, # 5 phút timeout
)
if result.returncode != 0:
logger.error(f"Worker failed: {result.stderr}")Bài học rút ra
- Zombie ≠ process đang chạy. Chúng là metadata chờ parent thu thập —
kill -9vô dụng. - Fix zombie = fix parent, không phải fix child.
- Luôn
wait()trên child process — trong Python, Go, Node, hay bất kỳ ngôn ngữ nào. - Nếu parent không fix được ngay, restart parent để kernel re-parent zombie về PID 1 (systemd sẽ dọn).
Background Processes & Job Control
Khi bạn chạy lệnh qua SSH, process đó gắn với terminal session của bạn. Nếu bạn đóng SSH → process chết. Hiểu job control giúp bạn chạy task dài mà không sợ mất khi mất kết nối.
bash
# Chạy process ở background (thêm & ở cuối)
python process_reports.py &
# [1] 1500 ← job number 1, PID 1500
# Liệt kê background jobs
jobs -l
# [1]+ 1500 Running python process_reports.py &
# Đưa job về foreground
fg %1
# Ctrl+Z → pause process (gửi SIGTSTP)
# Sau đó đưa nó ra background tiếp tục chạy
bg %1
# Chạy task dài mà không sợ mất khi đóng SSH
nohup python long_task.py > output.log 2>&1 &
# nohup: bỏ qua SIGHUP (signal gửi khi terminal đóng)
# > output.log: redirect stdout
# 2>&1: redirect stderr vào stdout
# &: chạy background
# Kiểm tra task đang chạy sau khi reconnect SSH
ps aux | grep long_task.py
# deploy 1600 5.2 2.1 ... python long_task.py
tail -f output.log # theo dõi output real-time💡 Screen/Tmux — giải pháp tốt hơn nohup
Trong thực tế production, dùng screen hoặc tmux thay vì nohup. Chúng cho phép bạn attach lại vào session sau khi reconnect SSH — xem output, gửi input, thậm chí chia terminal. Cài đặt: sudo apt install tmux. Cheat sheet: tmux new -s deploy → chạy lệnh → Ctrl+B, D để detach → tmux attach -t deploy để quay lại.
Bài tập nhanh
Bài 1: Đọc ps output — tìm zombie
Cho output sau:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.3 169396 13200 ? Ss 09:00 0:02 /usr/lib/systemd/systemd
deploy 2100 2.3 1.5 312400 61200 ? Sl 14:00 0:45 python task_scheduler.py
deploy 2201 0.0 0.0 0 0 ? Z 14:05 0:00 [worker] <defunct>
deploy 2202 0.0 0.0 0 0 ? Z 14:06 0:00 [worker] <defunct>
deploy 2203 0.0 0.1 42000 4200 ? S 14:07 0:02 python worker.py task-99
www-data 3000 0.1 0.2 55124 8640 ? S 09:00 0:01 nginx: master process
www-data 3001 0.5 0.4 58000 16400 ? S 09:00 0:30 nginx: worker processCâu hỏi:
- Process nào là zombie? → PID 2201 và 2202 (STAT =
Z, hiển thị<defunct>) - Parent của zombie là ai? → Chạy
ps -o ppid= -p 2201— nhiều khả năng là PID 2100 (task_scheduler.py) kill -9 2201có hiệu quả không? → Không. Zombie đã chết, phải fix parent.- Cách xử lý đúng? →
kill 2100(SIGTERM parent) → systemd restart → zombie được dọn.
Bài 2: Chọn đúng signal
| Tình huống | Signal đúng |
|---|---|
| Reload config nginx mà không gián đoạn traffic | SIGHUP (kill -HUP <nginx_pid>) |
| Dừng một Python script đang stuck trong infinite loop | SIGTERM (kill <pid>), đợi 30s, rồi SIGKILL nếu cần |
| Force-kill process hoàn toàn không phản hồi SIGTERM | SIGKILL (kill -9 <pid>) — last resort |
| Tạm dừng process để kiểm tra memory snapshot | SIGSTOP (kill -STOP <pid>), sau đó SIGCONT để tiếp tục |
Spot the Bug 🐛
Đoạn script deploy sau có lỗi gì?
bash
#!/bin/bash
# deploy.sh — restart application
echo "Deploying new version..."
kill -9 $(cat /var/run/myapp.pid)
sleep 2
python /opt/myapp/main.py &
echo $! > /var/run/myapp.pid
echo "Deploy complete!"Bugs:
kill -9ngay lập tức — không cho app cơ hội dọn dẹp (đóng DB, flush log, hoàn thành request). Phải dùngkill(SIGTERM) trước.sleep 2quá ngắn — SIGTERM cần thời gian để app cleanup. Phải đợi đủ lâu (10-30s) và kiểm tra process thật sự đã thoát.- Không kiểm tra process đã thoát — nếu process cũ chưa chết mà process mới đã start → port conflict, hai instance chạy song song.
- Không dùng systemd — quản lý PID file thủ công rất dễ sai. Hãy dùng systemd service (bài sau).
Script đúng hơn:
bash
#!/bin/bash
set -euo pipefail
PID_FILE="/var/run/myapp.pid"
APP_PID=$(cat "$PID_FILE" 2>/dev/null || echo "")
if [ -n "$APP_PID" ] && ps -p "$APP_PID" > /dev/null 2>&1; then
echo "Sending SIGTERM to PID $APP_PID..."
kill "$APP_PID"
# Đợi tối đa 30 giây cho graceful shutdown
for i in $(seq 1 30); do
if ! ps -p "$APP_PID" > /dev/null 2>&1; then
echo "Process exited gracefully after ${i}s"
break
fi
sleep 1
done
# Escalate nếu vẫn chưa chết
if ps -p "$APP_PID" > /dev/null 2>&1; then
echo "SIGTERM failed, sending SIGKILL..."
kill -9 "$APP_PID"
sleep 1
fi
fi
echo "Starting new version..."
python /opt/myapp/main.py &
echo $! > "$PID_FILE"
echo "Deploy complete! PID: $(cat $PID_FILE)"Production Anti-Pattern — "The kill -9 Reflex"
⚠️ Cạm bẫy
Đây là anti-pattern phổ biến nhất trong quản lý process. Quy trình sai:
bash
# ❌ "Nó chậm quá, kill cho nhanh"
kill -9 $(pgrep -f "python myapp.py")Hậu quả thực tế:
- Database transaction đang ghi dở → data bị corrupt hoặc row bị lock vĩnh viễn
- Connection pool không được trả lại → app mới start bị "too many connections"
- In-flight API response mất → client nhận 502 Bad Gateway hoặc timeout
- Lock file
/var/run/myapp.pidkhông bị xóa → systemd nghĩ app còn chạy → không restart được - Temp file trên disk không bị dọn → disk đầy dần theo thời gian
Quy trình đúng — luôn là SIGTERM trước:
SIGTERM → đợi 30s → kiểm tra → (nếu vẫn sống) → SIGKILLTrong Kubernetes, đây chính xác là điều terminationGracePeriodSeconds làm. Default: 30 giây SIGTERM, sau đó mới SIGKILL. Nếu bạn hiểu signal, bạn hiểu K8s pod lifecycle.
Best Practices Checklist
✅ Checklist triển khai
Process Management:
- [ ] Biết phân biệt PID, PPID, và cách đọc process tree
- [ ] Dùng
ps auxcho snapshot,top/htopcho real-time monitoring - [ ] Biết đọc
/proc/PID/statusđể kiểm tra memory (VmRSS), state, threads - [ ] Kiểm tra file descriptors đang mở bằng
/proc/PID/fd/khi debug connection leak
Signal Handling:
- [ ] SIGTERM (graceful) → đợi → SIGKILL (force) — luôn theo thứ tự này
- [ ] Dùng SIGHUP cho reload config (nginx, HAProxy)
- [ ] Viết signal handler (SIGTERM) cho app của bạn — đừng phụ thuộc vào default behavior
- [ ] Không bao giờ
kill -9làm bước đầu tiên
Zombie Prevention:
- [ ] Luôn
wait()trên child process trong code (subprocess.run, child_process.exec) - [ ] Fix zombie bằng cách fix parent, không phải kill zombie
- [ ] Monitor process count — zombie tăng đột biến = bug trong parent
Job Control:
- [ ] Dùng
nohuphoặctmux/screencho task dài qua SSH - [ ] Không chạy production service bằng
&— dùng systemd (bài sau)
🧠 Quiz
Câu 1: Khi gõ kill 1234 (không có flag), signal nào được gửi?
- [ ] A. SIGKILL (9) — force kill ngay lập tức
- [x] B. SIGTERM (15) — yêu cầu graceful shutdown
- [ ] C. SIGHUP (1) — reload configuration
- [ ] D. SIGINT (2) — interrupt giống Ctrl+C
💡 Giải thích:
killmặc định gửi SIGTERM (15). Process nhận signal này có cơ hội chạy cleanup handlers trước khi thoát. Chỉ khi thêm-9mới gửi SIGKILL.
Câu 2: Zombie process là gì?
- [ ] A. Process chiếm quá nhiều CPU và memory
- [ ] B. Process đang chạy nhưng không phản hồi
- [x] C. Process đã kết thúc nhưng parent chưa gọi wait() để thu thập exit status
- [ ] D. Process bị kernel pause vì hết tài nguyên
💡 Giải thích: Zombie (state Z) là process ĐÃ CHẾT nhưng vẫn còn entry trong process table. Chúng chờ parent gọi wait().
kill -9không có tác dụng vì chúng đã chết rồi — fix bằng cách sửa hoặc restart parent.
Câu 3: Bạn muốn reload config nginx mà KHÔNG gián đoạn traffic. Cách nào đúng?
- [ ] A.
kill -9 $(cat /var/run/nginx.pid)rồi start lại - [ ] B.
systemctl stop nginx && systemctl start nginx - [x] C.
kill -HUP $(cat /var/run/nginx.pid)hoặcsystemctl reload nginx - [ ] D.
kill $(cat /var/run/nginx.pid)rồi start lại
💡 Giải thích: SIGHUP (signal 1) yêu cầu nginx đọc lại config file, tạo worker mới với config mới, và gracefully shutdown worker cũ. Không có downtime.
systemctl reloadcũng gửi SIGHUP bên dưới.
Câu 4: Thứ tự đúng khi cần dừng một process không phản hồi?
- [ ] A. SIGKILL → SIGTERM → SIGHUP
- [ ] B. SIGKILL ngay lập tức vì nhanh nhất
- [x] C. SIGTERM → đợi 10-30s → kiểm tra → SIGKILL nếu cần
- [ ] D. SIGHUP → SIGTERM → SIGKILL
💡 Giải thích: Luôn SIGTERM trước để cho process cơ hội cleanup. Đợi đủ thời gian. Chỉ escalate lên SIGKILL khi SIGTERM thất bại. Đây cũng là quy trình mà Kubernetes áp dụng (terminationGracePeriodSeconds).