gpt4 book ai didi

c - SIGHUP 信号处理以在 Unix 系统编程中取消命令的操作

转载 作者:行者123 更新时间:2023-11-30 14:43:57 25 4
gpt4 key购买 nike

我正在读一本关于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);
}

哪里

SIGHUPis the signal sent to the controlling process (session leader) associated with a controlling terminal if a disconnect is detected by the terminal interface.

所以基本上父进程调用fork然后退出。这样我们就保证了 child 不是小组长。 child 成为具有 setsid 的 session 领导者。

我不明白信号 SIG_UP 何时生成:从定义来看,它似乎是在关闭终端窗口时生成的,但从代码中的注释来看

/* *Ensure future opens won’t allocate controlling TTYs. */

看起来它是在不同的情况下生成的:什么时候生成的?

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

完整的功能如下:

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);
}
}

编辑

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

最佳答案

So basically ...

是的,父进程派生了一个子进程,并且该子进程执行 setsid()这样它将成为新进程组中的进程组领导者(也是唯一的进程),并且没有控制终端。最后一部分是关键。

(如果出于某种原因,子进程应与父进程在同一进程组中运行,则可以使用 int fd = open("/dev/tty", O_RDWR); if (fd != -1) ioctl(fd, TIOCNOTTY); 从控制终端分离。setsid() 更容易,通常更可取无论如何,让子进程在新的进程组中运行,因为可以向它及其子进程发送信号,而不会影响任何其他进程。)

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

每当控制终端断开连接时,就会向将该终端作为控制终端的每个进程传递 SIGHUP 信号。 (SIG_UP 只是一个拼写错误。信号名称没有下划线,只有特殊处理程序 SIG_DFLSIG_IGNSIG_ERR 有。)

如果守护进程出于任何原因打开终端设备 - 例如,因为库想要将错误消息打印到控制台,并打开 /dev/tty1或类似的做法——守护进程会无意中获取控制终端。除了插入open() , fopen() , opendir()等等,以保证其底层open()标志将包括 O_NOCTTY ,守护进程无法做太多事情来确保它不会无意中获取控制终端。相反,更简单的选择是假设它可能,并简单地确保这不会造成太多麻烦。为了避免最典型的问题,死于 SIGHUP当控制终端断开连接时,守护进程可以简单地忽略 SIGHUP 的传送。信号。

简而言之,这是一种腰带和吊带方式。 setsid()将进程与控制终端分离;和SIGHUP如果守护进程通过打开 tty 设备而不使用 O_NOCTTY 无意中获取控制终端,则会被忽略。标志。

关于c - SIGHUP 信号处理以在 Unix 系统编程中取消命令的操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53672944/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com