มีตัวแปร UNIX ใดบ้างที่กระบวนการลูกตายกับผู้ปกครอง?


41

ฉันได้ศึกษาพฤติกรรมเคอร์เนลของ Linux มาระยะหนึ่งแล้วและมันชัดเจนสำหรับฉันเสมอว่า:

เมื่อกระบวนการตายเด็กทุกคนจะถูกส่งกลับไปที่initกระบวนการ (PID 1) จนกว่าพวกเขาจะตายในที่สุด

อย่างไรก็ตามเมื่อเร็ว ๆ นี้มีคนที่มีประสบการณ์มากกว่าฉันด้วยเคอร์เนลบอกฉันว่า:

เมื่อกระบวนการออกไปเด็กทุกคนในนั้นก็ตาย (ยกเว้นคุณใช้NOHUPในกรณีที่พวกเขากลับไปinit)

ตอนนี้แม้ว่าฉันจะไม่เชื่อเรื่องนี้ฉันยังคงเขียนโปรแกรมง่าย ๆ เพื่อให้แน่ใจ ฉันรู้ว่าฉันไม่ควรพึ่งพาเวลา ( sleep) สำหรับการทดสอบเนื่องจากทุกอย่างขึ้นอยู่กับการจัดตารางกระบวนการ แต่สำหรับกรณีง่าย ๆ นี้ฉันคิดว่ามันค่อนข้างเพียงพอ

int main(void){
    printf("Father process spawned (%d).\n", getpid());
    sleep(5);

    if(fork() == 0){
        printf("Child process spawned (%d => %d).\n", getppid(), getpid());
        sleep(15);
        printf("Child process exiting (%d => %d).\n", getppid(), getpid());
        exit(0);
    }

    sleep(5);
    printf(stdout, "Father process exiting (%d).\n", getpid());
    return EXIT_SUCCESS;
}

นี่คือผลลัพธ์ของโปรแกรมพร้อมpsผลลัพธ์ที่เกี่ยวข้องทุกครั้งที่printfพูดถึง:

$ ./test &
Father process spawned (435).

$ ps -ef | grep test
myuser    435    392   tty1    ./test

Child process spawned (435 => 436).

$ ps -ef | grep test
myuser    435    392   tty1    ./test
myuser    436    435   tty1    ./test

Father process exiting (435).

$ ps -ef | grep test
myuser    436    1     tty1    ./test

Child process exiting (436).

ทีนี้อย่างที่คุณเห็นสิ่งนี้มีพฤติกรรมเหมือนที่ฉันคาดไว้ กระบวนการเด็กกำพร้า (436) จะได้รับกลับไปที่init(1) จนกว่ามันจะตาย

อย่างไรก็ตามมีระบบที่ใช้ UNIX ซึ่งพฤติกรรมนี้ไม่ได้ใช้งานโดยค่าเริ่มต้นหรือไม่? มีระบบใดบ้างที่การตายของกระบวนการทำให้เกิดการตายของลูกทั้งหมด?

คำตอบ:


68

เมื่อกระบวนการจบการทำงานเด็ก ๆ ทุกคนก็จะตาย (ยกเว้นคุณใช้ NOHUP ในกรณีที่พวกเขากลับมาเพื่อเริ่มต้น)

นี่เป็นสิ่งที่ผิด ตายผิด คนที่บอกว่าผิดพลาดหรือสับสนสถานการณ์เฉพาะกับคดีทั่วไป

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

การเริ่มต้นงานจากเชลล์ด้วยการnohupหยุดสัญญาณแหล่งที่สองของ HUP เนื่องจากงานจะละเว้นสัญญาณและดังนั้นจึงไม่ถูกบอกให้ตายเมื่อเทอร์มินัลหายไป วิธีอื่น ๆ ในการแยกการแพร่กระจายของสัญญาณ HUP จากเชลล์ไปยังงานรวมถึงการใช้disownbuiltin ของเชลล์ถ้ามีหนึ่ง (งานจะถูกลบออกจากรายการงานของเชลล์) และการฟอร์กกิ้งสองครั้ง (เชลล์เปิดใช้ชายด์ที่เปิดลูก ของตัวเองและออกทันทีหอยไม่มีความรู้เกี่ยวกับหลานของมัน)

อีกครั้งงานที่เริ่มต้นในเทอร์มินัลไม่ได้ตายเพราะกระบวนการหลักของพวกเขา (เปลือก) ตาย แต่เพราะกระบวนการหลักของพวกเขาตัดสินใจที่จะฆ่าพวกเขาเมื่อมีการบอกให้ฆ่าพวกเขา และเชลล์เริ่มต้นในเทอร์มินัลจะไม่ตายเพราะกระบวนการพาเรนต์ของมันตาย แต่เนื่องจากเทอร์มินัลนั้นหายไป (ซึ่งอาจจะหรืออาจไม่เกิดขึ้นโดยบังเอิญก็ได้เพราะเทอร์มินัลนั้น


1
มีวิธีที่สามคือกลุ่มควบคุม ทั้งหมดที่ฉันรู้คือ systemd ใช้เพื่อบังคับให้ฆ่าคนให้สะอาด
o11c

5
@JanHudec ฉันคิดว่าคุณกำลังสับสนกับnohup เป็น builtin ในเชลล์บางตัวที่ลบงานที่กำลังรันอยู่ออกจากตารางงาน (รับอาร์กิวเมนต์เช่น) และผลข้างเคียงที่เป็นประโยชน์หลักของมันคือเชลล์จะไม่ส่ง SIGHUP ไปยังกระบวนการย่อย เป็นยูทิลิตีภายนอกที่ละเว้น SIGHUP (และทำสิ่งอื่น ๆ อีกเล็กน้อย) จากนั้นเริ่มคำสั่งที่ส่งผ่านบนบรรทัดคำสั่ง disowndisown%1nohup
Gilles 'หยุดความชั่วร้าย'

@Gilles: ไม่ฉันไม่ได้ แต่การดู strace nohupในความเป็นจริงไม่สนใจสัญญาณก่อนที่จะexecเป็นคำสั่ง
Jan Hudec

ขอบคุณสำหรับคำตอบ! ฉันชอบพื้นหลังทางประวัติศาสตร์เล็กน้อยเกี่ยวกับการวางสายโมเด็ม ผมเริ่มกังวลฉันได้รับความเข้าใจผิดเคอร์เนลที่ทุกเวลานี้ ...
จอห์นสมิ ธ WH

3
นอกจากนี้ควรสังเกตว่าโปรแกรมออกจากเมื่อได้รับ SIGHUP เนื่องจากตัวจัดการสัญญาณเริ่มต้นสำหรับ SIGHUP คือการออก แต่โปรแกรมใด ๆ อาจใช้ตัวจัดการ SIGHUP ของตนเองซึ่งอาจทำให้โปรแกรมไม่ออกเมื่อเชลล์พาเรนต์ส่ง SIGHUP
slebetman

12

เมื่อกระบวนการจบการทำงานเด็ก ๆ ทุกคนก็จะตาย (ยกเว้นคุณใช้ NOHUP ในกรณีที่พวกเขากลับมาเพื่อเริ่มต้น)

สิ่งนี้ถูกต้องหากกระบวนการเป็นผู้นำเซสชัน เมื่อผู้นำเซสชันเสียชีวิต SIGHUP จะถูกส่งไปยังสมาชิกทั้งหมดของเซสชันนั้น ในทางปฏิบัตินั่นหมายถึงลูก ๆ และลูกหลานของมัน

setsidกระบวนการทำให้ตัวเองเป็นผู้นำเซสชั่นโดยการเรียก เชลล์ใช้สิ่งนี้


ฉันเพิ่งจะทำการทดสอบเกี่ยวกับเรื่องนี้ แต่ฉันไม่สามารถทำซ้ำพฤติกรรมที่คุณอธิบายได้ ... ฉันวางไข่กระบวนการหนึ่งให้เซสชันใหม่ ( forkฆ่ากระบวนการผู้ปกครองsetsid) และแยกเด็กคนอื่นเข้าสู่เซสชันใหม่นี้ อย่างไรก็ตามเมื่อกระบวนการหลัก (หัวหน้าเซสชันใหม่) เสียชีวิตเด็กจะไม่ได้รับ SIGHUP และถูกผูกไว้initจนกว่าจะออกในที่สุด
John WH Smith

จากsetpgid(2)manpage: หากเซสชันมีเทอร์มินัลการควบคุมและไม่ได้ตั้งค่าสถานะ CLOCAL สำหรับเทอร์มินัลนั้นและการแฮงเอาท์เทอร์มินัลเกิดขึ้นหัวหน้าเซสชันจะถูกส่ง SIGHUP หากผู้นำเซสชันออกจากนั้นสัญญาณ SIGHUP จะถูกส่งไปยังแต่ละกระบวนการในกลุ่มกระบวนการทำงานเบื้องหน้าของเทอร์มินัลการควบคุม
ninjalj

1
@ninjalj จากนั้นฉันเดาว่าคำตอบนี้ใช้ได้ถ้าหากหัวหน้าเซสชันเป็นกระบวนการควบคุมของเทอร์มินัลด้วยเช่นกัน ผู้นำเซสชันมาตรฐานซึ่งเป็นอิสระจากเทอร์มินัลใด ๆ จะไม่ฆ่าลูกของมัน ถูกต้องหรือไม่
John WH Smith

-1

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

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


หลายสาเหตุสามารถทำให้ผู้ปกครองตาย ไม่มีวิธีใดที่จะป้องกันการเอาชีวิตรอดของเด็ก ๆ ได้หากผู้ปกครองเป็น SIGKILLed
Emil Jeřábek

-1

ฉันขาดคำตอบเล็กน้อยที่เกี่ยวข้องกับผู้ปกครองที่กำลังจะตายเล็กน้อย: เมื่อกระบวนการเขียนบนไพพ์ที่ไม่มีกระบวนการอ่านอีกต่อไปมันจะได้รับ SIGPIPE การกระทำมาตรฐานสำหรับ SIGPIPE กำลังสิ้นสุดลง

สิ่งนี้สามารถทำให้กระบวนการตายได้ ในความเป็นจริงมันเป็นวิธีมาตรฐานในโปรแกรมที่yesตาย

ถ้าฉันรัน

(yes;echo $? >&2)|head -10

ในระบบของฉันคำตอบคือ

y
y
y
y
y
y
y
y
y
y
141

และ 141 นั้นเป็น 128 + SIGPIPE:

   SIGPIPE      13       Term    Broken pipe: write to pipe with no
                                 readers

man 7 signalจาก


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