Chương 06 — Process & Job control
Mỗi lệnh bạn gõ là một process. Chương này dạy bạn nhìn thấy chúng, điều khiển chúng, dừng chúng đúng cách — và chạy nhiều việc song song mà không cần mở hàng tá tab Terminal.
1. Process là gì?
Khi bạn gõ ls, hệ điều hành tạo ra một process để chạy chương trình ls.
Mỗi process có một số định danh duy nhất gọi là PID (Process ID).
Khi process kết thúc, PID đó được giải phóng và có thể tái sử dụng cho process khác.
Parent & Child — quan hệ cây
Process không tự nhiên xuất hiện. Một process được sinh ra bởi một parent process thông qua hai bước cốt lõi của Unix:
fork()— parent tự nhân bản mình thành 2 bản giống nhau (parent + child).exec()— child thay thế bộ nhớ của mình bằng chương trình mới (vd:ls).
Vì vậy mọi process đều có PPID (Parent PID). Toàn hệ thống là một cây process,
gốc là launchd (macOS) hoặc systemd (Linux) với PID = 1.
Foreground vs Background
| Loại | Đặc điểm | Ví dụ |
|---|---|---|
| Foreground | Chiếm terminal, bạn phải đợi nó xong mới gõ tiếp được | ls, vim file.txt |
| Background | Chạy ngầm, terminal trả prompt ngay để gõ lệnh khác | sleep 100 & |
zsh (hoặc bash).
Mọi lệnh bạn gõ trong tab đó đều là child của shell này. Đóng tab → shell chết → mọi child cũng chết theo (trừ khi bạn dùng nohup / disown).
2. ps — list process
Lệnh ps in ra danh sách process đang chạy. Nó có nhiều phong cách flag (BSD vs SysV) hơi rối, nhưng 2 cách phổ biến nhất:
$ ps # chỉ process của shell hiện tại — ngắn
$ ps aux # BSD style — TẤT CẢ process của mọi user (phổ biến nhất)
$ ps -ef # SysV style — tương tự, hiển thị PPID rõ hơn
Đọc cột của ps aux
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
huy 523 2.1 0.4 4789012 65432 ?? S Mon09AM 0:12.45 /Applications/Firefox.app/...
huy 1042 0.0 0.1 408765 1234 s001 S+ 10:33AM 0:00.04 -zsh
huy 2891 88.7 3.2 1234567 524288 ?? R 11:02AM 2:14.33 node server.js
| Cột | Ý nghĩa |
|---|---|
USER | Chủ process |
PID | Process ID — dùng để kill |
%CPU | % CPU đang dùng (snapshot) |
%MEM | % RAM đang dùng |
STAT | Trạng thái: R=Running, S=Sleeping, Z=Zombie, T=Stopped |
COMMAND | Lệnh đầy đủ đã chạy |
Kết hợp với grep — siêu hữu ích
$ ps aux | grep node # tìm mọi process có chữ "node"
$ ps aux | grep -i chrome # không phân biệt hoa thường
$ ps aux | grep -v grep # bỏ đi dòng grep tự match chính nó
Mánh kinh điển: ps aux | grep [n]ode — dấu ngoặc [] khiến chính grep không match chính nó (vì pattern [n]ode không khớp với chuỗi [n]ode trong dòng grep).
3. top, htop — xem realtime
ps chỉ là snapshot 1 thời điểm. Để xem realtime, dùng top (có sẵn) hoặc htop (đẹp hơn nhiều, phải cài).
top — có sẵn trên macOS
$ top # mở giao diện, q để thoát
$ top -o cpu # sort theo CPU (macOS)
$ top -o mem # sort theo memory
Trong top đang chạy, gõ phím:
- P — sort theo CPU
- M — sort theo Memory
- q — quit
htop — phiên bản đẹp & tương tác
$ brew install htop # cài 1 lần
$ htop # mở
htop có màu, scroll được, click chuột được, kill process bằng phím F9. Một khi đã thử là không muốn quay lại top.
So sánh ngắn
| Tiêu chí | top | htop |
|---|---|---|
| Có sẵn | Có (mặc định) | Phải brew install |
| Màu sắc | Đơn sắc | Có màu rõ ràng |
| Scroll danh sách | Không | Có (mũi tên / chuột) |
| Kill process | Gõ k rồi PID | F9 chọn signal |
| Dễ đọc cho beginner | Trung bình | Tốt hơn nhiều |
Đọc 3 dòng đầu của top
Processes: 512 total, 3 running, 509 sleeping, 2451 threads
Load Avg: 1.85, 2.12, 1.94 CPU usage: 12.4% user, 5.3% sys, 82.3% idle
PhysMem: 14G used (3G wired), 2G unused.
- Load Avg 1.85, 2.12, 1.94 = trung bình 1, 5, 15 phút. Trên máy 8 core, nếu vượt 8 nghĩa là đang nghẽn.
- CPU usage — phần trăm tổng.
idlecàng cao càng nhàn. - PhysMem — RAM thật đang dùng.
Usecase thật: server bỗng nhiên chậm → htop để xem process nào ngốn CPU/RAM, load avg có cao không. Đây là phản xạ debug đầu tiên.
4. kill — gửi signal
kill không chỉ "giết". Tên đầy đủ là send signal — gửi tín hiệu cho process. Tùy signal, process sẽ phản ứng khác nhau.
$ kill 2891 # gửi SIGTERM (mặc định) — yêu cầu thoát lịch sự
$ kill -15 2891 # tương đương: -15 = TERM
$ kill -9 2891 # SIGKILL — bắt buộc chết, không cứu vãn
$ kill -l # liệt kê mọi signal
Bảng signal phổ biến
| Số | Tên | Ý nghĩa | Process có "bắt" được không? |
|---|---|---|---|
1 | SIGHUP | Hangup — terminal đóng. Nhiều daemon dùng để reload config. | Có |
2 | SIGINT | Interrupt — gửi khi bạn bấm Ctrl-C | Có |
9 | SIGKILL | Giết ngay lập tức, không thương lượng | KHÔNG |
15 | SIGTERM | Yêu cầu thoát lịch sự (mặc định của kill) | Có |
18 | SIGCONT | Tiếp tục process đang stop | Có |
19 | SIGSTOP | Tạm dừng process (như Ctrl-Z nhưng không chặn được) | KHÔNG |
kill -9 = súng lục chứ không phải dao mổSIGKILL giết process tức thời, không cho nó dọn dẹp: file đang ghi dở có thể hỏng, lock chưa giải phóng, transaction database chưa commit. Luôn thử kill PID (TERM) trước. Chỉ -9 khi process treo cứng và TERM không có tác dụng.
pkill & killall — kill theo tên
$ pkill node # kill mọi process có tên chứa "node"
$ pkill -f "npm run dev" # match cả command-line đầy đủ
$ killall Chrome # kill mọi process tên CHÍNH XÁC là Chrome
$ killall -9 node # force kill
pkill vs killall — khác nhau ở đâu?pkill match một phần tên (substring) — pkill node giết cả node-server. killall đòi tên khớp chính xác. Trên macOS, killall còn dùng để restart service hệ thống: killall Finder, killall Dock.
Workflow điển hình: app treo cần kill
$ ps aux | grep node # 1. tìm PID
huy 2891 88.7 3.2 ... node server.js
$ kill 2891 # 2. thử lịch sự trước
$ ps aux | grep 2891 # 3. còn không?
$ kill -9 2891 # 4. nếu còn, mới ép
5. Job control trong shell
Shell (zsh, bash) cho bạn quản lý nhiều job trong cùng một tab terminal. Đây là trick năng suất rất lớn.
Các thao tác cơ bản
| Thao tác | Phím / lệnh | Tác dụng |
|---|---|---|
| Chạy nền | cmd & | Khởi chạy trực tiếp ở background |
| Suspend | Ctrl-Z | Tạm dừng job đang foreground |
| List jobs | jobs | Xem danh sách job của shell hiện tại |
| Background | bg %1 | Cho job đang stop chạy tiếp ở nền |
| Foreground | fg %1 | Mang job về foreground |
| Kill job | kill %1 | Giết job số 1 (không cần PID) |
Kịch bản đầy đủ
$ sleep 100 & # chạy ngầm ngay từ đầu
[1] 4521
$ sleep 200 # chạy foreground — terminal bị "khóa"
^Z # bấm Ctrl-Z → suspend
[2]+ Stopped sleep 200
$ jobs
[1]- Running sleep 100 &
[2]+ Stopped sleep 200
$ bg %2 # đẩy job 2 vào nền chạy tiếp
$ jobs
[1]- Running sleep 100 &
[2]+ Running sleep 200 &
$ fg %1 # kéo job 1 lên foreground
$ kill %2 # giết job 2 — dùng %N thay PID
%1, %2 là job number, KHÔNG phải PIDjobs -l.
6. nohup, disown — chạy lâu, đóng terminal vẫn sống
Mặc định, khi bạn đóng terminal, shell gửi SIGHUP cho mọi child → chúng chết. Vấn đề: bạn muốn chạy build dài 2 tiếng, không lẽ phải bật terminal cả đêm?
nohup — "no hangup"
$ nohup ./long-build.sh &
[1] 6123
nohup: ignoring input and appending output to 'nohup.out'
$ exit # đóng terminal — process vẫn sống tiếp
nohup bọc lệnh sao cho nó bỏ qua SIGHUP. Output được redirect vào file nohup.out (cùng folder).
disown — đã chạy rồi mới quyết định "thả"
$ ./long-build.sh & # đã chạy nền nhưng chưa nohup
[1] 6789
$ disown -h %1 # tách job 1 ra, không nhận SIGHUP nữa
$ exit # đóng terminal — job vẫn sống
Khi nào dùng cái nào?
| Tình huống | Dùng |
|---|---|
| Biết trước sẽ chạy lâu, muốn redirect output sạch | nohup cmd & |
| Đã lỡ chạy, giờ mới muốn đóng terminal | disown -h %1 |
| Cần tương tác trở lại sau khi đóng & mở terminal mới | tmux (Chương 9) |
tmuxnohup & disown là giải pháp cổ điển. Nhưng usecase thực sự — chạy server, theo dõi log, gắn lại session từ máy khác — thì tmux mạnh hơn rất nhiều. Bạn sẽ học chi tiết ở Chương 9 (Tmux & SSH).
7. Thử ngay trong terminal giả lập
Một số kịch bản nên gõ tay qua một lượt:
- Xem process đang chạy:
ps aux | head - Tìm process tên "node":
ps aux | grep node - Khởi động sleep nền:
sleep 60 & - Liệt kê job:
jobs - Mang về foreground:
fg %1 - Suspend bằng Ctrl-Z, sau đó
bg %1 - Giết job:
kill %1
8. Bài tập về nhà
Trên Terminal thật, làm tuần tự:
- Chạy
ps auxrồi đếm có bao nhiêu process tổng cộng (gợi ý: pipe sangwc -l). - Tìm 5 process tốn CPU nhất bằng
ps aux | sort -k3 -nr | head -5. Đọc cột%CPUvàCOMMAND. - Mở
tophoặchtop, nhận diện process đang ngốn CPU/RAM nhiều nhất, ghi PID lại, rồikill PID(chọn process vô hại, vd một tab Chrome bạn không cần). - Chạy
sleep 300 &3 lần liên tiếp. Gõjobs— bạn có 3 job. Dùngkill %2để giết job số 2. - Chạy
sleep 200ở foreground. Bấm Ctrl-Z. Gõjobs, sau đóbg %1. Cuối cùngfg %1để mang trở lại. - Mở 1 tab terminal mới, chạy
nohup sleep 600 &, đóng tab. Mở tab khác, dùngps aux | grep sleepđể kiểm tra processsleep 600còn sống. Sau đókillnó. - Phân biệt: gửi
kill -15vàkill -9cho 2 lệnhsleep 100 &khác nhau. Xem có khác biệt gì không (gợi ý: cả hai đều chết — nhưng với app phức tạp, khác biệt rất lớn). - Dùng
pkill -f sleepđể giết mọi process có "sleep" trong command line. So vớikillall sleep, kết quả có khác? - Bonus: viết 1 dòng tìm và in ra PID của process Firefox (nếu có):
ps aux | grep -i firefox | grep -v grep | awk '{print $2}'— đoán xem mỗi đoạn làm gì.
9. Quiz — Kiểm tra kiến thức
10. Tổng kết
- ✅ Mỗi process có PID; sinh ra qua
fork+exectừ parent. - ✅ Liệt kê:
ps aux,ps aux | grep name, realtime vớitop/htop. - ✅ Đọc
%CPU,%MEM,STATđể chẩn đoán. - ✅
kill PID= TERM (lịch sự) — luôn thử trước.kill -9= KILL (ép) — chỉ khi treo cứng. - ✅ Job control:
&, Ctrl-Z,jobs,bg,fg,kill %N. - ✅ Chạy lâu:
nohup cmd &hoặcdisown -h %1; usecase thật →tmux. - ✅
pkillmatch substring;killallmatch chính xác tên.
Chương 7 ta sẽ học Shell scripting — gói mọi mẹo CLI bạn vừa học vào file .sh tự động chạy: vòng lặp, biến, hàm, điều kiện. Đây là bước biến bạn từ "user CLI" thành "tự viết tool".