Vì sao pipe "đứng hình": buffering
- Mô tả vấn đề: Khi chạy lệnh
tail -f /some/log/file | grep thing1 | grep thing2 để tìm một đầu ra cụ thể trong file log, nếu các dòng log được thêm vào chậm thì có thể không thấy đầu ra xuất hiện. Điều này trông như thể pipe bị treo, nhưng thực ra là vì chương trình không ghi dữ liệu vào pipe.
Nguyên nhân của buffering
- Vì sao có buffering: Việc chương trình thực hiện buffering trước khi ghi dữ liệu vào pipe hoặc file là điều phổ biến. Mục đích là để cải thiện hiệu năng: thay vì ghi ngay mọi đầu ra, chương trình gom một lượng dữ liệu nhất định rồi ghi một lần.
- Ví dụ:
grep thing1 sẽ giữ lại dữ liệu đã khớp cho đến khi tích lũy đủ 8KB dữ liệu, nên đầu ra có thể không xuất hiện ngay.
Khi ghi ra terminal thì không buffering
- Khác biệt giữa terminal và pipe:
grep dùng line buffering khi đầu ra đi tới terminal, nhưng dùng block buffering khi đầu ra đi tới pipe. Việc này được quyết định thông qua hàm isatty.
Lệnh nào buffering và lệnh nào không
- Các lệnh không buffering:
tail, cat, tee v.v. không buffering.
- Các lệnh có buffering:
grep, sed, awk, tcpdump, jq, tr, cut v.v. có buffering, và một số lệnh cho phép tắt buffering bằng cờ cụ thể.
Buffering đầu ra mặc định của các ngôn ngữ lập trình
- Các ngôn ngữ có buffering: C, Python, Ruby, Perl v.v. mặc định đều buffering đầu ra và có thể tắt bằng những cách nhất định.
Mất nội dung trong buffer khi nhấn Ctrl-C
- Mô tả vấn đề: Khi nhấn
Ctrl-C, nội dung đang nằm trong buffer sẽ bị mất. Điều này xảy ra vì tín hiệu SIGINT được gửi đi trước.
- Cách xử lý: Tìm PID của
tcpdump rồi chạy kill -TERM $PID để flush buffer.
Buffering cả khi redirect vào file
- Redirect vào file: Khi redirect vào file, buffering vẫn xảy ra, nhưng không gặp vấn đề mất dữ liệu trong buffer do
Ctrl-C.
Nhiều cách để tránh buffering
- Giải pháp 1: Chạy chương trình kết thúc nhanh.
- Giải pháp 2: Dùng cờ
--line-buffered của grep.
- Giải pháp 3: Dùng
awk.
- Giải pháp 4: Dùng
stdbuf.
- Giải pháp 5: Dùng
unbuffer.
Biến môi trường để tắt buffering
- Ý tưởng: Có ý kiến cho rằng sẽ rất tốt nếu tồn tại một biến môi trường chuẩn như
PYTHON_UNBUFFERED. Bài viết đề xuất một biến như NO_BUFFER.
Nội dung được lược bỏ
- Các chủ đề bị lược bỏ: Sự khác nhau giữa line buffering và hoàn toàn không buffering, sự khác nhau về buffering giữa stderr và stdout, buffering của driver TTY trong hệ điều hành, v.v.
1 bình luận
Ý kiến trên Hacker News
Truy cập có đệm nên được flush sau một số byte hoặc sau một khoảng thời gian nhất định. Đây là cách phổ biến để giải quyết vấn đề tương tự trong các giao diện phần cứng
Khi toàn bộ CPU của hệ thống chuyển sang trạng thái nhàn rỗi, sẽ muốn flush mọi bộ đệm
Đã làm việc với các hệ thống NIX hơn 20 năm, nhưng lúc nào cũng quên mất vấn đề buffering
Đã dùng Unix hơn 35 năm nhưng chưa từng hiểu hoàn toàn cách buffering hoạt động. Phần giải thích này rất hữu ích
Đang nhầm lẫn giữa "unbuffered" và "line buffering"
Bộ đệm tồn tại vì ghi vào bộ đệm nhanh hơn rất nhiều so với việc in đầu ra ra màn hình
Nhấn Ctrl-C có thể làm mất nội dung trong bộ đệm
Đã gặp vấn đề buffering trên Unix, và không phải mọi triển khai 'awk' đều hoạt động giống nhau
Có cảm giác mình đã bỏ lỡ trò đùa về đường ống bị đóng băng