【问题标题】:Using signals in a child process在子进程中使用信号
【发布时间】:2019-03-15 20:45:56
【问题描述】:

我想创建一个简单的程序,它使用 fork 并创建一个子进程,使用 pause 正在等待。我希望这个子进程在它从父进程获得特定信号后启动。我写的代码:

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

int main() {
    pid_t c = fork();
    if (c == 0) {
        pause();
        printf("signal was given");
    }
    if (c > 0)
        kill(c, SIGINT);

    return 0;
}

我认为 kill 给带有 pid c(child) 的进程一个特定的信号,我认为 pause 只是等待一个信号来取消暂停该进程。但是,在这种情况下,运行此程序没有任何结果。我还尝试使用signal(SIGINT, handler) 向孩子添加信号捕获功能,并创建一个打印所需结果的处理程序函数,但它仍然无法正常工作。有什么想法吗?

【问题讨论】:

    标签: c operating-system


    【解决方案1】:

    如果您将SIGINT(其默认处置是终止进程)发送给既不阻止也不处理它​​的进程,则该进程将终止。

    如果你想让信号中断像pause()这样的阻塞调用,它需要有一个处理程序。

    但是简单地安装一个处理程序会引入竞争条件:

    if (c == 0 ){
        //< if the signal arrives here the child dies
        signal(SIGINT, handler);
        //< if the signal arrives here then nothing happens except the handler is run
        pause(); //< if the handler arrives here then pause gets interrupted
        printf("signal was given\n");
        exit(0);
    }
    

    要消除竞争条件,您需要

    1. 阻塞父级中的信号,使子级以阻塞的信号开始
    2. 在子进程中安装处理程序
    3. 在一个原子步骤中解锁信号和pause()

    要实现3.一步,你需要sigsuspend()而不是pause()

    #include <stdio.h>
    #include<sys/types.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<sys/wait.h>
    #include<signal.h>
    
    void handler(int Sig){}
    
    int main()
    {
        sigset_t sigint, oldmask; sigemptyset(&sigint); sigaddset(&sigint, SIGINT);
        sigprocmask(SIG_BLOCK, &sigint, &oldmask);
    
        pid_t c=fork();
        if(0>c) return perror(0),1;
        if (c==0){
            signal(SIGINT, handler);
            sigdelset(&oldmask,SIGINT); /*in (the unlikely) case the process started with SIGINT blocked*/
            sigsuspend(&oldmask);
            printf("signal was given\n");
            exit(0);
        }
        kill(c,SIGINT);
        wait(0);
        return 0; 
    }
    

    或者,您可以使用 sigwait() 并完全不需要处理程序:

    #include <stdio.h>
    #include<sys/types.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<sys/wait.h>
    #include<signal.h>
    
    int main()
    {
        sigset_t sigint, oldmask; sigemptyset(&sigint); sigaddset(&sigint, SIGINT);
        sigprocmask(SIG_BLOCK, &sigint, &oldmask);
    
        pid_t c=fork();
        if(0>c) return perror(0),1;
        if (c==0){
            int sig; sigwait(&sigint,&sig);
            printf("signal was given\n");
            exit(0);
        }
        kill(c,SIGINT);
        wait(0);
        return 0; 
    }
    

    【讨论】:

    • 在这种情况下,您只需在调用fork() 之前安装信号处理程序即可消除竞争条件。
    • @AndrewHenle 这本身并不能消除在输入pause() 之前信号可能到达的竞争条件,在这种情况下,孩子现在会无限期地被阻塞。
    • 嗯,是的,因为该代码中有两个竞争条件 - 获得一个信号处理程序,以及 pause() 的时间安排。在信号处理程序中放置一个信号量,在处理程序中调用sem_post(),并使用sem_wait()而不是pause()。然后进入父/子/信号兔子洞我们去...... ;-)
    • 感谢您的解释!
    【解决方案2】:

    你有两个问题:

    1. 子进程在调用pause()之前收到一个信号。
    2. SIGINT 默认会杀死一个进程,所以printf 永远不会被执行。

    试试这个:

    void handler(int signum)
    {
        //nothing here
    }
    
    int main()
    {
        pid_t c = fork();
        if (c == 0) {
            signal(SIGINT, handler);
            pause();
            printf("signal was given");
        }
        if (c > 0) {
            sleep(1); // <-- give the child process some time to pause()
            kill(c, SIGINT);
        }
    
        return 0;
    }
    

    【讨论】:

    • 非常感谢您的解决方案先生。为什么在暂停之前给出信号?我可以在不使用睡眠呼叫的情况下以某种方式执行此操作吗?
    • @Labyrinthian,在多任务操作系统中,进程同时执行(并行或看似并行),因此无法知道哪个进程的哪个“部分”将首先执行。在这种情况下,似乎大多数时候信号是在孩子执行pause() 之前传递的,但实际上并不能保证。至于比sleep()更好的解决方案,相信PSkocik已经给你两个了:)
    猜你喜欢
    • 2015-05-25
    • 2020-03-25
    • 1970-01-01
    • 2019-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-11
    相关资源
    最近更新 更多