简体   繁体   English

linux - 由子进程中断的父进程的sleep()

[英]linux - sleep() of parent process interrupted by child process

When a child process is fork() ed , then the parent process can wait() for the child process to complete . 当子进程是fork() ed时,父进程可以wait()以完成子进程。 Suppose , just for experimenting , instead of wait() ing , if we make the parent process sleep() , then why does not it work ? 假设,只是为了试验,而不是wait() ing,如果我们让父进程sleep() ,那为什么它不起作用?

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

int main()
{
    pid_t child_id ;

    child_id = fork() ;

    if (child_id == 0)
    {
        printf("\nChild process");
        printf("\nChild process exiting");
    }
    else
    {
        printf("\nParent process");
        sleep(10);
        printf("\nParent process exiting");
    }
}

I guess SIGCHLD signal is causing the the parent process to wake from sleep() . 我猜SIGCHLD信号导致父进程从sleep()唤醒。 But why , it is a child process , they have different address space and resources , then how can it interfere in the matters of parent process ? 但为什么,它是一个子进程,它们有不同的地址空间和资源,那么它如何干扰父进程的问题呢?

Beware differences between systems. 注意系统之间的差异。 With this minor adaptation of your code, running on Mac OS X 10.9, the child dying does not affect the sleep(10) in the parent: 通过在Mac OS X 10.9上运行代码的这种小修改,孩子死亡不会影响父母的sleep(10)

Parent process

Child process
Child process exiting 1384590368

Parent process exiting 1384590378

As you can see, the parent exited 10 seconds or so later than the child did. 如你所见,父母比孩子晚了10秒左右。

#include <stdio.h>
#include <unistd.h>
#include <time.h>

int main(void)
{
    pid_t child_id;

    child_id = fork();

    if (child_id == 0)
    {
        printf("\nChild process");
        printf("\nChild process exiting %ld\n", (long)time(0));
    }
    else
    {
        printf("\nParent process\n");
        sleep(10);
        printf("\nParent process exiting %ld\n", (long)time(0));
    }
}

I got the same behaviour on a VM running an ancient version of Linux (2.6.16.60 kernel from 2008); 我在运行古老版本的Linux(2008年的2.6.16.60内核)的VM上有相同的行为; the parent died 10 seconds after the child did. 父母在孩子出生后10秒死亡。

So, if the behaviour you are asking "why does it not work?" 所以,如果你问的行为“为什么它不起作用?” is 'the parent exits immediately child dies', then your code does not prove that it does exit on either of two systems. 是'父母立刻退出孩子死了',那么你的代码并没有证明它确实退出了两个系统中的任何一个。 I can't categorically say the parent doesn't die promptly on your system, but it would be unexpected. 我不能断然说父母不会在你的系统上迅速死亡,但这是出乎意料的。

You might find this program useful for studying the behaviour of SIGCHLD signals: 您可能会发现此程序对于研究SIGCHLD信号的行为很有用:

#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

static siginfo_t sig_info;
static volatile sig_atomic_t sig_num;
static void *sig_ctxt;

static void catcher(int signum, siginfo_t *info, void *vp)
{
    sig_num = signum;
    sig_info = *info;
    sig_ctxt = vp;
}

static void set_handler(int signum)
{
    struct sigaction sa;
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = catcher;
    sigemptyset(&sa.sa_mask);

    if (sigaction(signum, &sa, 0) != 0)
    {
        int errnum = errno;
        fprintf(stderr, "Failed to set signal handler (%d: %s)\n", errnum, strerror(errnum));
        exit(1);
    }
}

static void prt_interrupt(FILE *fp)
{
    if (sig_num != 0)
    {
        fprintf(fp, "Signal %d from PID %d\n", sig_info.si_signo, (int)sig_info.si_pid);
        sig_num = 0;
    }
}

static void five_kids(void)
{
    for (int i = 0; i < 5; i++)
    {
        pid_t pid = fork();
        if (pid < 0)
            break;
        else if (pid == 0)
        {
            printf("PID %d - exiting with status %d\n", (int)getpid(), i);
            exit(i);
        }
        else
        {
            int status = 0;
            pid_t corpse = wait(&status);
            printf("Child: %d; Corpse: %d; Status = 0x%.4X\n", pid, corpse, (status & 0xFFFF));
            prt_interrupt(stdout);
            fflush(0);
        }
    }
}

int main(void)
{
    printf("SIGCHLD set to SIG_DFL\n");
    signal(SIGCHLD, SIG_DFL);
    five_kids();

    printf("SIGCHLD set to SIG_IGN\n");
    signal(SIGCHLD, SIG_IGN);
    five_kids();

    printf("SIGCHLD set to catcher()\n");
    set_handler(SIGCHLD);
    five_kids();

    return(0);
}

On Mac OS X 10.9 again, it produced: 在Mac OS X 10.9上再次产生:

SIGCHLD set to SIG_DFL
PID 52345 - exiting with status 0
Child: 52345; Corpse: 52345; Status = 0x0000
PID 52346 - exiting with status 1
Child: 52346; Corpse: 52346; Status = 0x0100
PID 52347 - exiting with status 2
Child: 52347; Corpse: 52347; Status = 0x0200
PID 52348 - exiting with status 3
Child: 52348; Corpse: 52348; Status = 0x0300
PID 52349 - exiting with status 4
Child: 52349; Corpse: 52349; Status = 0x0400
SIGCHLD set to SIG_IGN
PID 52350 - exiting with status 0
Child: 52350; Corpse: -1; Status = 0x0000
PID 52351 - exiting with status 1
Child: 52351; Corpse: -1; Status = 0x0000
PID 52352 - exiting with status 2
Child: 52352; Corpse: -1; Status = 0x0000
PID 52353 - exiting with status 3
Child: 52353; Corpse: -1; Status = 0x0000
PID 52354 - exiting with status 4
Child: 52354; Corpse: -1; Status = 0x0000
SIGCHLD set to catcher()
PID 52355 - exiting with status 0
Child: 52355; Corpse: -1; Status = 0x0000
Signal 20 from PID 52355
Child: 52356; Corpse: 52355; Status = 0x0000
PID 52356 - exiting with status 1
Child: 52357; Corpse: -1; Status = 0x0000
PID 52357 - exiting with status 2
Signal 20 from PID 52356
Child: 52358; Corpse: 52357; Status = 0x0200
Signal 20 from PID 52357
PID 52358 - exiting with status 3
Child: 52359; Corpse: 52356; Status = 0x0100
PID 52359 - exiting with status 4

The behaviour on Linux is similar — not quite identical: Linux上的行为类似 - 不完全相同:

SIGCHLD set to SIG_DFL
PID 14645 - exiting with status 0
Child: 14645; Corpse: 14645; Status = 0x0000
PID 14646 - exiting with status 1
Child: 14646; Corpse: 14646; Status = 0x0100
PID 14647 - exiting with status 2
Child: 14647; Corpse: 14647; Status = 0x0200
PID 14648 - exiting with status 3
Child: 14648; Corpse: 14648; Status = 0x0300
PID 14649 - exiting with status 4
Child: 14649; Corpse: 14649; Status = 0x0400
SIGCHLD set to SIG_IGN
PID 14650 - exiting with status 0
Child: 14650; Corpse: -1; Status = 0x0000
PID 14651 - exiting with status 1
Child: 14651; Corpse: -1; Status = 0x0000
PID 14652 - exiting with status 2
Child: 14652; Corpse: -1; Status = 0x0000
PID 14653 - exiting with status 3
Child: 14653; Corpse: -1; Status = 0x0000
PID 14654 - exiting with status 4
Child: 14654; Corpse: -1; Status = 0x0000
SIGCHLD set to catcher()
PID 14655 - exiting with status 0
Child: 14655; Corpse: 14655; Status = 0x0000
Signal 17 from PID 14655
PID 14656 - exiting with status 1
Child: 14656; Corpse: 14656; Status = 0x0100
Signal 17 from PID 14656
PID 14657 - exiting with status 2
Child: 14657; Corpse: 14657; Status = 0x0200
Signal 17 from PID 14657
PID 14658 - exiting with status 3
Child: 14658; Corpse: 14658; Status = 0x0300
Signal 17 from PID 14658
PID 14659 - exiting with status 4
Child: 14659; Corpse: 14659; Status = 0x0400
Signal 17 from PID 14659

Please read carefully the man pages of fork(2) , execve(2) , wait(2) ... 仔细阅读fork(2)的man手册, execve(2)wait(2) ...

The wait syscall do much more than just passively waiting for a child process. wait系统调用不仅仅是被动地等待子进程。 It cleans internal kernel state to avoid zombie processes . 它清除内部内核状态以避免僵尸进程

Use also strace(1) eg as strace -f on your program. 也可以在程序中使用strace(1)例如strace -f

And take several hours to read a good book like eg Advanced Linux Programming . 花几个小时读一本好书,比如Advanced Linux Programming Understanding processes need many hours, and we have not that much time to teach it to you. 理解过程需要很长时间,我们没有那么多时间教给你。 Please read books and continue experimenting like you do! 请阅读书籍并继续像你一样进行实验! Also, take some time to read the source code of free software (like the source code of some shell -eg bash or sash ) 另外,花点时间阅读自由软件的源代码(比如一些shell -eg bashsash的源代码)

BTW, you program is wrong on another point: you should always test for failure of fork (so always handle the three possible returns of fork : ==0 in child process, >0 in parent process, <0 on failure). 顺便说一句,你的程序是错误的另外一点:你应该总是测试失败fork (所以总是处理的三种可能的回报fork==0的子进程, >0的父进程, <0失败)。 Consider using ulimit (which calls setrlimit(2) inside the shell) to trigger such an error condition for testing purposes. 考虑使用ulimit (在shell中调用setrlimit(2) )来触发这样的错误条件以用于测试目的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM