วิธีที่จะทำให้กระบวนการเด็กตายหลังจากที่พ่อแม่ออกไป?


209

สมมติว่าฉันมีกระบวนการที่วางกระบวนการลูกหนึ่งกระบวนการ ตอนนี้เมื่อกระบวนการผู้ปกครองออกจากด้วยเหตุผลใดก็ตาม (ปกติหรือผิดปกติโดยการฆ่า, ^ C, ยืนยันความล้มเหลวหรือสิ่งอื่นใด) ฉันต้องการให้กระบวนการเด็กที่จะตาย ทำอย่างไรให้ถูกต้อง?


บางคำถามที่คล้ายกันใน stackoverflow:


คำถามที่คล้ายกันบางอย่างใน stackoverflow สำหรับWindows :

คำตอบ:


189

เด็กสามารถขอให้เคอร์เนลส่งSIGHUP(หรือสัญญาณอื่น ๆ ) เมื่อผู้ปกครองตายโดยระบุตัวเลือกPR_SET_PDEATHSIGในprctl()syscall ดังนี้:

prctl(PR_SET_PDEATHSIG, SIGHUP);

ดูman 2 prctlรายละเอียดที่

แก้ไข: นี่เป็น Linux เท่านั้น


5
นี่เป็นทางออกที่ไม่ดีเพราะผู้ปกครองอาจเสียชีวิตไปแล้ว สภาพการแข่งขัน. วิธีแก้ปัญหาที่ถูกต้อง: stackoverflow.com/a/17589555/412080
Maxim Egorushkin

16
การเรียกคำตอบที่ไม่ดีนั้นไม่ดีมาก - แม้ว่ามันจะไม่ได้อยู่ในสภาพการแข่งขัน ดูคำตอบของฉันเกี่ยวกับวิธีการใช้งานprctl()ในสภาพการแข่งขันฟรี Btw คำตอบที่เชื่อมโยงโดย Maxim ไม่ถูกต้อง
maxschlepzig

4
นี่เป็นเพียงคำตอบที่ผิด มันจะส่งสัญญาณไปยังกระบวนการเด็กในเวลาที่ด้ายซึ่งเรียกว่าส้อมตายไม่ใช่เมื่อกระบวนการหลักตาย
Lothar

2
@ Lothar มันคงจะดีถ้าได้เห็นหลักฐานบางอย่าง man prctlพูดว่า: ตั้งค่าสัญญาณตายกระบวนการแม่ของกระบวนการเรียกไปที่ arg2 (ทั้งค่าสัญญาณในช่วง 1..maxsig หรือ 0 เพื่อล้าง) นี่คือสัญญาณที่กระบวนการเรียกจะได้รับเมื่อผู้ปกครองตาย ค่านี้ถูกลบสำหรับชายด์ของ fork (2) และ (ตั้งแต่ Linux 2.4.36 / 2.6.23) เมื่อดำเนินการ set-user-ID หรือไบนารี set-group-ID
qrdl

1
@maxschlepzig ขอบคุณสำหรับลิงค์ใหม่ ดูเหมือนว่าลิงก์ก่อนหน้านี้ไม่ถูกต้อง อย่างไรก็ตามหลังจากหลายปีที่ผ่านมายังไม่มี API สำหรับการตั้งค่าตัวเลือกในด้านผู้ปกครอง น่าเสียดาย
rox

68

ฉันพยายามแก้ไขปัญหาเดียวกันและเนื่องจากโปรแกรมของฉันต้องทำงานบน OS X โซลูชัน Linux เท่านั้นเท่านั้นจึงไม่ได้ผลสำหรับฉัน

ฉันมาถึงข้อสรุปเช่นเดียวกับคนอื่น ๆ ในหน้านี้ - ไม่มีวิธีที่เข้ากันได้กับ POSIX ในการแจ้งเด็กเมื่อผู้ปกครองเสียชีวิต ดังนั้นฉันจึงรวบรวมสิ่งที่ดีที่สุด - การสำรวจความคิดเห็นของเด็ก

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

สิ่งนี้ไม่ดี แต่ใช้งานได้และง่ายกว่าโซลูชันการสำรวจหน่วยความจำ TCP socket / lockfile ที่แนะนำที่อื่นในหน้านี้


6
ทางออกที่ดีเยี่ยม getppid () ต่อเนื่องจนกระทั่งมันคืนค่า 1 แล้วออก นี่เป็นสิ่งที่ดีและตอนนี้ฉันก็ใช้ด้วย วิธีการแก้ปัญหาที่ไม่ใช่มลพิษจะดี ขอบคุณ Schof
neoneye

10
สำหรับข้อมูลบน Solaris หากคุณอยู่ในโซนgettpid()ไม่กลายเป็น 1 แต่จะได้รับตัวpidกำหนดเวลาของโซน (กระบวนการzsched)
Patrick Schlüter

4
หากใครสงสัยในระบบ Android pid น่าจะเป็น 0 (Process System pid) แทน 1 เมื่อผู้ปกครองตาย
Rui Marques

2
หากต้องการมีวิธีที่แข็งแกร่งและเป็นอิสระในการทำแพลตฟอร์มก่อน fork () - ing เพียงแค่ getpid () และถ้า getppid () จากเด็กแตกต่างออกไป
เซบาสเตียน

2
สิ่งนี้ใช้ไม่ได้หากคุณไม่ได้ควบคุมกระบวนการลูก ตัวอย่างเช่นฉันกำลังทำงานกับคำสั่งที่ wraps find (1) และฉันต้องการตรวจสอบให้แน่ใจว่าการค้นหาถูกฆ่าหาก wrapper ตายด้วยเหตุผลบางอย่าง
Lucretiel

34

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

อาจเป็นไปไม่ได้ในกรณีของคุณ แต่น่ารักเมื่อใช้งานได้


ทางออกที่ดีมากขอบคุณ! อันที่ได้รับการยอมรับในปัจจุบันนั้นกว้างกว่าทั่วไป แต่คุณพกพาสะดวกกว่า
Paweł Hajdan

1
ปัญหาใหญ่ในการทำงานในพาเรนต์คือคุณกำลังเปลี่ยนกระบวนการพาเรนต์ ในกรณีของเซิร์ฟเวอร์ที่ต้องเรียกใช้ "ตลอดไป" นั่นไม่ใช่ตัวเลือก
Alexis Wilke

29

หากคุณไม่สามารถแก้ไขกระบวนการลูกคุณสามารถลองทำสิ่งต่อไปนี้:

int pipes[2];
pipe(pipes)
if (fork() == 0) {
    close(pipes[1]); /* Close the writer end in the child*/
    dup2(0, pipes[0]); /* Use reader end as stdin */
    exec("sh -c 'set -o monitor; child_process & read dummy; kill %1'")
}

close(pipes[0]); /* Close the reader end in the parent */

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

เมื่อผู้ปกครองเสียชีวิต - ไม่ว่าจะด้วยเหตุผลอะไร - มันจะปิดท้ายท่อ เปลือกลูกจะได้รับ EOF จากการอ่านและดำเนินการต่อเพื่อฆ่ากระบวนการเด็กพื้นหลัง


2
ดี, แต่การเรียกของระบบห้าครั้ง, และการเกิดในรหัสสิบบรรทัดทำให้ฉันสงสัยเกี่ยวกับการแสดงโค้ดนี้
Oleiade

+1 คุณสามารถหลีกเลี่ยงdup2และเข้าควบคุม stdin โดยใช้read -uแฟล็กเพื่ออ่านจากไฟล์ descriptor ฉันยังเพิ่ม a setpgid(0, 0)ในเด็กเพื่อป้องกันไม่ให้ออกเมื่อกด ^ C ในเทอร์มินัล
Greg Hewgill

คำสั่งอาร์กิวเมนต์ของการdup2()โทรผิด หากคุณต้องการที่จะใช้pipes[0]เป็น stdin คุณต้องเขียนแทนdup2(pipes[0], 0) dup2(0, pipes[0])เป็นdup2(oldfd, newfd)ที่ที่การโทรปิด newfd ที่เปิดไว้ก่อนหน้านี้
maxschlepzig

@Oleiade ผมเห็นด้วยโดยเฉพาะอย่างยิ่งนับตั้งแต่การดวลจุดโทษจากกระบอกไม้ไผ่ไม่เพียงแค่ส้อมอีกครั้งเพื่อดำเนินการตามกระบวนการเด็กจริง ...
maxschlepzig

16

ภายใต้ Linux คุณสามารถติดตั้งสัญญาณการตายของผู้ปกครองในเด็กได้เช่น:

#include <sys/prctl.h> // prctl(), PR_SET_PDEATHSIG
#include <signal.h> // signals
#include <unistd.h> // fork()
#include <stdio.h>  // perror()

// ...

pid_t ppid_before_fork = getpid();
pid_t pid = fork();
if (pid == -1) { perror(0); exit(1); }
if (pid) {
    ; // continue parent execution
} else {
    int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
    if (r == -1) { perror(0); exit(1); }
    // test in case the original parent exited just
    // before the prctl() call
    if (getppid() != ppid_before_fork)
        exit(1);
    // continue child execution ...

โปรดทราบว่าการจัดเก็บรหัสกระบวนการหลักก่อนที่จะแยกและทดสอบในเด็กหลังจากprctl()กำจัดสภาพการแข่งขันระหว่างprctl()และออกจากกระบวนการที่เรียกว่าเด็ก

นอกจากนี้โปรดทราบว่าสัญญาณการเสียชีวิตของผู้ปกครองของเด็กจะถูกล้างในลูกที่สร้างขึ้นใหม่ด้วยตนเอง execve()มันไม่ได้รับผลกระทบโดย

การทดสอบนั้นสามารถทำให้ง่ายขึ้นหากเรามั่นใจว่ากระบวนการของระบบที่รับผิดชอบดูแลเด็กกำพร้าทั้งหมดมี PID 1:

pid_t pid = fork();
if (pid == -1) { perror(0); exit(1); }
if (pid) {
    ; // continue parent execution
} else {
    int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
    if (r == -1) { perror(0); exit(1); }
    // test in case the original parent exited just
    // before the prctl() call
    if (getppid() == 1)
        exit(1);
    // continue child execution ...

อย่างไรก็ตามการใช้กระบวนการของระบบinitและการมี PID 1 นั้นไม่สามารถพกพาได้ POSIX.1-2008 ระบุ :

ID กระบวนการหลักของกระบวนการลูกที่มีอยู่ทั้งหมดและกระบวนการ zombie ของกระบวนการที่เรียกจะต้องตั้งค่าเป็น ID กระบวนการของกระบวนการระบบที่กำหนดใช้งาน นั่นคือกระบวนการเหล่านี้จะได้รับมรดกโดยกระบวนการระบบพิเศษ

ตามเนื้อผ้ากระบวนการของระบบที่ใช้เด็กกำพร้าทั้งหมดคือ PID 1 คือ init ซึ่งเป็นบรรพบุรุษของกระบวนการทั้งหมด

ในระบบที่ทันสมัยเช่นLinuxหรือFreeBSDกระบวนการอื่นอาจมีบทบาทนั้น ตัวอย่างเช่นในลินุกซ์กระบวนการที่สามารถโทรprctl(PR_SET_CHILD_SUBREAPER, 1)เพื่อสร้างตัวเองเป็นกระบวนการระบบที่สืบทอดเด็กกำพร้าทั้งหมดใด ๆ ของลูกหลาน (cf ตัวอย่างบน Fedora 25)


ฉันไม่เข้าใจ "การทดสอบนั้นสามารถทำให้ง่ายขึ้นถ้าเรามั่นใจว่าปู่ย่าตายายมักจะเป็นกระบวนการเริ่มต้น" เมื่อกระบวนการหลักตายกระบวนการกลายเป็นลูกของกระบวนการเริ่มต้น (pid 1) ไม่ใช่ลูกของปู่ย่าตายายใช่ไหม ดังนั้นการทดสอบจึงถูกต้องเสมอ
Johannes Schaub - litb


น่าสนใจขอบคุณ แม้ว่าฉันจะไม่เห็นว่ามันเกี่ยวข้องกับปู่ย่าตายายอย่างไร
Johannes Schaub - litb

1
@ JohannesSchaub-litb คุณไม่สามารถคาดเดาได้เสมอว่าความโปร่งใสของกระบวนการจะเป็นinit(8)กระบวนการ ... สิ่งเดียวที่คุณสามารถสันนิษฐานได้ก็คือเมื่อกระบวนการผู้ปกครองเสียชีวิตนั่นคือ id หลักของมันจะเปลี่ยนไป สิ่งนี้เกิดขึ้นครั้งเดียวในชีวิตของกระบวนการ .... และเมื่อแม่ของกระบวนการนั้นตาย มีเพียงหนึ่งหลักข้อนี้ได้และสำหรับinit(8)เด็ก แต่คุณได้รับการคุ้มครองจากนี้เช่นinit(8)เคยexit(2)(ตื่นตกใจเคอร์เนลในกรณีที่)
หลุยส์โคโลราโด

1
น่าเสียดายที่ถ้าเด็ก ๆ แยกจากเธรดและจากนั้นการออกจากเธรดกระบวนการของเด็กจะได้รับ SIGTERM
rox

14

เพื่อความสมบูรณ์ ใน macOS คุณสามารถใช้ kqueue:

void noteProcDeath(
    CFFileDescriptorRef fdref, 
    CFOptionFlags callBackTypes, 
    void* info) 
{
    // LOG_DEBUG(@"noteProcDeath... ");

    struct kevent kev;
    int fd = CFFileDescriptorGetNativeDescriptor(fdref);
    kevent(fd, NULL, 0, &kev, 1, NULL);
    // take action on death of process here
    unsigned int dead_pid = (unsigned int)kev.ident;

    CFFileDescriptorInvalidate(fdref);
    CFRelease(fdref); // the CFFileDescriptorRef is no longer of any use in this example

    int our_pid = getpid();
    // when our parent dies we die as well.. 
    LOG_INFO(@"exit! parent process (pid %u) died. no need for us (pid %i) to stick around", dead_pid, our_pid);
    exit(EXIT_SUCCESS);
}


void suicide_if_we_become_a_zombie(int parent_pid) {
    // int parent_pid = getppid();
    // int our_pid = getpid();
    // LOG_ERROR(@"suicide_if_we_become_a_zombie(). parent process (pid %u) that we monitor. our pid %i", parent_pid, our_pid);

    int fd = kqueue();
    struct kevent kev;
    EV_SET(&kev, parent_pid, EVFILT_PROC, EV_ADD|EV_ENABLE, NOTE_EXIT, 0, NULL);
    kevent(fd, &kev, 1, NULL, 0, NULL);
    CFFileDescriptorRef fdref = CFFileDescriptorCreate(kCFAllocatorDefault, fd, true, noteProcDeath, NULL);
    CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack);
    CFRunLoopSourceRef source = CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, fdref, 0);
    CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopDefaultMode);
    CFRelease(source);
}

คุณสามารถทำได้ด้วย API ที่ดีกว่าเล็กน้อยโดยใช้แหล่งการแจกจ่ายด้วย DISPATCH_SOURCE_PROC และ PROC_EXIT
russbishop

ไม่ว่าจะด้วยเหตุผลใดก็ตามนี่ทำให้ Mac ของฉันตื่นตระหนก การเรียกใช้โปรเซสด้วยรหัสนี้มีโอกาส 50% หรือมากกว่านั้นซึ่งจะทำให้แฟน ๆ หมุนตามอัตราที่ฉันไม่เคยได้ยินมาก่อน (เร็วสุด ๆ ) จากนั้นแม็คก็ปิดเครื่อง ระวังให้ดีด้วยรหัสนี้
Qix - MONICA ถูกยกเลิก

ดูเหมือนว่าใน macOS ของฉันกระบวนการลูกออกโดยอัตโนมัติหลังจากที่ผู้ปกครองออก ฉันไม่รู้ว่าทำไม
Yi Lin Liu

@YiLinLiu iirc ฉันใช้NSTaskหรือวางไข่ posix ดูstartTaskฟังก์ชั่นในรหัสของฉันที่นี่: github.com/neoneye/newton-commander-browse/blob/master/Classes/…
neoneye

11

กระบวนการลูกมีไพพ์ไปยัง / จากกระบวนการพาเรนต์หรือไม่? ถ้าเป็นเช่นนั้นคุณจะได้รับ SIGPIPE ถ้าเขียนหรือรับ EOF เมื่ออ่าน - อาจตรวจพบเงื่อนไขเหล่านี้


1
ฉันพบว่าสิ่งนี้ไม่ได้เกิดขึ้นอย่างน่าเชื่อถืออย่างน้อยใน OS X
Schof

ฉันมาที่นี่เพื่อเพิ่มคำตอบนี้ นี่เป็นวิธีแก้ปัญหาที่ฉันใช้สำหรับกรณีนี้
Dolda2000

ข้อควรระวัง: systemd ปิดการใช้งาน SIGPIPE โดยค่าเริ่มต้นในบริการที่จัดการ แต่คุณยังสามารถตรวจสอบการปิดท่อได้ ดูfreedesktop.org/software/systemd/man/systemd.exec.htmlภายใต้ IgnoreSIGPIPE
jdizzle

11

ฉันได้รับแรงบันดาลใจจากคำตอบอื่นที่นี่ฉันมาพร้อมกับโซลูชัน POSIX ทั้งหมดต่อไปนี้ แนวคิดทั่วไปคือการสร้างกระบวนการกลางระหว่างผู้ปกครองและเด็กที่มีวัตถุประสงค์เดียว: แจ้งให้ทราบเมื่อผู้ปกครองตายและฆ่าเด็กอย่างชัดเจน

การแก้ปัญหาประเภทนี้มีประโยชน์เมื่อไม่สามารถแก้ไขรหัสในลูกได้

int p[2];
pipe(p);
pid_t child = fork();
if (child == 0) {
    close(p[1]); // close write end of pipe
    setpgid(0, 0); // prevent ^C in parent from stopping this process
    child = fork();
    if (child == 0) {
        close(p[0]); // close read end of pipe (don't need it here)
        exec(...child process here...);
        exit(1);
    }
    read(p[0], 1); // returns when parent exits for any reason
    kill(child, 9);
    exit(1);
}

มีข้อแม้เล็ก ๆ สองประการด้วยวิธีนี้:

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

นอกจากนี้รหัสจริงที่ฉันใช้อยู่ใน Python นี่มันเพื่อความสมบูรณ์:

def run(*args):
    (r, w) = os.pipe()
    child = os.fork()
    if child == 0:
        os.close(w)
        os.setpgid(0, 0)
        child = os.fork()
        if child == 0:
            os.close(r)
            os.execl(args[0], *args)
            os._exit(1)
        os.read(r, 1)
        os.kill(child, 9)
        os._exit(1)
    os.close(r)

โปรดทราบว่าในขณะที่ย้อนกลับภายใต้ IRIX ฉันใช้รูปแบบพ่อแม่ / ลูกที่ฉันมีท่อระหว่างทั้งสองและการอ่านจากไปป์สร้าง SIGHUP หากทั้งสองตาย นั่นเป็นวิธีที่ฉันใช้ในการฆ่าส้อม () เด็ก ๆ ของเอ็ดโดยไม่จำเป็นต้องมีกระบวนการระดับกลาง
Alexis Wilke

ฉันคิดว่าข้อแม้ที่สองของคุณผิด pid ของเด็กเป็นทรัพยากรที่เป็นของผู้ปกครองและมันไม่สามารถเป็นอิสระ / นำมาใช้ใหม่จนกว่าผู้ปกครอง (กระบวนการกลาง) รออยู่บนมัน (หรือยกเลิกและให้ init รอบน)
.. GitHub หยุดช่วยน้ำแข็ง

7

ฉันไม่เชื่อว่าเป็นไปได้ที่จะรับประกันว่าจะใช้การเรียก POSIX มาตรฐานเท่านั้น เช่นเดียวกับชีวิตจริงเมื่อเด็กเกิดมามันมีชีวิตของมันเอง

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

ตัวอย่างเช่นไม่มีกระบวนการใดสามารถตรวจจับSIGKILLได้ เมื่อเคอร์เนลจัดการสัญญาณนี้มันจะฆ่ากระบวนการที่ระบุโดยไม่มีการแจ้งเตือนไปยังกระบวนการนั้น แต่อย่างใด

เพื่อขยายความคล้ายคลึง - วิธีมาตรฐานอื่น ๆ ในการทำคือให้เด็กฆ่าตัวตายเมื่อพบว่าไม่มีพ่อแม่อีกต่อไป

มีวิธี Linux เท่านั้นที่ทำกับprctl(2)- ดูคำตอบอื่น ๆ


6

ในขณะที่คนอื่น ๆ ชี้ให้เห็นการพึ่งพา pid ของผู้ปกครองจะกลายเป็น 1 เมื่อการออกของผู้ปกครองไม่สามารถพกพาได้ แทนที่จะรอให้ ID กระบวนการหลักเฉพาะรอให้ ID เปลี่ยน:

pit_t pid = getpid();
switch (fork())
{
    case -1:
    {
        abort(); /* or whatever... */
    }
    default:
    {
        /* parent */
        exit(0);
    }
    case 0:
    {
        /* child */
        /* ... */
    }
}

/* Wait for parent to exit */
while (getppid() != pid)
    ;

เพิ่ม micro-sleep ตามต้องการหากคุณไม่ต้องการสำรวจความคิดเห็นด้วยความเร็วเต็มที่

ตัวเลือกนี้ดูง่ายกว่าสำหรับฉันมากกว่าการใช้ไพพ์หรืออาศัยสัญญาณ


น่าเสียดายที่โซลูชันนั้นไม่แข็งแกร่ง เกิดอะไรขึ้นถ้ากระบวนการหลักตายก่อนที่คุณจะได้รับค่าเริ่มต้น เด็กจะไม่ออกจาก
dgatwood

@dgatwood คุณหมายถึงอะไร! เป็นครั้งแรกที่จะทำในผู้ปกครองก่อนที่จะเรียกgetpid() fork()หากผู้ปกครองเสียชีวิตก่อนหน้านั้นเด็กจะไม่อยู่ สิ่งที่อาจเกิดขึ้นคือเด็กออกไปใช้ชีวิตกับผู้ปกครองในขณะที่
Alexis Wilke

ในตัวอย่างที่มีการคาดเดานี้มันใช้งานได้ แต่ในรหัสโลกแห่งความจริงส้อมนั้นเกือบจะตามมาด้วย exec และกระบวนการใหม่ต้องเริ่มต้นใหม่โดยขอ PPID ของมัน ในช่วงเวลาระหว่างการตรวจสอบทั้งสองถ้าพ่อแม่ไปเด็กจะไม่มีความคิด นอกจากนี้คุณไม่น่าจะควบคุมทั้งรหัสหลักและลูกได้ (หรือคุณสามารถส่ง PPID เป็นอาร์กิวเมนต์ได้) ดังนั้นวิธีการแก้ปัญหาทั่วไปวิธีนี้ใช้ไม่ได้ผลดีมาก และตามความเป็นจริงถ้าระบบปฏิบัติการ UNIX เหมือนออกมาโดยไม่ต้องเริ่มต้นที่ 1 สิ่งต่าง ๆ มากมายจะทำให้ฉันไม่สามารถจินตนาการได้ว่ามีใครทำอยู่ดี
dgatwood

pass parent pid คืออาร์กิวเมนต์บรรทัดคำสั่งเมื่อทำการเรียกใช้สำหรับเด็ก
Nish

2
การสำรวจด้วยความเร็วเต็มที่จะไม่ดี
maxschlepzig

4

ติดตั้งตัวจัดการกับดักเพื่อจับ SIGINT ซึ่งฆ่ากระบวนการลูกของคุณถ้ามันยังมีชีวิตอยู่แม้ว่าโปสเตอร์อื่น ๆ จะถูกต้องว่ามันจะไม่จับ SIGKILL

เปิด. lockfile ที่มีการเข้าถึงแบบเอกสิทธิ์เฉพาะบุคคลและให้โพลสำรวจความคิดเห็นเด็กพยายามเปิด - หากเปิดสำเร็จกระบวนการลูกควรจบการทำงาน


หรือเด็กสามารถเปิด lockfile ในเธรดแยกต่างหากในโหมดบล็อกซึ่งในกรณีนี้อาจเป็นโซลูชันที่ดีและสะอาด อาจเป็นไปได้ว่ามีข้อ จำกัด ในการพกพา
Jean

4

วิธีนี้ใช้ได้ผลสำหรับฉัน:

  • ส่งผ่าน stdin ไปยังเด็กคุณไม่จำเป็นต้องเขียนข้อมูลใด ๆ ลงในสตรีม
  • เด็กอ่านอย่างไม่มีกำหนดจาก stdin จนถึง EOF EOF ส่งสัญญาณว่าพาเรนต์หายไป
  • นี่เป็นวิธีที่เข้าใจผิดและพกพาได้ในการตรวจสอบเมื่อผู้ปกครองหายไป แม้ว่าผู้ปกครองขัดข้อง OS จะปิดไปป์

นี่เป็นกระบวนการประเภทผู้ปฏิบัติงานที่มีอยู่ทำให้รู้สึกเมื่อผู้ปกครองยังมีชีวิตอยู่


@SebastianJylanki ฉันจำไม่ได้ว่าฉันพยายาม แต่มันอาจจะทำงานได้เพราะดั้งเดิม (กระแส POSIX) เป็นมาตรฐานที่ค่อนข้างทั่วทั้งระบบปฏิบัติการ
joonas.fi

3

ฉันคิดว่าวิธีที่รวดเร็วและสกปรกคือการสร้างท่อระหว่างเด็กกับผู้ปกครอง เมื่อผู้ปกครองออกจากเด็ก ๆ จะได้รับ SIGPIPE


SIGPIPE ไม่ได้ถูกส่งไปที่ pipe แต่จะส่งเฉพาะเมื่อเด็กพยายามเขียน
Alcaro

3

ผู้โพสต์บางคนได้พูดถึงไปป์และkqueueแล้ว ในความเป็นจริงคุณยังสามารถสร้างซ็อกเก็ตโดเมน Unix ที่เชื่อมต่อคู่โดยการsocketpair()โทร SOCK_STREAMประเภทซ็อกเก็ตที่ควรจะเป็น

ให้เราสมมติว่าคุณมีไฟล์อธิบายซ็อกเก็ตสองไฟล์ fd1, fd2 ตอนนี้fork()เพื่อสร้างกระบวนการลูกซึ่งจะสืบทอด fds ใน parent คุณปิด fd2 และใน child คุณปิด fd1 ตอนนี้แต่ละขั้นตอนสามารถpoll()เปิด fd ที่เหลือได้ที่ส่วนท้ายของมันสำหรับPOLLINเหตุการณ์ ตราบใดที่แต่ละด้านไม่ได้close()เป็น fd อย่างชัดเจนในช่วงอายุการใช้งานปกติคุณสามารถมั่นใจได้ว่าการPOLLHUPตั้งค่าสถานะควรบ่งบอกถึงการสิ้นสุดของอีกฝ่าย (ไม่ว่าจะสะอาดหรือไม่ก็ตาม) เมื่อได้รับแจ้งจากเหตุการณ์นี้เด็กสามารถตัดสินใจว่าจะทำอย่างไร (เช่นตาย)

#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <poll.h>
#include <stdio.h>

int main(int argc, char ** argv)
{
    int sv[2];        /* sv[0] for parent, sv[1] for child */
    socketpair(AF_UNIX, SOCK_STREAM, 0, sv);

    pid_t pid = fork();

    if ( pid > 0 ) {  /* parent */
        close(sv[1]);
        fprintf(stderr, "parent: pid = %d\n", getpid());
        sleep(100);
        exit(0);

    } else {          /* child */
        close(sv[0]);
        fprintf(stderr, "child: pid = %d\n", getpid());

        struct pollfd mon;
        mon.fd = sv[1];
        mon.events = POLLIN;

        poll(&mon, 1, -1);
        if ( mon.revents & POLLHUP )
            fprintf(stderr, "child: parent hung up\n");
        exit(0);
    }
}

./a.out &คุณสามารถลองรวบรวมรหัสหลักฐานของแนวคิดข้างต้นและทำงานในเช่นขั้ว คุณมีเวลาประมาณ 100 วินาทีในการทดสอบกับการฆ่า PID แม่ด้วยสัญญาณต่าง ๆ หรือมันจะออก ในกรณีใดกรณีหนึ่งคุณควรเห็นข้อความ "child: parent hang up"

เปรียบเทียบกับวิธีการใช้SIGPIPEตัวจัดการวิธีนี้ไม่ต้องลองwrite()โทร

วิธีนี้ยังสมมาตรเช่นกระบวนการสามารถใช้ช่องสัญญาณเดียวกันเพื่อตรวจสอบการมีอยู่ของกันและกัน

โซลูชันนี้เรียกใช้ฟังก์ชัน POSIX เท่านั้น ฉันลองสิ่งนี้ใน Linux และ FreeBSD ฉันคิดว่ามันควรจะทำงานกับยูนิกซ์อื่น ๆ แต่ฉันไม่ได้ทดสอบจริง ๆ

ดูสิ่งนี้ด้วย:

  • unix(7)ของหน้าคนลินุกซ์unix(4)สำหรับ FreeBSD, poll(2), socketpair(2), socket(7)บน Linux

เจ๋งมากฉันสงสัยว่ามันมีปัญหาความน่าเชื่อถือหรือไม่ คุณได้ทดสอบสิ่งนี้ในการผลิตหรือไม่? ด้วยแอพที่แตกต่างกัน?
Aktau

@ Aktau ฉันใช้ Python เทียบเท่ากับเคล็ดลับนี้ในโปรแกรม Linux ฉันต้องการมันเพราะตรรกะการทำงานของเด็กคือ "การทำความสะอาดอย่างดีที่สุดหลังจากที่พ่อแม่ออกแล้วก็ออกไปด้วย" อย่างไรก็ตามฉันไม่แน่ใจเกี่ยวกับแพลตฟอร์มอื่น ๆ จริงๆ ข้อมูลโค้ด C ทำงานบน Linux และ FreeBSD แต่นั่นคือทั้งหมดที่ฉันรู้ ... นอกจากนี้ยังมีบางกรณีที่คุณควรระวังเช่นผู้ปกครองฟอร์กอีกครั้งหรือผู้ปกครองเลิก fd ก่อนออกอย่างแท้จริง (เช่นการสร้างหน้าต่างเวลาสำหรับ สภาพการแข่งขัน).
Cong Ma

@Atau - นี่จะเชื่อถือได้อย่างสมบูรณ์
Omnifarious

1

ภายใต้POSIXที่exit(), _exit()และ_Exit()ฟังก์ชั่นที่กำหนดไว้ต่อไปนี้:

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

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

โปรดทราบว่าคุณอาจต้องอ่านงานพิมพ์จำนวนมากรวมถึงส่วนนิยามฐาน (คำจำกัดความ) รวมถึงข้อมูลบริการระบบสำหรับexit()และsetsid()และsetpgrp()- เพื่อให้ได้ภาพที่สมบูรณ์ (ฉันจะ!)


3
อืมมม เอกสารนั้นคลุมเครือและขัดแย้งกับเรื่องนี้ แต่ปรากฏว่ากระบวนการหลักจะต้องเป็นกระบวนการนำสำหรับเซสชันไม่ใช่เฉพาะกลุ่มกระบวนการ กระบวนการนำสำหรับเซสชันนั้นเข้าสู่ระบบอยู่เสมอและทำให้กระบวนการของฉันเข้ามาแทนที่เนื่องจากกระบวนการสร้างโอกาสสำหรับเซสชันใหม่นั้นเกินความสามารถของฉันในขณะนี้
Schof

2
SIGHUP จะได้รับการส่งไปยังกระบวนการลูกอย่างมีประสิทธิภาพเท่านั้นหากกระบวนการออกเป็นเปลือกเข้าสู่ระบบ opengroup.org/onlinepubs/009695399/functions/exit.html "การยุติกระบวนการไม่ได้ยกเลิกลูก ๆ โดยตรงการส่งสัญญาณ SIGHUP ตามที่อธิบายไว้ด้านล่างนี้เป็นการยุติเด็ก / ในบางสถานการณ์ /"
Rob K

1
@Rob: ถูกต้อง - นั่นคือสิ่งที่ฉันพูดให้คำพูดด้วยเช่นกัน: ในบางสถานการณ์ที่กระบวนการของเด็กจะได้รับ SIGHUP และมันก็เป็นการทำให้เข้าใจง่ายเกินกว่าจะพูดได้ว่ามันเป็นเพียงเชลล์ล็อกอินที่ส่ง SIGHUP ถึงแม้ว่ามันจะเป็นกรณีที่พบบ่อยที่สุด หากกระบวนการที่มีเด็กหลายคนตั้งค่าตัวเองเป็นกระบวนการควบคุมสำหรับตัวเองและเด็กจากนั้น SIGHUP จะถูกส่งไปยังลูก ๆ ของตนอย่างสะดวกสบายเมื่อผู้เชี่ยวชาญตาย OTOH กระบวนการที่ไม่ค่อยมีปัญหามากนัก - ดังนั้นฉันจึงหยิบมันขึ้นมามากกว่าการพูดให้ฟังอย่างมีนัยสำคัญจริงๆ
Jonathan Leffler

2
ฉันหลงกลไปกับมันสองสามชั่วโมงและไม่สามารถใช้งานได้ มันจะต้องจัดการกรณีที่ฉันมีภูตกับเด็กบางคนที่ทุกคนต้องตายเมื่อพ่อแม่ออกจาก
Rob K

1

หากคุณส่งสัญญาณไปที่ pid 0 โดยใช้เช่น

kill(0, 2); /* SIGINT */

สัญญาณนั้นถูกส่งไปยังกลุ่มกระบวนการทั้งหมดจึงฆ่าเด็กได้อย่างมีประสิทธิภาพ

คุณสามารถทดสอบได้อย่างง่ายดายด้วยสิ่งต่อไปนี้:

(cat && kill 0) | python

หากคุณกด ^ D คุณจะเห็นข้อความ"Terminated"เป็นตัวบ่งชี้ว่าล่าม Python ถูกฆ่าตายจริง ๆ แทนที่จะออกจากเพียงเพราะ stdin ถูกปิด


1
(echo -e "print(2+2)\n" & kill 0) | sh -c "python -" พิมพ์ 4 อย่างมีความสุขแทนการจบ
Kamil Szot

1

ในกรณีที่มีความเกี่ยวข้องกับคนอื่นเมื่อฉันวางไข่ JVM อินสแตนซ์ในกระบวนการลูกแยกจาก C ++ วิธีเดียวที่ฉันจะได้รับอินสแตนซ์ JVM เพื่อยุติอย่างถูกต้องหลังจากกระบวนการหลักเสร็จสมบูรณ์คือการทำต่อไปนี้ หวังว่าบางคนสามารถให้ข้อเสนอแนะในความคิดเห็นหากนี่ไม่ใช่วิธีที่ดีที่สุดในการทำเช่นนี้

1) โทรหาprctl(PR_SET_PDEATHSIG, SIGHUP)กระบวนการลูกที่แยกตามคำแนะนำก่อนเปิดแอป Java ผ่านทางexecvและ

2) เพิ่มตะขอปิดการประยุกต์ใช้ Java ที่โพจนกระทั่งแม่ของ PID เท่ากับ 1 Runtime.getRuntime().halt(0)แล้วทำยาก การสำรวจจะทำโดยเรียกใช้เชลล์แยกต่างหากที่รันpsคำสั่ง (ดู: ฉันจะค้นหา PID ของฉันใน Java หรือ JRuby บน Linux ได้อย่างไร )

แก้ไข 130118:

ดูเหมือนว่าไม่ใช่วิธีแก้ปัญหาที่แข็งแกร่ง ฉันยังคงดิ้นรนเล็กน้อยเพื่อเข้าใจความแตกต่างของสิ่งที่เกิดขึ้น แต่บางครั้งฉันก็ยังได้รับกระบวนการ JVM กำพร้าเมื่อใช้งานแอปพลิเคชันเหล่านี้ในเซสชัน / หน้าจอ SSH

แทนที่จะโพลสำหรับ PPID ในแอป Java ฉันเพียงแค่ขอให้ปิดเครื่องทำการล้างข้อมูลตามด้วยการหยุดอย่างหนักดังที่กล่าวมา จากนั้นฉันก็ทำให้แน่ใจว่าจะเรียกใช้waitpidในแอพแม่ C ++ ในกระบวนการลูกที่เกิดเมื่อตอนนั้นถึงเวลาที่จะยุติทุกอย่าง สิ่งนี้ดูเหมือนจะเป็นวิธีแก้ปัญหาที่มีประสิทธิภาพมากขึ้นเนื่องจากกระบวนการลูกรับรองว่าจะยุติในขณะที่ผู้ปกครองใช้การอ้างอิงที่มีอยู่เพื่อให้แน่ใจว่าลูกของมันจะยุติ เปรียบเทียบสิ่งนี้กับโซลูชันก่อนหน้าซึ่งมีกระบวนการหลักสิ้นสุดลงเมื่อใดก็ตามที่พอใจและให้เด็ก ๆ ลองคิดดูว่าพวกเขาเคยเป็นเด็กกำพร้ามาก่อนหรือไม่


1
การPID equals 1รอไม่ถูกต้อง พาเรนต์ใหม่อาจเป็น PID อื่น คุณควรตรวจสอบว่ามันเปลี่ยนจากพาเรนต์ดั้งเดิม (getpid () หน้า fork ()) เป็นพาเรนต์ใหม่ (getppid () ในเด็กไม่เท่ากับ getpid () เมื่อถูกเรียกก่อน fork ()
Alexis Wilke

1

อีกวิธีในการทำเช่นนี้คือ Linux เฉพาะคือให้สร้างพาเรนต์ใน PID เนมสเปซใหม่ แล้วมันจะเป็น PID 1 ใน namespace SIGKILLที่และเมื่อมันออกมาจากมันทั้งหมดของเด็กก็จะถูกฆ่าทันที

แต่น่าเสียดายที่ในการสั่งซื้อเพื่อสร้าง namespace PID CAP_SYS_ADMINใหม่ที่คุณต้องมี แต่วิธีนี้มีประสิทธิภาพมากและไม่ต้องการการเปลี่ยนแปลงกับผู้ปกครองหรือเด็กนอกเหนือจากการเปิดตัวครั้งแรกของผู้ปกครอง

ดูโคลน (2) , pid_namespaces (7)และยกเลิกการแชร์ (2)


ฉันต้องแก้ไขด้วยวิธีอื่น เป็นไปได้ที่จะใช้ prctl เพื่อทำกระบวนการเป็นกระบวนการเริ่มต้นสำหรับทุกคนเป็นลูกหลานและลูกหลานที่ดี ฯลฯ ...
Omnifarious

0

หากผู้ปกครองเสียชีวิต PPID ของเด็กกำพร้าเปลี่ยนเป็น 1 - คุณเพียงแค่ต้องตรวจสอบ PPID ของคุณเอง ในทางนี้คือการสำรวจที่กล่าวถึงข้างต้น นี่คือเปลือกชิ้นส่วนสำหรับสิ่งนั้น:

check_parent () {
      parent=`ps -f|awk '$2=='$PID'{print $3 }'`
      echo "parent:$parent"
      let parent=$parent+0
      if [[ $parent -eq 1 ]]; then
        echo "parent is dead, exiting"
        exit;
      fi
}


PID=$$
cnt=0
while [[ 1 = 1 ]]; do
  check_parent
  ... something
done

0

ฉันพบวิธีแก้ไขปัญหา 2 วิธีทั้งสองไม่สมบูรณ์แบบ

1. ฆ่าเด็กทั้งหมดโดย kill (-pid) เมื่อได้รับสัญญาณ SIGTERM
เห็นได้ชัดว่าวิธีนี้ไม่สามารถจัดการ "kill -9" ได้ แต่จะทำงานได้กับทุกกรณีและง่ายที่สุดเพราะไม่จำเป็นต้องจดจำกระบวนการลูกทั้งหมด


    var childProc = require('child_process').spawn('tail', ['-f', '/dev/null'], {stdio:'ignore'});

    var counter=0;
    setInterval(function(){
      console.log('c  '+(++counter));
    },1000);

    if (process.platform.slice(0,3) != 'win') {
      function killMeAndChildren() {
        /*
        * On Linux/Unix(Include Mac OS X), kill (-pid) will kill process group, usually
        * the process itself and children.
        * On Windows, an JOB object has been applied to current process and children,
        * so all children will be terminated if current process dies by anyway.
        */
        console.log('kill process group');
        process.kill(-process.pid, 'SIGKILL');
      }

      /*
      * When you use "kill pid_of_this_process", this callback will be called
      */
      process.on('SIGTERM', function(err){
        console.log('SIGTERM');
        killMeAndChildren();
      });
    }

ในทำนองเดียวกันคุณสามารถติดตั้ง 'exit' handler เหมือนข้างบนถ้าคุณเรียก process.exit ที่ไหนสักแห่ง หมายเหตุ: Ctrl + C และความผิดพลาดฉับพลันได้รับการประมวลผลโดยอัตโนมัติโดย OS เพื่อฆ่ากลุ่มกระบวนการดังนั้นไม่ต้องเพิ่มเติมที่นี่

2.use chjj / pty.jsจะวางไข่กระบวนการของคุณด้วยการควบคุมอาคารผู้โดยสารที่แนบมา
เมื่อคุณฆ่ากระบวนการปัจจุบันด้วยการฆ่า -9 กระบวนการลูกทั้งหมดจะถูกฆ่าโดยอัตโนมัติด้วย (โดย OS) ฉันเดาว่าเนื่องจากกระบวนการปัจจุบันถืออีกด้านหนึ่งของเทอร์มินัลดังนั้นหากกระบวนการปัจจุบันตายกระบวนการเด็กจะทำให้ SIGPIPE ตาย


    var pty = require('pty.js');

    //var term =
    pty.spawn('any_child_process', [/*any arguments*/], {
      name: 'xterm-color',
      cols: 80,
      rows: 30,
      cwd: process.cwd(),
      env: process.env
    });
    /*optionally you can install data handler
    term.on('data', function(data) {
      process.stdout.write(data);
    });
    term.write(.....);
    */

0

ฉันจัดการเพื่อทำโซลูชันแบบพกพาที่ไม่ใช่การทำโพลด้วย 3 กระบวนการด้วยการใช้เทอร์มินัลการควบคุมและเซสชัน นี่คือการสำเร็จความใคร่ด้วยตนเอง แต่ได้ผล

เคล็ดลับคือ:

  • กระบวนการ A เริ่มต้นขึ้น
  • กระบวนการ A สร้างไปป์ P (และไม่เคยอ่านจากมัน)
  • โปรเซส A แบ่งเป็นโปรเซส B
  • กระบวนการ B สร้างเซสชันใหม่
  • โปรเซส B จัดสรรเทอร์มินัลเสมือนสำหรับเซสชันใหม่นั้น
  • กระบวนการ B ติดตั้งตัวจัดการ SIGCHLD ที่จะตายเมื่อเด็กออก
  • กระบวนการ B ตั้งค่าตัวจัดการ SIGPIPE
  • กระบวนการ B ส้อมเป็นกระบวนการ C
  • กระบวนการ C ทำทุกสิ่งที่ต้องการ (เช่น exec () s ไบนารีที่ไม่ได้แก้ไขหรือรันตรรกะใด ๆ )
  • โปรเซส B เขียนไปที่ไพพ์ P (และบล็อกด้วยวิธีนั้น)
  • กระบวนการรอ () s ในกระบวนการ B และออกเมื่อมันตาย

ทางนั้น:

  • ถ้ากระบวนการ A ตาย: กระบวนการ B รับ SIGPIPE และตาย
  • ถ้ากระบวนการ B ตายตัว: การรอของกระบวนการ A () ส่งคืนและตายกระบวนการ C รับ SIGHUP (เนื่องจากเมื่อผู้นำเซสชันของเซสชันที่มีเทอร์มินัลเชื่อมต่อตายกระบวนการทั้งหมดในกลุ่มกระบวนการพื้นหน้าจะได้รับ SIGHUP)
  • ถ้ากระบวนการ C ตาย: กระบวนการ B รับ SIGCHLD และตายดังนั้นกระบวนการ A จะตาย

ข้อบกพร่อง:

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

0

แม้ว่า 7 ปีที่ผ่านมาฉันเพิ่งพบปัญหานี้เนื่องจากฉันใช้แอพพลิเคชั่น SpringBoot ที่ต้องการเริ่มต้น webpack-dev-server ในระหว่างการพัฒนาและจำเป็นต้องฆ่ามันเมื่อกระบวนการแบ็คเอนด์หยุดลง

ฉันพยายามใช้Runtime.getRuntime().addShutdownHookแต่ทำงานบน Windows 10 แต่ไม่ได้ใช้บน Windows 7

ฉันได้เปลี่ยนเป็นใช้เธรดเฉพาะที่รอให้กระบวนการเลิกทำงานหรือInterruptedExceptionซึ่งทำงานได้อย่างถูกต้องบน Windows ทั้งสองรุ่น

private void startWebpackDevServer() {
    String cmd = isWindows() ? "cmd /c gradlew webPackStart" : "gradlew webPackStart";
    logger.info("webpack dev-server " + cmd);

    Thread thread = new Thread(() -> {

        ProcessBuilder pb = new ProcessBuilder(cmd.split(" "));
        pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
        pb.redirectError(ProcessBuilder.Redirect.INHERIT);
        pb.directory(new File("."));

        Process process = null;
        try {
            // Start the node process
            process = pb.start();

            // Wait for the node process to quit (blocking)
            process.waitFor();

            // Ensure the node process is killed
            process.destroyForcibly();
            System.setProperty(WEBPACK_SERVER_PROPERTY, "true");
        } catch (InterruptedException | IOException e) {
            // Ensure the node process is killed.
            // InterruptedException is thrown when the main process exit.
            logger.info("killing webpack dev-server", e);
            if (process != null) {
                process.destroyForcibly();
            }
        }

    });

    thread.start();
}

0

ในอดีตจาก UNIX v7 ระบบกระบวนการตรวจพบเด็กกำพร้าของกระบวนการโดยการตรวจสอบ ID ผู้ปกครองของกระบวนการ ตามที่ฉันพูดในอดีตinit(8)กระบวนการของระบบเป็นกระบวนการพิเศษด้วยเหตุผลเพียงข้อเดียวเท่านั้น: มันไม่สามารถตายได้ ไม่สามารถตายได้เนื่องจากอัลกอริทึมเคอร์เนลจัดการกับการกำหนด id กระบวนการหลักใหม่ขึ้นอยู่กับข้อเท็จจริงนี้ เมื่อกระบวนการดำเนินการexit(2)เรียกใช้ (โดยการเรียกระบบกระบวนการหรือโดยงานภายนอกเป็นการส่งสัญญาณหรือสิ่งที่คล้ายกัน) เคอร์เนลกำหนดใหม่ให้กับลูกทั้งหมดของกระบวนการนี้ id ของกระบวนการเริ่มต้นเป็น id กระบวนการหลักของพวกเขา สิ่งนี้นำไปสู่การทดสอบที่ง่ายที่สุดและวิธีการพกพาที่รู้ว่ากระบวนการมีเด็กกำพร้าหรือไม่ เพียงตรวจสอบผลลัพธ์ของการgetppid(2)เรียกของระบบและหากเป็น id กระบวนการของinit(2) กระบวนการจากนั้นกระบวนการได้รับเด็กกำพร้าก่อนที่จะเรียกระบบ

ปัญหาสองประการเกิดขึ้นจากวิธีการนี้ซึ่งอาจนำไปสู่ปัญหา:

  • อันดับแรกเรามีความเป็นไปได้ที่จะเปลี่ยนinitกระบวนการเป็นกระบวนการผู้ใช้ดังนั้นเราจะมั่นใจได้อย่างไรว่ากระบวนการเริ่มต้นจะเป็นกระบวนการหลักของกระบวนการเด็กกำพร้าทุกครั้งหรือไม่ ในexitรหัสการโทรของระบบจะมีการตรวจสอบอย่างชัดเจนเพื่อดูว่ากระบวนการที่ดำเนินการเรียกนั้นเป็นกระบวนการเริ่มต้น (กระบวนการที่มี pid เท่ากับ 1) และถ้าเป็นกรณีนี้เคอร์เนลของความตื่นตระหนก (ไม่น่าจะรักษาได้อีกต่อไป ลำดับขั้นของกระบวนการ) ดังนั้นจึงไม่อนุญาตให้กระบวนการเริ่มทำการexit(2)เรียก
  • ประการที่สองมีสภาพการแข่งขันในการทดสอบขั้นพื้นฐานที่เปิดเผยข้างต้น id ของกระบวนการเริ่มต้นถูกสันนิษฐานว่าน่าจะเป็นในอดีต1แต่ไม่ได้รับการรับประกันโดยวิธีการ POSIX ซึ่งระบุ (ตามที่เปิดเผยในการตอบกลับอื่น ๆ ) ว่ามีเพียง id กระบวนการของระบบเท่านั้นที่สงวนไว้สำหรับจุดประสงค์นั้น เกือบจะไม่มีการติดตั้ง posix ทำสิ่งนี้และคุณสามารถสันนิษฐานได้ในระบบที่ได้รับยูนิกซ์ดั้งเดิมว่าการ1ตอบสนองของการgetppid(2)เรียกระบบก็เพียงพอที่จะถือว่ากระบวนการนี้เป็นเด็กกำพร้า อีกวิธีในการตรวจสอบคือการสร้างgetppid(2)ทางแยกและเปรียบเทียบค่านั้นกับผลลัพธ์ของการโทรใหม่ สิ่งนี้จะไม่ทำงานในทุกกรณีเนื่องจากการโทรทั้งสองไม่ได้เป็นแบบอะตอมมิกและกระบวนการหลักสามารถตายหลังจากfork(2)และก่อนการgetppid(2)เรียกระบบครั้งแรก กระบวนการparent id only changes once, when its parent does anออก (2) call, so this should be enough to check if thegetppid (2)result changed between calls to see that parent process has exit. This test is not valid for the actual children of the init process, because they are always children ofinit (8) `, แต่คุณสามารถสันนิษฐานได้อย่างปลอดภัยว่ากระบวนการเหล่านี้ไม่มีผู้ปกครอง (ยกเว้นเมื่อคุณแทนที่ระบบการเริ่มต้นกระบวนการ)

-1

ฉันผ่านผู้ปกครอง pid โดยใช้สภาพแวดล้อมไปยังเด็กแล้วตรวจสอบเป็นระยะถ้า / proc / $ ppid อยู่จากเด็ก

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