ทำไมไปป์ถึงเหมือน "ค้าง": การบัฟเฟอร์
- คำอธิบายปัญหา: เมื่อรันคำสั่ง
tail -f /some/log/file | grep thing1 | grep thing2 เพื่อค้นหาเอาต์พุตบางอย่างจากไฟล์ล็อก หากมีการเพิ่มบรรทัดล็อกเข้ามาช้า ๆ อาจเกิดปัญหาที่ไม่มีเอาต์พุตแสดงออกมา ดูเหมือนว่าไปป์จะค้าง แต่จริง ๆ แล้วเป็นเพราะโปรแกรมยังไม่เขียนข้อมูลลงไปในไปป์
สาเหตุของการบัฟเฟอร์
- เหตุผลที่มีการบัฟเฟอร์: โดยทั่วไปโปรแกรมมักจะทำการบัฟเฟอร์ก่อนเขียนข้อมูลลงไปยังไปป์หรือไฟล์ เพื่อเพิ่มประสิทธิภาพ แทนที่จะเขียนเอาต์พุตทุกครั้งทันที ก็จะสะสมข้อมูลให้ได้ปริมาณหนึ่งแล้วค่อยเขียนออกครั้งเดียว
- ตัวอย่าง:
grep thing1 อาจเก็บข้อมูลที่แมตช์ไว้จนกว่าจะครบ 8KB ซึ่งทำให้เอาต์พุตอาจยังไม่แสดงออกมา
เวลาเขียนไปยังเทอร์มินัลจะไม่บัฟเฟอร์แบบเดียวกัน
- ความต่างระหว่างเทอร์มินัลกับไปป์:
grep จะใช้ line buffering เมื่อเอาต์พุตส่งไปยังเทอร์มินัล แต่จะใช้ block buffering เมื่อส่งไปยังไปป์ โดยตัดสินจากฟังก์ชัน isatty
คำสั่งที่บัฟเฟอร์และคำสั่งที่ไม่บัฟเฟอร์
- คำสั่งที่ไม่บัฟเฟอร์:
tail, cat, tee เป็นต้น จะไม่บัฟเฟอร์
- คำสั่งที่บัฟเฟอร์:
grep, sed, awk, tcpdump, jq, tr, cut เป็นต้น จะมีการบัฟเฟอร์ และบางตัวสามารถปิดการบัฟเฟอร์ได้ด้วยแฟล็กเฉพาะ
การบัฟเฟอร์เอาต์พุตเริ่มต้นของภาษาโปรแกรม
- ภาษาที่มีการบัฟเฟอร์: C, Python, Ruby, Perl เป็นต้น จะมีการบัฟเฟอร์เอาต์พุตโดยปริยาย และสามารถปิดได้ด้วยวิธีเฉพาะ
ข้อมูลในบัฟเฟอร์สูญหายเมื่อกด Ctrl-C
- คำอธิบายปัญหา: เมื่อกด
Ctrl-C เนื้อหาที่อยู่ในบัฟเฟอร์จะสูญหาย เพราะสัญญาณ SIGINT ถูกส่งไปก่อน
- วิธีแก้: หา PID ของ
tcpdump แล้วรัน kill -TERM $PID เพื่อ flush บัฟเฟอร์
รีไดเร็กต์ไปยังไฟล์ก็ยังมีการบัฟเฟอร์
- การรีไดเร็กต์ไฟล์: แม้จะรีไดเร็กต์ไปยังไฟล์ก็ยังเกิดการบัฟเฟอร์ แต่จะไม่เกิดปัญหาข้อมูลในบัฟเฟอร์สูญหายจาก
Ctrl-C
หลายวิธีในการหลีกเลี่ยงการบัฟเฟอร์
- วิธีแก้ 1: รันโปรแกรมที่จบการทำงานได้เร็ว
- วิธีแก้ 2: ใช้แฟล็ก
--line-buffered ของ grep
- วิธีแก้ 3: ใช้
awk
- วิธีแก้ 4: ใช้
stdbuf
- วิธีแก้ 5: ใช้
unbuffer
ตัวแปรสภาพแวดล้อมสำหรับปิดการบัฟเฟอร์
- แนวคิด: มีความเห็นว่าน่าจะมีตัวแปรสภาพแวดล้อมมาตรฐานแบบ
PYTHON_UNBUFFERED ให้ใช้ร่วมกันได้ เช่นเสนอชื่อว่า NO_BUFFER
เนื้อหาที่ไม่ได้กล่าวถึง
- หัวข้อที่ละไว้: ความต่างระหว่าง line buffering กับการไม่บัฟเฟอร์เลย, ความต่างของการบัฟเฟอร์ระหว่าง stderr และ stdout, การบัฟเฟอร์ของ TTY driver ในระบบปฏิบัติการ เป็นต้น
1 ความคิดเห็น
ความเห็นจาก Hacker News
การเข้าถึงแบบมีบัฟเฟอร์ควรถูก flush เมื่อถึงจำนวนไบต์หรือเวลาที่กำหนด เป็นวิธีทั่วไปที่ใช้แก้ปัญหาคล้ายกันในอินเทอร์เฟซฮาร์ดแวร์
อยากให้ระบบ flush บัฟเฟอร์ทั้งหมดเมื่อ CPU ทั้งระบบเข้าสู่สถานะ idle
ใช้ระบบตระกูล NIX มานานกว่า 20 ปีแล้ว แต่ก็ยังลืมเรื่องปัญหาบัฟเฟอร์อยู่เสมอ
ใช้ Unix มานานกว่า 35 ปี แต่ไม่เคยเข้าใจกลไกการทำงานของบัฟเฟอร์อย่างถ่องแท้ คำอธิบายนี้มีประโยชน์มาก
กำลังสับสนระหว่าง "unbuffered" กับ "line buffered"
บัฟเฟอร์มีอยู่เพราะการเขียนลงบัฟเฟอร์ช้ากว่าการพิมพ์เอาต์พุตขึ้นหน้าจออย่างมาก
เมื่อกด Ctrl-C เนื้อหาในบัฟเฟอร์อาจสูญหายได้
เคยเจอปัญหาบัฟเฟอร์บน Unix และ
awkแต่ละ implementation ก็ไม่ได้ทำงานเหมือนกันทั้งหมดรู้สึกเหมือนพลาดมุกเรื่องท่อที่จับตัวเป็นน้ำแข็ง