不确定如何确定子进程是否通过完成或信号终止

Unsure how to find out whether the Child process is terminated via completion or a signal

我正在尝试编写一个程序,其中

因此,我尝试执行的方式同样,我为我的父进程添加了一个 SIGINT 处理程序,我的猜测是当调用 execvp() 函数时,子进程将使用默认处理程序执行 SIGINT 进程。

 void sigint_handler(int signum){
    printf("");
 }

int main(int argc, char **argv){

pid_t pid = fork();

if(pid<0)
{
printf("fork: error");
exit(-1);

}
else if(pid>0){
    int status;
    signal(SIGINT,sigint_handler);
    pid_t child_pid= waitpid(pid, &status, 0);
    printf("");
}
else if(pid==0){
    printf("");
    if(execvp(argv[1],&argv[1]) == -1) {
    printf(" ");
    exit(-1);
      }

}

}

这段代码是否足以处理相同的问题?此外,如果我的子进程被 SIGINT 或 SIGSEGV 等终止,我将如何确定我的子进程是在完成后终止还是由于信号以及使用了什么信号而终止。

来自wait()或friends for status的值告诉你——普通退出使用宏WIFEXITED()WIFEXITSTATUS(),或者WIFSIGNALED()WIFTERMSIG() 对于信号(当然 IF 宏测试它是如何退出的)。还有 WIFSTOPPED()WIFCONTINUED()WSTOPSIG() 用于作业控制。

参见 POSIX waitpid()(也有文档 wait())。大多数 Unix-based 系统也提供 WCOREDUMP(),但 POSIX 并不强制要求。您还可以查看以十六进制(4 位数字)表示的退出状态,很快就能发现模式,但您应该使用宏。


For the first part of my question, would my above-mentioned code ignore the SIGINT signal while it is still in the parent part of the process?

注意:avoid using printf() in signal handlers.

不清楚您的信号处理代码是如何工作的;您从问题中删除了太多代码。但是,您现在已经添加了足够多的缺失代码,而且正如我猜想的那样,您的代码不是防弹的,因为显示的 signal() 调用是在 fork() 之后和 parent 过程。您应该在调用 fork() 之前禁用信号;那么child应该re-enable吧。这样可以正确保护进程。

例如(文件 sig11.c — 使用 gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes sig11.c -o sig11 编译创建 sig11):

#include <assert.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    if (argc < 2)
    {
        fprintf(stderr, "Usage: %s command [arg ...]\n", argv[0]);
        return 1;
    }
    bool handle_signals = (signal(SIGINT, SIG_IGN) != SIG_IGN);

    pid_t pid = fork();

    if (pid < 0)
    {
        fprintf(stderr, "fork: error\n");
        return 1;
    }
    else if (pid > 0)
    {
        int status;
        pid_t child_pid = waitpid(pid, &status, 0);
        printf("child %d exited with status 0x%.4X\n", (int)child_pid, status);
    }
    else
    {
        assert(pid == 0);
        if (handle_signals)
            signal(SIGINT, SIG_DFL);
        execvp(argv[1], &argv[1]);
        fprintf(stderr, "failed to exec %s\n", argv[1]);
        exit(-1);
    }
    return 0;
}

示例输出:

$ ./sig11 sleep 40
^Cchild 19721 exited with status 0x0002
$

我们可以讨论您是否应该改天使用 sigaction() rather than signal()

handle_signals的检查意味着如果程序是运行并忽略了SIGINT,它不会突然启用信号。这是标准的防御技术。请注意,错误是在标准错误上报告的,而不是标准输出。无需检查 execvp() 中的 return 值;它不会 return 如果它成功了,如果它 return 它最终失败了。使用 errnostrerror() 可以报告更好的错误,但显示的内容并非完全不合理。检查是否有足够的参数是另一种防御措施;报告正确的用法是有礼貌的。我还将 if / else if / else if 转换为 if / else if / else + 断言——第三个测试是不必要的。


使用来自 <sys/wait.h> 的宏:

#include <assert.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    if (argc < 2)
    {
        fprintf(stderr, "Usage: %s command [arg ...]\n", argv[0]);
        return 1;
    }
    bool handle_signals = (signal(SIGINT, SIG_IGN) != SIG_IGN);

    pid_t pid = fork();

    if (pid < 0)
    {
        fprintf(stderr, "fork: error\n");
        return 1;
    }
    else if (pid > 0)
    {
        int status;
        int corpse = waitpid(pid, &status, 0);
        printf("child %d exited with status 0x%.4X\n", corpse, status);
        if (WIFEXITED(status))
            printf("child %d exited normally with exit status %d\n", corpse, WEXITSTATUS(status));
        else if (WIFSIGNALED(status))
            printf("child %d exited because of signal %d\n", corpse, WTERMSIG(status));
        else
            printf("child %d neither exited normally nor because of a signal\n", corpse);
    }
    else
    {
        assert(pid == 0);
        if (handle_signals)
            signal(SIGINT, SIG_DFL);
        execvp(argv[1], &argv[1]);
        fprintf(stderr, "failed to exec %s\n", argv[1]);
        exit(-1);
    }
    return 0;
}

示例输出:

$ ./sig11 sleep 4
child 19839 exited with status 0x0000
child 19839 exited normally with exit status 0
$ ./sig11 sleep 10
^Cchild 19842 exited with status 0x0002
child 19842 exited because of signal 2
$