Tiến Trình và Tín Hiệu

K
Kai··5 min read

Mọi thứ đang "chạy" trên Linux đều là một tiến trình (process). Bài này là deep-dive về tiến trình: cách xem chúng, cách chạy nền, và cách điều khiển/kết thúc bằng tín hiệu (signal) — bao gồm câu hỏi kinh điển: vì sao kill -9 khác kill thường.

Tiến trình và quan hệ cha-con

Mỗi tiến trình có một PID (Process ID) định danh, và một PPID (Parent PID) — tiến trình sinh ra nó. Mọi tiến trình tạo thành một cây, gốc là PID 1 (tiến trình init, được nhân khởi động đầu tiên; trong container thường là tiến trình bạn chạy).

Xem tiến trình của shell hiện tại:

ps
  PID TTY          TIME CMD
    1 ?        00:00:00 bash
    7 ?        00:00:00 ps

ps (không tham số) chỉ thấy tiến trình của shell hiện tại. Để thấy mọi tiến trình trên hệ thống, dùng một trong hai dạng phổ biến:

ps aux       # kiểu BSD: kèm %CPU, %MEM, user
ps -ef       # kiểu Unix: kèm PPID (cha)

ps -ef cho thấy cột PPID — hữu ích để lần cây cha-con:

UID    PID  PPID  CMD
root     8     1  sleep 300     ← PID 8, cha là PID 1

Xem tiến trình "sống": top

ps là ảnh chụp một thời điểm. Để xem động (CPU, RAM cập nhật liên tục):

top

top liệt kê tiến trình ngốn CPU/RAM nhiều nhất, làm mới mỗi vài giây (nhấn q để thoát). Khi server chậm, đây là lệnh đầu tiên để xem cái gì đang ngốn tài nguyên. htop (cần cài) là phiên bản đẹp và dễ dùng hơn.

Tìm nhanh tiến trình theo tên:

pgrep -a nginx        # liệt kê PID + dòng lệnh của tiến trình tên nginx
ps aux | grep nginx | grep -v grep   # cách thủ công (loại chính lệnh grep)

Chạy nền: &, jobs, fg, bg

Mặc định lệnh chạy ở foreground — chiếm terminal tới khi xong. Thêm & để chạy nền:

sleep 300 &
[1] 8        ← [số job] PID

Shell trả lại dấu nhắc ngay. Quản lý các job nền:

jobs        # liệt kê job nền của shell này
fg %1       # đưa job 1 lên foreground
bg %1       # cho job đang dừng chạy tiếp ở nền

Phím tắt liên quan: Ctrl + Z tạm dừng tiến trình foreground (đưa nó thành job dừng), rồi bg cho chạy nền hoặc fg đưa lại lên.

Job nền vẫn gắn với terminal — đóng terminal là chúng chết theo (nhận tín hiệu SIGHUP). Muốn chạy tiếp sau khi đăng xuất, dùng nohup lệnh & hoặc disown. Nhưng cho dịch vụ chạy lâu dài, cách đúng là dùng systemd (Bài 15), không phải nohup.

Tín hiệu: cách giao tiếp với tiến trình

Bạn điều khiển tiến trình bằng cách gửi tín hiệu (signal) — một thông điệp ngắn của nhân tới tiến trình. Tiến trình có thể "bắt" một số tín hiệu để xử lý gọn gàng (lưu dữ liệu, đóng file) trước khi thoát. Vài tín hiệu quan trọng:

Tín hiệu   Số   Ý nghĩa                          Bắt/bỏ qua được?
─────────────────────────────────────────────────────────────────
SIGINT     2    ngắt (chính là Ctrl+C)           có
SIGTERM    15   yêu cầu dừng lịch sự (mặc định)  có  ← nên dùng
SIGKILL    9    giết ngay lập tức                KHÔNG ← biện pháp cuối
SIGHUP     1    đóng terminal / reload cấu hình  có
SIGSTOP    19   tạm dừng (Ctrl+Z gửi SIGTSTP)    KHÔNG

Xem danh sách đầy đủ: kill -l.

kill: gửi tín hiệu

Dù tên là "kill", lệnh này thực ra gửi tín hiệu (mặc định là SIGTERM):

kill 8           # gửi SIGTERM (15) tới PID 8 — yêu cầu dừng lịch sự
kill -TERM 8     # tương đương, ghi rõ tín hiệu
kill -9 8        # gửi SIGKILL — giết ngay, không cho dọn dẹp
kill -HUP 8      # SIGHUP — nhiều dịch vụ hiểu là "đọc lại cấu hình"

Thử:

sleep 300 &      # giả sử PID 8
kill 8           # dừng nó
ps -ef | grep "sleep 300" | grep -v grep   # (không còn)

Vì sao kill -9 khác kill thường — và khi nào dùng

  • kill (SIGTERM) yêu cầu tiến trình tự dừng. Tiến trình bắt được tín hiệu này, nên nó kịp lưu dữ liệu, đóng kết nối, dọn dẹp rồi thoát sạch sẽ. Đây là cách nên dùng trước tiên.
  • kill -9 (SIGKILL) ép nhân giết tiến trình ngay lập tức. Tiến trình không bắt được SIGKILL, nên nó không kịp dọn dẹp — có thể để lại dữ liệu dở dang, file khóa, kết nối treo.

Quy tắc: thử kill (TERM) trước, cho tiến trình cơ hội thoát đẹp. Chỉ dùng kill -9 khi nó không chịu dừng sau TERM. Nhiều người quen tay kill -9 ngay — đó là thói quen xấu, dễ gây hỏng dữ liệu.

Giết theo tên thay vì PID:

pkill -f "sleep 300"     # giết tiến trình khớp dòng lệnh
killall sleep            # giết mọi tiến trình tên sleep

🧹 Dọn dẹp

pkill -f "sleep" 2>/dev/null   # dọn các tiến trình sleep còn sót từ thực hành

Tổng kết

Mọi thứ đang chạy là một tiến trình, có PIDPPID tạo thành cây từ PID 1. Xem bằng ps aux/ps -ef (ảnh chụp) hoặc top (động); tìm bằng pgrep. Chạy nền bằng &, quản lý bằng jobs/fg/bg. Điều khiển bằng tín hiệu: kill gửi SIGTERM (dừng lịch sự — nên dùng trước), kill -9 gửi SIGKILL (ép giết, không dọn dẹp — chỉ khi cần). SIGHUP thường để reload cấu hình.

Bạn đã nắm các nhóm kỹ năng cốt lõi nhất. Bài 9 quay lại việc thực dụng hằng ngày: nén và giải nén file với tar/gzip — để backup và di chuyển dữ liệu.