Ba con số us, sy, wa xuất hiện trong mọi công cụ đo Linux. Bài này đo xem mỗi loại tải chảy vào con số nào — và tìm ra một chỗ wa nói dối.
Ba loại tải, ba chỗ thời gian chảy vào
| Tải | us |
sy |
id |
wa |
b |
in |
cs |
|---|---|---|---|---|---|---|---|
| Nền | 5 | 2 | 93 | 0 | 0 | 16.339 | 24.446 |
| Tính toán thuần | 38 | 2 | 59 | 0 | 0 | 18.063 | 18.784 |
| Gọi hệ thống dày đặc | 13 | 26 | 61 | 0 | 0 | 14.931 | 13.762 |
| Đọc đĩa trực tiếp | 1 | 2 | 62 | 35 | 6 | 72.951 | 106.175 |
Ba dấu vết rõ rệt:
uscao — CPU chạy mã của bạn. Đây là tải "thật", và tối ưu hoá thuật toán sẽ giúp.sycao — CPU chạy trong nhân. Thường là quá nhiều lời gọi hệ thống nhỏ, hoặc mạng.wacao — CPU rảnh và đang chờ đĩa.
Cột in (số ngắt) ở dòng cuối đáng chú ý: 72.951 so với 16.339 lúc nền — gấp 4,5 lần. Mỗi lần một thao tác I/O hoàn tất là một ngắt. Đây là tín hiệu phụ tốt để xác nhận nghi ngờ về đĩa.
Nhưng iowait tụt xuống khi CPU bận
Đây là phép đo quan trọng nhất bài.
Tôi giữ nguyên tải đĩa và thêm 14 tiến trình tính toán:
| Tình huống | us |
sy |
id |
wa |
b |
|---|---|---|---|---|---|
| Chỉ tải đĩa | 5 | 3 | 56 | 35 | 6 |
| Tải đĩa + 14 tiến trình CPU | 88 | 3 | 5 | 4 | 6 |
| CPU hết, còn đĩa | 8 | 4 | 54 | 35 | 6 |
Cột b bằng 6 ở cả ba dòng — lượng I/O không đổi một chút nào.
Nhưng wa đi từ 35 xuống 4 rồi trở lại 35.
Vì sao: iowait là một phần của idle
iowait không phải "thời gian dành cho I/O". Nó là thời gian CPU không có gì để chạy, và có ít nhất một thao tác I/O đang treo.
Khi CPU có việc khác để làm, thời gian đó được tính vào us — và wa sụp xuống dù đĩa vẫn tắc nghẽn y hệt.
Hệ quả thực hành, và nó ngược với cách phần lớn người ta dùng con số này:
wathấp không chứng minh hệ thống không nghẽn đĩa.
Trên một máy chủ chạy đầy tải, wa gần như luôn thấp — không phải vì đĩa nhanh, mà vì CPU luôn có việc. Đúng lúc bạn cần nó nhất, chỉ số này im lặng.
Muốn chắc thì nhìn cột b — số tiến trình bị chặn ở I/O. Nó không phụ thuộc CPU có bận hay không. Hoặc nhìn thẳng độ trễ thiết bị bằng iostat -x, phần sau của sê-ri sẽ đo.
sy cao nghĩa là gì
Trong phép đo, 6 tiến trình dd if=/dev/zero of=/dev/null bs=1 đẩy sy lên 26%. Mỗi byte là hai lời gọi hệ thống — read và write — nên chúng sinh ra hàng triệu lời gọi mỗi giây.
Ba nguyên nhân thường gặp của sy cao trong ứng dụng thật:
- Đọc/ghi từng byte hoặc từng dòng thay vì theo lô. Đây là phiên bản Linux của bài toán "lượt đi về" trong sê-ri Redis.
- Nhiều kết nối mạng ngắn. Mỗi
accept,read,write,closelà một lời gọi. - Tranh chấp khoá làm nhiều lời gọi
futex.
Cách xác nhận rẻ nhất:
strace -c -p <pid> -f
Nó đếm số lời gọi theo loại. Một hàng đứng đầu với hàng triệu lời gọi là câu trả lời.
Các cột ít được nhắc
vmstat và /proc/stat còn vài cột nữa, và hai cái đáng biết:
st (steal) — thời gian CPU ảo bị nhà cung cấp lấy đi để phục vụ máy ảo khác. Trên máy vật lý nó luôn 0. Trên đám mây, st khác 0 kéo dài nghĩa là bạn đang chia sẻ CPU với hàng xóm ồn ào, và không có gì trong ứng dụng của bạn sửa được điều đó.
ni (nice) — thời gian chạy tiến trình đã hạ ưu tiên. Tách khỏi us để bạn phân biệt được công việc nền với công việc chính.
Và /proc/stat chứa số tích luỹ từ lúc khởi động:
cpu user=68093583 nice=5 system=17692748 idle=1908196406 iowait=2913490
Đơn vị là "jiffies". Mọi công cụ đều đọc file này và tính hiệu giữa hai lần đọc — đó là lý do dòng đầu của vmstat khác hẳn các dòng sau: nó là trung bình từ lúc khởi động.
Quy trình
Từ những gì đo được, thứ tự đọc bốn con số:
stkhác 0? → vấn đề ở tầng ảo hoá, không ở bạn.wacao? → nghẽn đĩa, chắc chắn.wathấp nhưngb> 0 kéo dài? → vẫn nghẽn đĩa, chỉ là CPU bận nênwakhông hiện.sycao màusthấp? → quá nhiều lời gọi hệ thống; xemstrace -c.uscao? → tải tính toán thật; đây là lúc profiler có ích.
Bước 3 là bước hay bị bỏ qua nhất, và nó chính là kết quả đo trong bài này.
Thử ba mươi giây
Xem thời gian CPU của hệ thống đang chảy vào đâu, và kiểm chéo với b:
vmstat 1 6 | tail -5 | awk '{
printf "us=%-3s sy=%-3s id=%-3s wa=%-3s st=%-3s | r=%-3s b=%-3s\n", $13,$14,$15,$16,$17,$1,$2
}'
Rồi với một tiến trình cụ thể, xem tỉ lệ giữa thời gian người dùng và thời gian nhân:
cat /proc/<pid>/stat | awk '{print "utime=" $14 " stime=" $15}'
stime lớn hơn utime nghĩa là tiến trình đó dành phần lớn thời gian trong nhân — gần như luôn là dấu hiệu của quá nhiều lời gọi hệ thống nhỏ, và gần như luôn sửa được bằng cách gộp thao tác lại.
Phần sau: đọc /proc — nguồn thật của mọi con số ở trên.