สาขา () สาขามากกว่าที่คาดไว้หรือไม่


186

พิจารณารหัสต่อไปนี้:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(void)
{
    int i;
    for(i = 0; i < 2; i++)
    {
        fork();
        printf(".");
    }
    return 0;
}

โปรแกรมนี้ให้ผลลัพธ์ 8 จุด เป็นไปได้อย่างไร? ไม่ควรมี 6 จุดแทนใช่ไหม



คำตอบ:


245

fork()ดั้งเดิมมักจะเหยียดจินตนาการ จนกว่าคุณจะรู้สึกถึงมันคุณควรติดตามบนกระดาษว่าการดำเนินการแต่ละอย่างและบัญชีเกี่ยวกับจำนวนกระบวนการ อย่าลืมว่า fork () สร้างสำเนาที่สมบูรณ์แบบของกระบวนการปัจจุบัน ความแตกต่างที่สำคัญที่สุด (เพื่อจุดประสงค์ส่วนใหญ่) คือfork()มูลค่าการส่งคืนที่แตกต่างระหว่างผู้ปกครองและเด็ก (เนื่องจากรหัสนี้ไม่สนใจค่าส่งคืนจึงไม่มีความแตกต่าง)

ดังนั้นในตอนแรกมีกระบวนการเดียว ที่สร้างกระบวนการที่สองซึ่งทั้งสองพิมพ์จุดและวนรอบ ในการวนซ้ำครั้งที่สองแต่ละครั้งจะสร้างสำเนาอีกชุดหนึ่งดังนั้นจึงมีสี่กระบวนการที่พิมพ์จุดแล้วออก ดังนั้นเราสามารถอธิบายจุดหกจุดได้ง่ายอย่างที่คุณคาดหวัง

อย่างไรก็ตามสิ่งที่ทำprintf()จริงๆคือบัฟเฟอร์การส่งออก ดังนั้นจุดแรกจากเมื่อมีเพียงสองกระบวนการไม่ปรากฏขึ้นเมื่อเขียน จุดเหล่านั้นยังคงอยู่ในบัฟเฟอร์ซึ่งทำซ้ำที่ fork () มันไม่ได้จนกว่ากระบวนการกำลังจะออกจากจุดบัฟเฟอร์ที่ปรากฏขึ้น กระบวนการสี่พิมพ์จุดบัฟเฟอร์รวมทั้งกระบวนการใหม่ให้ 8 จุด

หากคุณต้องการหลีกเลี่ยงพฤติกรรมนั้นให้โทรหาfflush(stdout);หลังจากprintf()นั้น


12
ขอบคุณฉันไม่ทราบว่าบัฟเฟอร์ซ้ำกับ fork () มันอธิบายพฤติกรรมที่แปลกประหลาดเช่นนี้
Nikolay Kovalenko

1
ไม่ควรที่จะให้ 10 จุดไม่ใช่ 8? เนื่องจากเด็กรุ่นที่สอง 4 สืบทอดจุดบัฟเฟอร์เพิ่มของพวกเขาเองแล้วลบออกจากพวกเขาจะพิมพ์ทั้งหมด 8 จุด แต่กระบวนการรุ่นแรก 2 จะยังคงมีหนึ่งจุดแต่ละบัฟเฟอร์และล้างออกเมื่อออก ให้ทั้งหมด 10
psusi

12
@psusi หนึ่งในกระบวนการรุ่นที่สองเป็นกระบวนการรุ่นแรก fork()ไม่ได้สร้าง 2 จากนั้นออก แต่สร้างเพียง 1 กระบวนการเท่านั้น
Izkata

70

คุณมีบัฟเฟอร์ปราศจากข้อผูกมัดในการส่งออกลำธาร stdout เป็น line-buffered และบัฟเฟอร์ถูกเรพลิเคตพร้อมกับส่วนที่เหลือของกระบวนการ เมื่อโปรแกรมสิ้นสุดลงบัฟเฟอร์ที่ไม่มีข้อผูกมัดจะถูกเขียนสองครั้ง (หนึ่งครั้งสำหรับแต่ละกระบวนการ) ทั้งการใช้

printf("a\n");

และ

printf("a "); fflush(stdout);

ไม่แสดงปัญหา

ในตัวอย่างแรกของคุณคุณสร้างสี่กระบวนการที่มีสองจุดแต่ละจุดในบัฟเฟอร์กระแสข้อมูลขาออก เมื่อสตรีมแต่ละรายการสิ้นสุดลงฟลัชบัฟเฟอร์จะสร้างจุดแปดจุด


2

เมื่อฉัน = 0

กระบวนการ __1: ข้อความบัฟเฟอร์ = 1 จุด

Process_2 (สร้างโดย Process_1): ข้อความบัฟเฟอร์ = 1 จุด

เมื่อ i = 1

Process_3 (สร้างโดย Process_1): รับ 1 จุดบัฟเฟอร์จาก Process_1 และพิมพ์ 1 จุดด้วยตัวเอง โดยรวม Process_3 พิมพ์ 2 จุด

Process_4 (สร้างโดย Process_2): รับ 1 บัฟเฟอร์จุดจาก Process_2 และพิมพ์ 1 จุดด้วยตัวเอง โดยรวม Process_4 พิมพ์ 2 จุด

Process_1: พิมพ์ 2 จุด (จุดบัฟเฟอร์หนึ่งจุดเมื่อ i = 0 และจุดอื่นเมื่อ i = 1)

Process_2: พิมพ์ 2 จุด (จุดบัฟเฟอร์หนึ่งจุดเมื่อ i = 0 และจุดอื่นเมื่อ i = 1)

ผลลัพธ์สุดท้าย: 8 จุด :)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.