C SIGHUP信号处理在Unix系统编程中取消命令的监视

C SIGHUP信号处理在Unix系统编程中取消命令的监视,c,unix,signals,systems-programming,sigaction,C,Unix,Signals,Systems Programming,Sigaction,我正在读一本关于Unix系统编程的书。在书中有一个创建守护进程的函数 我对部分代码不是很清楚,尤其是以下代码: struct sigaction sa; .... /* *Become a session leader to lose controlling TTY. */ if ((pid = fork()) < 0) { err_quit("%s: can’t fork", cmd); } else if (pid != 0) /* parent */ { exi

我正在读一本关于Unix系统编程的书。在书中有一个创建守护进程的函数

我对部分代码不是很清楚,尤其是以下代码:

struct sigaction    sa;
....
/* *Become a session leader to lose controlling TTY. */
if ((pid = fork()) < 0)
{
    err_quit("%s: can’t fork", cmd);
}
else if (pid != 0) /* parent */
{
    exit(0); //the parent will exit
}
setsid();

/* *Ensure future opens won’t allocate controlling TTYs. */
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGHUP, &sa, NULL) < 0)
{
    err_quit("%s: can’t ignore SIGHUP", cmd);
}
void daemonize(const char *cmd)
{
    int i, fd0, fd1, fd2;
    pid_t pid;
    struct rlimit       rl;
    struct sigaction    sa;
    /* *Clear file creation mask.*/
    umask(0);
    /* *Get maximum number of file descriptors. */
    if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
    {
        err_quit("%s: can’t get file limit", cmd);
    }
    /* *Become a session leader to lose controlling TTY. */
    if ((pid = fork()) < 0)
    {
        err_quit("%s: can’t fork", cmd);
    }
    else if (pid != 0) /* parent */
    {
        exit(0); //the parent will exit
    }
    setsid();
    /* *Ensure future opens won’t allocate controlling TTYs. */
    sa.sa_handler = SIG_IGN;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    if (sigaction(SIGHUP, &sa, NULL) < 0)
    {
        err_quit("%s: can’t ignore SIGHUP", cmd);
    }
    if ((pid = fork()) < 0)
    {
        err_quit("%s: can’t fork", cmd);
    }
    else if (pid != 0) /* parent */
    {
        exit(0);
    }
    /*
    *Change the current working directory to the root so
    * we won’t prevent file systems from being unmounted.
    */
    if (chdir("/") < 0)
    {
        err_quit("%s: can’t change directory to /", cmd);
    }
    /*
    *Close all open file descriptors.
    */
    if (rl.rlim_max == RLIM_INFINITY)
    {
        rl.rlim_max = 1024;
    }
    for (i = 0; i < rl.rlim_max; i++)
    {
        close(i);
    }
    /*
    *Attach file descriptors 0, 1, and 2 to /dev/null.
    */
    fd0 = open("/dev/null", O_RDWR);
    fd1 = dup(0);
    fd2 = dup(0);
    /*
    *Initialize the log file.
    */
    openlog(cmd, LOG_CONS, LOG_DAEMON);
    if (fd0 != 0 || fd1 != 1 || fd2 != 2) {
        syslog(LOG_ERR, "unexpected file descriptors %d %d %d", fd0, fd1, fd2);
        exit(1);
    }
}
它似乎是在不同的情况下生成的:何时生成

其次,它想忽略这个信号,因此它设置
sa.sa\u handler=SIG\u IGN
,然后调用
sigaction
。如果它忽略信号设置
SIG_IGN
作为其处理程序,为什么它要将传递给
sigaction
的掩码设置为
sigpemptyset(&sa.sa_mask)?我的意思是,如果没有处理程序,则不会使用在执行处理程序之前设置的掩码:是吗

完整功能如下所示:

struct sigaction    sa;
....
/* *Become a session leader to lose controlling TTY. */
if ((pid = fork()) < 0)
{
    err_quit("%s: can’t fork", cmd);
}
else if (pid != 0) /* parent */
{
    exit(0); //the parent will exit
}
setsid();

/* *Ensure future opens won’t allocate controlling TTYs. */
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGHUP, &sa, NULL) < 0)
{
    err_quit("%s: can’t ignore SIGHUP", cmd);
}
void daemonize(const char *cmd)
{
    int i, fd0, fd1, fd2;
    pid_t pid;
    struct rlimit       rl;
    struct sigaction    sa;
    /* *Clear file creation mask.*/
    umask(0);
    /* *Get maximum number of file descriptors. */
    if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
    {
        err_quit("%s: can’t get file limit", cmd);
    }
    /* *Become a session leader to lose controlling TTY. */
    if ((pid = fork()) < 0)
    {
        err_quit("%s: can’t fork", cmd);
    }
    else if (pid != 0) /* parent */
    {
        exit(0); //the parent will exit
    }
    setsid();
    /* *Ensure future opens won’t allocate controlling TTYs. */
    sa.sa_handler = SIG_IGN;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    if (sigaction(SIGHUP, &sa, NULL) < 0)
    {
        err_quit("%s: can’t ignore SIGHUP", cmd);
    }
    if ((pid = fork()) < 0)
    {
        err_quit("%s: can’t fork", cmd);
    }
    else if (pid != 0) /* parent */
    {
        exit(0);
    }
    /*
    *Change the current working directory to the root so
    * we won’t prevent file systems from being unmounted.
    */
    if (chdir("/") < 0)
    {
        err_quit("%s: can’t change directory to /", cmd);
    }
    /*
    *Close all open file descriptors.
    */
    if (rl.rlim_max == RLIM_INFINITY)
    {
        rl.rlim_max = 1024;
    }
    for (i = 0; i < rl.rlim_max; i++)
    {
        close(i);
    }
    /*
    *Attach file descriptors 0, 1, and 2 to /dev/null.
    */
    fd0 = open("/dev/null", O_RDWR);
    fd1 = dup(0);
    fd2 = dup(0);
    /*
    *Initialize the log file.
    */
    openlog(cmd, LOG_CONS, LOG_DAEMON);
    if (fd0 != 0 || fd1 != 1 || fd2 != 2) {
        syslog(LOG_ERR, "unexpected file descriptors %d %d %d", fd0, fd1, fd2);
        exit(1);
    }
}
void daemonize(const char*cmd)
{
int i,fd0,fd1,fd2;
pid_t pid;
结构rlimit-rl;
struct-sigaction-sa;
/**清除文件创建掩码*/
乌马斯克(0);
/**获取最大数量的文件描述符*/
if(getrlimit(RLIMIT_NOFILE,&rl)<0)
{
err_quit(“%s:无法获取文件限制”,cmd);
}
/**成为会议主持人,失去对TTY的控制*/
如果((pid=fork())<0)
{
err_quit(“%s:无法分叉”,cmd);
}
如果(pid!=0)/*父级,则为else*/
{
退出(0);//父级将退出
}
setsid();
/**确保未来开放不会分配控制TTY*/
sa.sa_handler=SIG_IGN;
sigemptyset(和sa.sa_面具);
sa.sa_标志=0;
if(sigaction(SIGHUP,&sa,NULL)<0)
{
err_quit(“%s:不能忽略SIGHUP”,cmd);
}
如果((pid=fork())<0)
{
err_quit(“%s:无法分叉”,cmd);
}
如果(pid!=0)/*父级,则为else*/
{
出口(0);
}
/*
*将当前工作目录更改为根目录,以便
*我们不会阻止卸载文件系统。
*/
如果(chdir(“/”)小于0)
{
err_quit(“%s:无法将目录更改为/”,cmd);
}
/*
*关闭所有打开的文件描述符。
*/
if(rl.rlim_max==rlim_无穷大)
{
rl.rlim_max=1024;
}
对于(i=0;i
编辑

我还有一个问题。为什么在函数中调用了两次
fork

所以基本上

是的,父进程分叉一个子进程,而该子进程分叉
setsid()
,因此它将是新进程组中的进程组长(也是唯一的进程),并且没有控制终端。最后一部分是关键

(如果子进程应该在与父进程相同的进程组中运行,可以使用
int fd=open(“/dev/tty”,O_RDWR);如果(fd!=-1)ioctl(fd,TIOCNOTTY);
从控制终端分离。
setid()
更简单,而且通常最好让子进程在新的进程组中运行,因为可以向它及其子进程发送信号,而不会影响任何其他进程。)

现在,每当没有控制终端的进程打开终端设备(tty或伪tty)时,该设备将成为其控制终端(除非在打开设备时使用了
O_NOCTTY
标志)

当控制终端断开时,SIGHUP信号被传送到每个将该终端作为其控制终端的进程。(SIG_-UP只是一个输入错误。信号名没有下划线,只有特殊处理程序
SIG_-DFL
SIG_-IGN
SIG-ERR
有下划线。)

如果守护进程出于任何原因打开终端设备(例如,因为库想要向控制台打印错误消息,并打开
/dev/tty1
或类似操作),守护进程将无意中获取控制终端。除了插入
open()。相反,更简单的选择是假设它可能会,并简单地确保这不会造成太多麻烦。为了避免最典型的问题,当控制终端断开连接时,由于
SIGHUP
而死亡,守护进程可以简单地忽略
SIGHUP
信号的传递


简言之,这是一种安全带和吊带的方法。
setId()
将进程与控制终端分离;如果守护进程在不使用
O_NOCTTY
标志的情况下打开tty设备,无意中获取了控制终端,那么
SIGHUP
将被忽略。

您的书实际上说的是
SIG_UP
?希望这是一个打印错误,而不是糟糕的编辑…信号是
SIGHUP
。即使在不同的章节,名字的拼写也是一样的。首先谢谢你的回答。关于
sigeptyset(&sa.sa_mask)呢
与sigaction(SIGHUP,&sa,NULL)<0一起使用
?为什么要为
NULL
处理程序设置掩码?@FrancescoBoi:
。sa_mask
是在信号处理程序执行期间阻塞的信号掩码,以及在发送信号时线程中阻塞的任何信号。必须使用
sigeptypyset(&sa.sa_mask)将其初始化为空集。让它空着是很常见的。
sigaction()
的第三个参数不是处理程序b