พื้นหลังที่สำคัญในที่นี้stdout
คือต้องมีการบัฟเฟอร์บรรทัดโดยมาตรฐานเป็นการตั้งค่าเริ่มต้น
สิ่งนี้ทำให้ a \n
เพื่อล้างเอาต์พุต
เนื่องจากตัวอย่างที่สองไม่มีการขึ้นบรรทัดใหม่เอาต์พุตจะไม่ถูกลบทิ้งและfork()
คัดลอกกระบวนการทั้งหมดจึงคัดลอกสถานะของstdout
บัฟเฟอร์
ในตอนนี้การfork()
เรียกเหล่านี้ในตัวอย่างของคุณสร้างกระบวนการทั้งหมด 8 กระบวนการ - ทั้งหมดนั้นมีการคัดลอกสถานะของstdout
บัฟเฟอร์
ตามคำนิยามกระบวนการเหล่านี้จะเรียกใช้exit()
เมื่อส่งคืนจากmain()
และexit()
เรียกfflush()
ตามด้วยสตรีมstdio ที่fclose()
ใช้งานอยู่ทั้งหมด ซึ่งรวมถึงและเป็นผลให้คุณเห็นเนื้อหาเดียวกันแปดครั้งstdout
เป็นวิธีปฏิบัติที่ดีในการเรียกfflush()
ใช้สตรีมทั้งหมดที่มีเอาต์พุตที่ค้างอยู่ก่อนการโทรfork()
หรือเพื่อให้เด็กที่ถูกแยกออกมาเรียกอย่างชัดเจน_exit()
ว่าจะออกจากกระบวนการโดยไม่ล้างข้อมูลสตรีม stdio
หมายเหตุโทรที่exec()
ไม่ได้ล้างบัฟเฟอร์ stdio ดังนั้นจึงมีการตกลงที่จะไม่สนใจเกี่ยวกับบัฟเฟอร์ stdio ถ้าคุณ (หลังจากเรียกfork()
) โทรexec()
และ (ถ้าที่ล้มเหลว) _exit()
โทร
BTW: เพื่อให้เข้าใจว่าการบัฟเฟอร์ผิดอาจทำให้เกิดนี่เป็นข้อผิดพลาดเดิมใน Linux ที่ได้รับการแก้ไขเร็ว ๆ นี้:
มาตรฐานจะต้องไม่มีstderr
การstderr
บัฟเฟอร์ตามค่าเริ่มต้น แต่ Linux เพิกเฉยต่อสิ่งนี้และทำบัฟเฟอร์บรรทัดและ (ยิ่งแย่กว่า) บัฟเฟอร์เต็มในกรณีที่ stderr ถูกเปลี่ยนเส้นทางผ่านไพพ์ ดังนั้นโปรแกรมที่เขียนสำหรับ UNIX ก็ทำสิ่งที่เอาต์พุตโดยไม่ขึ้นบรรทัดใหม่บน Linux
ดูความคิดเห็นด้านล่างดูเหมือนว่าจะได้รับการแก้ไขในขณะนี้
นี่คือสิ่งที่ฉันทำเพื่อแก้ไขปัญหา Linux นี้:
/*
* Linux comes with a broken libc that makes "stderr" buffered even
* though POSIX requires "stderr" to be never "fully buffered".
* As a result, we would get garbled output once our fork()d child
* calls exit(). We work around the Linux bug by calling fflush()
* before fork()ing.
*/
fflush(stderr);
รหัสนี้ไม่เป็นอันตรายต่อแพลตฟอร์มอื่น ๆ เนื่องจากการเรียกfflush()
สตรีมที่เพิ่งถูกฟลัชคือ noop
./prog1 > prog1.out
) หรือไพพ์ (./prog1 | cat
) เตรียมที่จะมีความคิดของคุณเป่า. :-)