2 điểm bởi GN⁺ 2024-11-30 | 1 bình luận | Chia sẻ qua WhatsApp

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

 
GN⁺ 2024-11-30
Ý 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

    • Thư viện đệm ở không gian người dùng nên thiết lập bộ hẹn giờ phù hợp khi bắt đầu đệm dữ liệu
    • Tham số timeout nên được truyền dưới dạng đối số, hoặc thấp hơn một chút so với thang thời gian cảm nhận của con người, hoặc tỷ lệ với băng thông/ngưỡng, hoặc tỷ lệ với chi phí flush
    • Điều này áp dụng cho cả ghi và đọc, và có thể khác nhau tùy theo kênh dữ liệu
  • 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

    • Buffering thường là một kỹ thuật để tiết kiệm CPU
    • Khi CPU rảnh, nên gửi tín hiệu "hãy flush bộ đệm" tới mọi tiến trình
  • Đã 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"

    • Unbuffered có thể làm giảm hiệu năng và tạo ra đầu ra sai khi nhiều nguồn cùng ghi vào một pipe
    • Line buffering là mặc định của terminal và phù hợp với pipe
  • 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

    • Đây là vấn đề thường gặp khi làm việc với UART, và có nhiều cách giải quyết khác nhau
    • Có nhiều phương pháp như dùng ký tự đặc biệt, cách tiếp cận dựa trên độ dài, cách tiếp cận dựa trên thời gian, v.v.
  • Nhấn Ctrl-C có thể làm mất nội dung trong bộ đệm

    • Tôi từng nghĩ phần lớn chương trình sẽ flush bộ đệm khi nhận SIGINT
  • Đã 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