Mini-challenge — mổ xẻ hai lệnh (cat vs curl) bằng strace
Diagnose và so sánh hai lệnh thực tế (cat một file lớn vs curl một URL) bằng strace: đếm, phân loại syscall theo nhóm và giải thích vì sao mỗi nhóm xuất hiện.
TL;DR: Bạn sẽ tự cầm strace -c mổ xẻ hai lệnh thật — cat một file lớn (nặng về I/O file) và curl một URL (nặng về mạng) — rồi trả lời ba câu: (1) mỗi lệnh gọi nhiều nhất những syscall nào và thuộc nhóm gì; (2) nhóm syscall nào đặc trưng cho từng lệnh; (3) syscall nào ngốn thời gian nhất và vì sao. Đây là bài tổng hợp cả module: bạn dùng kiến thức về ranh giới user/kernel, cơ chế syscall, và cách đọc strace để tự chẩn đoán một chương trình chỉ từ dấu vết syscall của nó — kỹ năng lõi khi debug service thật.
🎯 Đề bài
Bạn được giao hai lệnh và phải hiểu chúng "nói chuyện" với kernel thế nào, chỉ bằng strace — không đọc source của cat hay curl.
Chuẩn bị dữ liệu:
# Tao mot file ~50MB de co du lieu that
head -c 50000000 /dev/urandom > big.bin
Lệnh A — đọc file lớn:
cat big.bin > /dev/null
Lệnh B — tải một URL:
curl -s https://example.com -o /dev/null
Nhiệm vụ của bạn (làm trước khi đọc phần Lời giải):
- Chạy
strace -ccho từng lệnh, thu bảng tổng hợp. - Với mỗi lệnh: liệt kê 3–5 syscall bị gọi nhiều nhất và phân nhóm (file / memory / process / network).
- Chỉ ra nhóm đặc trưng phân biệt hai lệnh — vì sao
catkháccurlvề hình dạng syscall. - Tìm syscall ngốn thời gian nhất trong mỗi lệnh và giải thích vì sao nó đắt (nhớ khái niệm mode switch, chờ I/O, chờ mạng).
🔍 Phân tích I-P-O
| Mô tả | |
|---|---|
| Input | Hai lệnh: cat big.bin (đọc 50 MB) và curl (tải một trang web) |
| Process | Chạy mỗi lệnh dưới strace -c, đọc bảng, phân nhóm, xếp hạng |
| Output | Kết luận: nhóm syscall đặc trưng mỗi lệnh + syscall đắt nhất + lý do cơ chế |
Điều kiện để phân tích đúng:
- Chạy nhiều lần cho ổn định (lần đầu có thể chậm do cache lạnh, file chưa trong page cache).
- Nhớ
stracephóng đại thời gian (bài 04) — dùng để so tỉ lệ, không phải tốc độ tuyệt đối. - Với
curl, kết quả phụ thuộc mạng; nếu offline, thay bằng một URL nội bộ hoặc file local.
📦 Concept mapping
Bài này tổng hợp trực tiếp cả module:
| Kiến thức cần | Bài gốc | Dùng ở đây thế nào |
|---|---|---|
| User code phải xin kernel làm I/O | Bài 01 — Kernel vs user mode | Mọi dòng trace là một lần cat/curl vượt biên sang kernel |
| Cơ chế syscall + chi phí mode switch | Bài 02 — System call là gì | Giải thích vì sao nhiều read/write nhỏ thì đắt |
| Con đường vào kernel + chờ I/O | Bài 03 — Interrupt, trap & exception | recvfrom/poll block chờ interrupt "dữ liệu tới" từ card mạng |
Đọc và phân nhóm strace -c | Bài 04 — Đọc syscall bằng strace | Công cụ chính của bài |
▶️ Bắt đầu
Chạy và tự quan sát trước. Với lệnh A:
strace -c cat big.bin > /dev/null
Với lệnh B:
strace -c curl -s https://example.com -o /dev/null
Ghi lại bảng của mỗi lệnh. Tự trả lời bốn câu hỏi trong Đề bài trước khi mở Lời giải. Nếu curl không có sẵn, thử wget -q -O /dev/null https://example.com hoặc dùng getent hosts example.com để có một lệnh chạm mạng nhẹ hơn.
💡 Gợi ý
Gợi ý 1 — cách phân nhóm nhanh: đừng nhìn từng syscall lạ. Gộp theo bảng bốn nhóm ở bài 04: thấy read/write/openat/close → nhóm file; mmap/mprotect/brk → memory; socket/connect/recvfrom/sendto/poll → network; execve/clone/wait4 → process.
Gợi ý 2 — vì sao cat đầy read và write: cat đọc file theo từng khối rồi ghi ra. Kích thước khối quyết định số syscall: 50 MB đọc theo khối 128 KB cho khoảng vài trăm cặp read+write. Nếu bạn thấy con số lớn hơn nhiều, khối nhỏ hơn — mỗi khối là một mode switch.
Gợi ý 3 — vì sao curl có nhiều openat/mmap bất ngờ: phần lớn syscall của curl không phải mạng — mà là nạp thư viện (libcurl, libssl, libc) và đọc chứng chỉ TLS (openat hàng loạt file trong /etc/ssl/certs). Phần mạng thật (socket/connect/recvfrom) ít dòng hơn nhưng thường đắt thời gian nhất vì phải chờ.
Gợi ý 4 — syscall đắt nhất: nhìn cột % time, không phải cột calls. Syscall gọi nhiều chưa chắc tốn thời gian; syscall chờ (network, disk) tốn thời gian dù gọi ít.
✅ Lời giải
Dưới là bảng tiêu biểu (số cụ thể sẽ khác trên máy bạn — quan trọng là hình dạng).
Lệnh A — strace -c cat big.bin
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
52.10 0.014820 38 384 read
41.30 0.011750 30 383 write
2.80 0.000800 80 10 mmap
1.50 0.000420 42 10 openat
1.10 0.000310 31 10 close
0.60 0.000170 28 6 fstat
...
------ ----------- ----------- --------- --------- ----------------
100.00 0.028440 820 2 total
Phân tích:
- Nhóm đặc trưng: file / I/O.
read(384) vàwrite(383) áp đảo cả về số lần lẫn thời gian. Đây đúng bản chấtcat: đọc một khối rồi ghi một khối, lặp tới hết file. Số cặp ~384 với file 50 MB cho biết mỗi khối cỡ 128 KB (50MB / 384 ≈ 130KB). - Nhóm memory (
mmap,openat,close,fstat) chỉ vài lần: đây là phần khởi động — nạplibc, mở file, kiểm tra metadata. Không đáng kể. - Syscall đắt nhất:
read(52% thời gian). Nó đắt vì mỗi lần đọc một khối 128 KB phải chờ dữ liệu từ đĩa (nếu chưa trong page cache) — tiến trình bị cho ngủ, đánh thức bởi interrupt "đọc xong" từ ổ đĩa (nhớ bài 03).writegần ngang vì cũng chuyển 50 MB ra đích.
Nếu big.bin đã nằm sẵn trong page cache (chạy lần hai), read sẽ nhanh hơn nhiều vì không chạm đĩa — chỉ copy từ cache. Đây là bằng chứng trực tiếp rằng "chờ I/O" là phần đắt.
Lệnh B — strace -c curl -s https://example.com -o /dev/null
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
43.20 0.006100 610 10 recvfrom
21.50 0.003030 1010 3 connect
12.80 0.001800 180 10 2 poll
8.10 0.001140 14 79 18 openat
5.40 0.000760 9 82 mmap
3.20 0.000450 10 45 read
2.10 0.000300 12 25 close
...
------ ----------- ----------- --------- --------- ----------------
100.00 0.014100 410 21 total
Phân tích:
- Nhóm đặc trưng: network.
recvfrom(nhận dữ liệu từ server),connect(mở kết nối TCP),poll(chờ socket sẵn sàng) — bộ ba này không xuất hiện ởcat. Đây là dấu vân tay của một chương trình mạng. - Nhưng
openatbị gọi nhiều nhất về số lần (79), phần lớn là nạp hàng loạt shared library (libssl, libcrypto, libz...) và đọc CA bundle chứng chỉ. Nhiều dòngENOENT(18 errors) ở đây là bình thường — search path tìm cert/lib (nhớ pitfall bài 04), không phải lỗi. - Syscall đắt nhất:
recvfromvàconnect(chiếm gần 65% thời gian dù chỉ vài lần gọi). Chúng đắt vì phải chờ mạng:connectchờ bắt tay TCP (một vòng đi-về tới server),recvfromchờ gói dữ liệu tới. Trong lúc chờ, tiến trình ngủ và được đánh thức bởi interrupt từ card mạng khi gói đến (bài 03). Đây là ví dụ hoàn hảo cho gợi ý 4: ít lần gọi nhưng tốn thời gian nhất, vì chờ chứ không phải vì gọi nhiều.
So sánh hai lệnh
flowchart LR
subgraph cat["cat big.bin"]
A["read x384"] --> B["write x383"]
B --> C["Nhom: FILE<br/>Dat vi: cho dia"]
end
subgraph curl["curl URL"]
D["connect x3"] --> E["recvfrom x10"]
E --> F["Nhom: NETWORK<br/>Dat vi: cho mang"]
end| Trục | cat big.bin | curl URL |
|---|---|---|
| Nhóm áp đảo (số lần) | read / write (file) | openat (nạp lib/cert) + recvfrom (network) |
| Nhóm đặc trưng | File I/O | Network |
| Syscall gọi nhiều nhất | read | openat |
| Syscall đắt thời gian nhất | read (chờ đĩa) | recvfrom / connect (chờ mạng) |
| Vì sao đắt | Chờ dữ liệu từ đĩa; chuyển 50 MB | Chờ vòng đi-về mạng tới server |
Kết luận lõi: cột calls và cột % time kể hai câu chuyện khác nhau. openat bị gọi nhiều nhất trong curl nhưng rẻ (không chờ gì); recvfrom gọi ít nhưng đắt (chờ mạng). Người debug giỏi nhìn cả hai: số lần cao gợi ý anti-pattern (syscall thừa), thời gian cao gợi ý điểm chờ (I/O/mạng).
🎓 Mở rộng
Xem từng syscall thay vì tổng hợp: bỏ -c, thêm -T để thấy thời gian mỗi call:
strace -T -e trace=network curl -s https://example.com -o /dev/null
Bạn sẽ thấy dòng connect(...) = ... <0.08...> — con số trong <> là thời gian thật của cú bắt tay TCP, thường là phần lâu nhất.
So sánh khối đọc lớn vs nhỏ: dùng dd với block size khác nhau và đếm syscall:
strace -c dd if=big.bin of=/dev/null bs=1024 # khoi 1KB -> rat nhieu read
strace -c dd if=big.bin of=/dev/null bs=1048576 # khoi 1MB -> it read
Số read chênh nhau khoảng một nghìn lần (1 MB / 1 KB = 1024) cho cùng một file — minh hoạ sống động bài học "gom syscall lớn" của bài 02.
Đếm theo nhóm process: thử strace -f -e trace=%process bash -c 'ls | wc -l' để thấy clone/execve/wait4 khi shell tạo tiến trình con và nối pipe — hé lộ module tiếp theo về vòng đời tiến trình.
✨ Điều bạn vừa làm được
Bạn vừa tự chẩn đoán hai chương trình chỉ từ dấu vết syscall — không đọc một dòng source nào. Hãy nhìn lại những gì bạn kết nối:
- Ranh giới user/kernel: mỗi dòng trace là một lần chương trình vượt biên nhờ kernel làm việc (bài 01).
- Cơ chế và chi phí syscall: hiểu vì sao khối đọc nhỏ sinh nhiều mode switch và tốn thời gian (bài 02).
- Chờ = interrupt: biết
recvfrom/readblock là tiến trình đang ngủ, chờ interrupt phần cứng đánh thức (bài 03). - Phân nhóm và xếp hạng: đọc
strace -c, phân biệt "gọi nhiều" với "tốn thời gian" (bài 04).
Kỹ năng này — nhìn một chương trình lạ, chạy strace, và nói được nó đang làm gì / kẹt ở đâu — là thứ phân biệt người hiểu hệ điều hành với người chỉ biết chương trình "chạy" hay "không chạy".
Bài tiếp theo: Tổng kết module — Kernel & System Call
Bài này có giúp bạn hiểu bản chất không?
Hỏi đáp về bài này
Chưa có câu hỏi
Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).
Đặt câu hỏi đầu tiên