HỌC BẰNG THỰC HÀNH
← Lộ trình học
01 · Linux Administration

Tiến trình, signal và độ ưu tiên

Thực hiện trên server của bạn, rồi kiểm tra từng tiêu chí.

Cốt lõi

Vì sao cần học

Dừng một dịch vụ "cho chắc" bằng kill -9 có thể làm hỏng dữ liệu đang ghi dở. Nhiều daemon nạp lại cấu hình khi nhận SIGHUP mà không cần restart, tránh mất kết nối của người dùng. Một job nặng có thể chạy tiếp mà không làm chậm dịch vụ chính nếu được hạ độ ưu tiên. Đây là những thao tác vận hành hằng ngày.

Mục tiêu

  • Tìm tiến trình theo tên, user, PID cha bằng ps, pgrep, top.
  • Phân biệt SIGTERM, SIGKILL, SIGHUP, SIGINT và dùng đúng lúc.
  • Đổi độ ưu tiên bằng nice và renice.
  • Đọc trạng thái tiến trình trong /proc/<PID>/.

Điều kiện tiên quyết

  • Bài Kiến trúc Linux, cây thư mục và link.

Mental model

Mỗi tiến trình có PID, PID cha (PPID), user, trạng thái và độ ưu tiên. Tiến trình mới được tạo bằng fork rồi exec; vì vậy mọi tiến trình đều có cha, gốc là PID 1 (systemd).

SignalSốÝ nghĩaBắt được?
SIGTERM15"Hãy dừng lại": cho tiến trình dọn dẹp rồi thoát. Mặc định của kill.Có
SIGINT2Ctrl+C trong terminalCó
SIGHUP1Ban đầu là "terminal đã đóng"; nhiều daemon dùng để nạp lại cấu hìnhCó
SIGKILL9Kernel dừng ngay, tiến trình không được dọn dẹpKhông
SIGSTOP / SIGCONT19 / 18Tạm dừng / chạy tiếpKhông / Có

Quy tắc: thử SIGTERM, đợi vài giây, chỉ dùng SIGKILL khi tiến trình không phản hồi.

nice từ -20 (ưu tiên cao nhất) đến 19 (thấp nhất), mặc định 0. User thường chỉ được tăng nice (hạ ưu tiên); chỉ root mới hạ được.

Thực hành (Guided Lab)

1. Chuẩn bị tiến trình

Lưu script vào ~/prepare-processes.sh, đọc qua, rồi chạy sudo bash ~/prepare-processes.sh.

#!/usr/bin/env bash
# KDC lab: processes. Tạo ba tiến trình mẫu.
set -euo pipefail
[[ $EUID -eq 0 ]] || { echo "Chạy bằng sudo." >&2; exit 1; }
grep -qs '^KDC-LAB' /etc/kdc-lab || { echo "Không thấy marker /etc/kdc-lab; script chỉ chạy trên máy lab." >&2; exit 1; }
pkill -x kdc-hog || true; pkill -x kdc-reload || true; pkill -x kdc-ticker || true
install -d -m 755 /opt/kdc-proc /var/lib/kdc-proc
rm -f /var/lib/kdc-proc/reloaded

cat > /opt/kdc-proc/kdc-hog <<'EOF'
#!/usr/bin/bash
trap 'echo "kdc-hog: bỏ qua SIGTERM"' TERM
while :; do sleep 1; done
EOF
cat > /opt/kdc-proc/kdc-reload <<'EOF'
#!/usr/bin/bash
trap 'date --iso-8601=seconds > /var/lib/kdc-proc/reloaded' HUP
while :; do sleep 1 & wait $!; done
EOF
cat > /opt/kdc-proc/kdc-ticker <<'EOF'
#!/usr/bin/bash
while :; do date >> /var/lib/kdc-proc/ticks; sleep 5; done
EOF
chmod 755 /opt/kdc-proc/kdc-*
for name in kdc-hog kdc-reload kdc-ticker; do
  setsid /opt/kdc-proc/$name >/dev/null 2>&1 < /dev/null &
done
sleep 1
ps -o pid,ppid,ni,user,comm -C kdc-hog,kdc-reload,kdc-ticker
Giải thích lệnh và tham số
Thành phần trong scriptÝ nghĩa
#!/usr/bin/env bashDòng shebang chọn Bash để chạy script; env tìm Bash qua PATH.
set -euo pipefail-e dừng khi lệnh thất bại ở những vị trí Bash áp dụng quy tắc này; -u báo lỗi khi đọc biến chưa đặt; -o pipefail làm pipeline báo lỗi nếu một lệnh bên trong lỗi. Các lệnh dùng làm điều kiện của if, &&, || có quy tắc ngoại lệ.
[[ $EUID -eq 0 ]]So sánh UID hiệu lực với 0 (root); -eq là so sánh số bằng nhau.
grep -qs '^KDC-LAB' /etc/kdc-lab-q chỉ kiểm tra có khớp, không in dòng; -s ẩn thông báo lỗi đọc file. ^ yêu cầu marker nằm đầu dòng.
lệnh || { ...; exit 1; }Chạy nhóm lệnh trong { ...; } khi lệnh trước thất bại; exit 1 kết thúc script với trạng thái lỗi.
pkill -x <tên>Gửi signal cho tiến trình có tên khớp toàn bộ; || true cho phép tiếp tục nếu chưa có tiến trình đó.
cat > <file> <<'EOF'Ghi nhiều dòng đến dòng EOF đứng riêng. Dấu nháy quanh 'EOF' giữ $biến/$(lệnh) trong nội dung chưa bị shell bên ngoài thay thế.
trap '...' TERM / HUPĐăng ký hành động khi Bash nhận signal TERM/HUP. Đây là hành vi script mẫu cố ý tạo.
while :; do ...; doneVòng lặp liên tục; : là lệnh không làm gì và trả thành công. sleep 1 chờ 1 giây.
lệnh &, $!, wait $!Chạy nền, lấy PID job nền gần nhất, rồi chờ job đó kết thúc.
setsidKhởi chạy lệnh trong một session mới; việc chạy nền và shell cha kết thúc làm các tiến trình mẫu được tiến trình quản lý nhận nuôi.
>/dev/null 2>&1 < /dev/nullBỏ đầu ra chuẩn, đưa đầu ra lỗi vào cùng nơi đó, và cấp đầu vào rỗng. Thứ tự 2>&1 sau > có ý nghĩa.

rm -f chỉ xóa file marker kết quả cũ. chmod 755 cho owner đọc/ghi/chạy, group và other đọc/chạy; kdc-* là mẫu tên được shell mở rộng thành các file khớp.

2. Quan sát

ps -ef | grep kdc-
pgrep -a kdc-
ps -o pid,ppid,ni,stat,user,etime,comm -C kdc-hog,kdc-reload,kdc-ticker
top -b -n 1 | head -15
cat /proc/$(pgrep -x kdc-ticker)/status | head -10
Giải thích lệnh và tham số
Lệnh / tùy chọnÝ nghĩa
ps -ef-e xem mọi tiến trình, -f dùng định dạng đầy đủ.
pgrep -a kdc-Tìm tên tiến trình chứa mẫu kdc-; -a in PID và dòng lệnh.
ps -o ... -C ...-o chọn cột, -C chọn tên executable; pid, ppid, ni, stat, user, etime, comm lần lượt là PID, PID cha, nice, trạng thái, user, thời gian đã chạy, tên tiến trình.
top -b -n 1-b in dạng batch thay vì giao diện tương tác; -n 1 chỉ lấy một lần cập nhật.
$(pgrep -x kdc-ticker)Chạy pgrep lấy PID rồi thay kết quả vào đường dẫn /proc/<PID>/status.

head -15/head -10 chỉ giới hạn số dòng để đọc. Không suy ra chương trình cha chỉ từ tên: đọc cột PPID để xem quan hệ thực tế.

Sau khi script cha kết thúc, các tiến trình mẫu thường được PID 1 nhận nuôi (hoặc một subreaper nếu môi trường có cấu hình). setsid tạo session mới; bản thân nó không tự đổi PPID thành 1. Dùng cột PPID để kiểm chứng trên VM của bạn.

3. Gửi signal đúng cách

sudo kill -TERM $(pgrep -x kdc-hog)
sleep 2; pgrep -a kdc-hog
Giải thích lệnh và tham số

kill nhận PID, không nhận tên tiến trình. -TERM yêu cầu dừng có xử lý; $(pgrep -x kdc-hog) cung cấp PID theo tên chính xác. sleep 2 chờ 2 giây trước khi xem lại. Các lệnh tiếp theo dùng -KILL để dừng ngay và -HUP để kích hoạt handler nạp lại của script mẫu; không phải mọi chương trình đều dùng HUP để reload.

kdc-hog bắt SIGTERM và bỏ qua nó. Đây là lúc dùng SIGKILL:

sudo kill -KILL $(pgrep -x kdc-hog)
Giải thích lệnh và tham số

kill -KILL <PID> gửi SIGKILL để dừng ngay; tiến trình không thể bắt hoặc bỏ qua signal này. $(pgrep -x kdc-hog) lấy PID có tên khớp chính xác (-x) rồi truyền vào kill.

kdc-reload mô phỏng một daemon nạp lại cấu hình khi nhận SIGHUP:

sudo kill -HUP $(pgrep -x kdc-reload)
cat /var/lib/kdc-proc/reloaded
pgrep -a kdc-reload
Giải thích lệnh và tham số

kill -HUP <PID> gửi SIGHUP; script mẫu dùng signal này để nạp lại cấu hình. pgrep -x khớp tên chính xác; pgrep -a in thêm dòng lệnh của các tiến trình tìm được. cat đọc file marker xác nhận.

Tiến trình vẫn sống, cấu hình đã được nạp lại. Nếu bạn gửi SIGTERM nhầm, chạy lại script chuẩn bị.

4. Hạ độ ưu tiên

sudo renice -n 10 -p $(pgrep -x kdc-ticker)
ps -o pid,ni,comm -C kdc-ticker
Giải thích lệnh và tham số

renice đổi nice của tiến trình đã chạy: -n 10 đặt nice bằng 10 trên môi trường lab thông thường, -p chọn PID. ps -o pid,ni,comm cho thấy cột NI phải là 10. nice -n 15 <lệnh> dùng mức điều chỉnh nice khi khởi chạy một lệnh mới.

Trong ví dụ tar -czf /tmp/etc.tar.gz /etc, tar đóng gói file: -c tạo archive, -z nén gzip, -f chỉ định file archive đứng sau nó. Ví dụ này có thể cần quyền đọc /etc; chỉ dùng trên VM lab.

Khởi động một lệnh mới với độ ưu tiên thấp: nice -n 15 tar -czf /tmp/etc.tar.gz /etc.

Tự kiểm tra (Verification)

pgrep -a kdc-
ps -o pid,ni,comm -C kdc-ticker
cat /var/lib/kdc-proc/reloaded
Giải thích lệnh và tham số

pgrep -a in PID và dòng lệnh khớp mẫu kdc-. ps -o pid,ni,comm chọn cột PID, nice và tên tiến trình; -C kdc-ticker lọc theo tên. cat in marker kết quả.

Chọn server và bấm Kiểm tra kết quả.

Lỗi thường gặp

  • kill -9 ngay lập tức: database và ứng dụng có thể để lại file khóa, dữ liệu ghi dở.
  • kill tiến trình con trong khi tiến trình cha (hoặc systemd) khởi động lại nó ngay. Với dịch vụ systemd, dùng systemctl stop, systemctl kill hoặc systemctl reload.
  • pkill kdc khớp với mọi tiến trình có chuỗi kdc trong tên, kể cả agent. Dùng pgrep -a xem trước, và -x để khớp chính xác.

Ghi chú production

  • systemd gửi SIGTERM, đợi TimeoutStopSec (mặc định 90 giây), rồi mới SIGKILL. Kubernetes làm tương tự với terminationGracePeriodSeconds. Ứng dụng tốt phải xử lý SIGTERM để dừng gọn.
  • Với dịch vụ systemd, đặt Nice= hoặc giới hạn CPU bằng CPUQuota= trong unit thay vì renice bằng tay; bài Điều tra CPU và bộ nhớ sẽ dùng cách này.

Dọn dẹp

sudo pkill -x kdc-reload; sudo pkill -x kdc-ticker
sudo rm -rf /opt/kdc-proc /var/lib/kdc-proc
Giải thích lệnh và tham số

pkill -x <tên> gửi SIGTERM tới tiến trình có tên khớp chính xác; khác pgrep chỉ tìm PID. Dấu ; phân tách các lệnh chạy lần lượt. rm -f xóa file và không hỏi xác nhận; -r thêm xóa đệ quy thư mục cùng nội dung, nên -rf gộp hai tùy chọn này.

Tài liệu chính thức