【问题标题】:Why the signal handler doesn't process the signal为什么信号处理程序不处理信号
【发布时间】:2019-06-16 11:51:31
【问题描述】:

我正在尝试制作一个模拟命令 nohup 的程序。程序的第一个参数是要执行的命令的名称。 当终端关闭时,我的程序执行的程序一定不能得到通知,它将不得不忽略 SIGHUP。 如果我使用以下命令测试我的程序:

         ./mynohup sleep 120 &

然后我尝试从另一个终端发送一个 SIGHUP,当它应该免疫它时睡眠终止。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>

#include <signal.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>

#include "utils.h"

#define NOHUP_OUT_FILE      "nohup.out"

static void handle_signal(int signum)
{
    if(signum == SIGHUP)
    {
        printf("This is ignored\n");
    }
    else
    {
        printf("Not ignored\n");
    }
    fflush(stdout);
}

/* configure handlers */
static void set_signals(void)
{
    struct sigaction sa;
    int rc;

    /* TODO - ignore SIGHUP */
    memset(&sa, 0, sizeof(struct sigaction));
    sa.sa_handler = handle_signal;
    rc = sigaction(SIGHUP, &sa, NULL);

    DIE(rc == -1, "sigaction");

}

/* execute a new program */
static void exec_func(int argc, char **argv)
{
    int rc;
    int i;
    char **exec_args;
    int fd;

    set_signals();  /* ignore SIGHUP */

    if(isatty(STDOUT_FILENO))
    {
        fd = open(NOHUP_OUT_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
        DIE(fd < 0, "open");

        dup2(fd, STDOUT_FILENO);
        close(fd);
    }

    /* exec a new process */
    exec_args = malloc(argc * sizeof(*exec_args));
    DIE(exec_args == NULL, "malloc");

    for (i = 0; i < argc-1; i++)
        exec_args[i] = argv[i+1];
    exec_args[argc-1] = NULL;

    execvp(exec_args[0], exec_args);
    DIE(1, "execvp");
}

int main(int argc, char **argv)
{
    if (argc <= 1) {
        fprintf(stderr, "Usage: %s command_and_arguments\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    exec_func(argc, argv);

    return 0;
}

我尝试跳过创建新进程,并且信号处理程序运行良好。 如果信号处理程序采用以下形式,则程序可以工作

static void set_signals(void)
{
    struct sigaction sa;
    int rc;

    /* ignore SIGHUP */
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = SIG_IGN;

    rc = sigaction(SIGHUP, &sa, NULL);
    DIE(rc == -1, "sigaction");
}

我不明白为什么当我创建信号处理程序的第一个版本时,程序不起作用,而第二个版本却起作用。

提前致谢!

【问题讨论】:

  • 在信号处理程序中使用 stdio 函数是不安全的,顺便说一句。 man signal-safety了解详情。

标签: c linux signals


【解决方案1】:

所有 exec 函数都将捕获的信号的处置重置为其默认处置。

当您执行时,您的进程映像被销毁并被新程序的进程映像替换。在其中,指向您传递给 sigaction 的 handle_function 的指针不再具有意义,或者至少不再具有旧意义。操作系统可以对execve 处理的信号做的唯一明智的事情是重置它们。

SIG_IGN 的含义是通用的,独立于当前程序,这就是为什么SIG_IGN 可以并且现在可以被继承。

【讨论】:

  • 但这不是问题的答案。实际上,您无法安装信号处理程序,但您必须为进程不接收信号做一些事情,因为它会在会话挂起时被内核杀死。您必须添加到您的问题,该进程必须创建一个新的进程组并与管理 shell 的工作分开,否则它无论如何都会死。
  • @LuisColorado 在正确执行之前忽略 SIGHUP 可以完成这项工作。 OP已经发现了这一点。不需要创建新的进程组。问题是为什么 SIG_IGN 方法有效,但使用安装处理程序却无效。
  • @PSkockik,SIGHUPSIGINTRSIGQUIT 没有问题,但对于不可屏蔽、不可忽略的SIGSTOP 信号来说确实是个问题。需要新建一个进程组,解除控制终端与进程的关联,或者由作业控制中的其他进程(主要是shell)分配终端,重新开始接收终端信号。
  • 当您从该程序执行它们时,决定将SIGHUP 设置为默认值的程序怎么办?他们会被杀死,这不是nohup(1)程序的目的。
  • @LuisColorado 启动一个新的进程组也不会阻止SIGSTOP 交付(仅SIGTSTOP)。无论如何,OP只是试图模仿nohup(根据问题),就信号而言,几乎所有nohup所做的都是忽略SIGHUPgit.savannah.gnu.org/gitweb/?p=coreutils.git;a=blob;f=src/…
【解决方案2】:

execvp()execve() 系统调用的前端。

来自its linux manpage

在 execve() 期间保留所有进程属性,但以下情况除外:

  *  The dispositions of any signals that are being caught are reset to
      the default (signal(7)).

所以你安装的信号处理器被重置了。

【讨论】:

    【解决方案3】:

    更正:(请参阅原始文本的更改历史记录)

    nohup(1) 程序只是将程序名称 (nohup) 及其选项从argc/argv 参数转移到main,将stdout/stderr 重定向到一个文件(nohup.out) 以防其中一个或两个都指向 tty 设备,然后忽略 SIGHUPexecvp(*argv, argv); 以执行原始程序。它甚至根本没有fork(2)

    FreeBSD nohup 的源代码在here 可用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-06-19
      • 2011-04-23
      • 2017-11-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-07
      相关资源
      最近更新 更多