为啥在编写 linux 守护进程时必须与 tty 分离?
Posted
技术标签:
【中文标题】为啥在编写 linux 守护进程时必须与 tty 分离?【英文标题】:Why MUST detach from tty when writing a linux daemon?为什么在编写 linux 守护进程时必须与 tty 分离? 【发布时间】:2012-02-05 08:22:58 【问题描述】:当我尝试在 linux 下使用 C 编写守护程序时,有人告诉我应该在 fork 代码块之后添加以下代码:
/* Preparations */
...
/* Fork a new process */
pid_t cpid = fork();
if (cpid == -1)perror("fork");exit(1);
if (cpid > 0)exit(0);
/* WHY detach from tty ? */
int fd = open("/dev/tty", O_RDWR);
ioctl(fd, TIOCNOTTY, NULL);
/* Why set PGID as current PID ? */
setpgid(getpid(), 0);
我的问题是: 以上操作是不是一定要做?
【问题讨论】:
我认为部分原因是守护进程不应该写入输出或读取输入。如果你要开始,例如SSH 会话上的 HTTP 服务器,您不会期望稍后在会话中输出随机警告。 @JohnChadwick 您所说的确实是您在转换为守护程序时想要做的事情之一,但是您可以通过关闭 stdin、stdout 和 stderr 来实现。您从终端分离以避免某些信号(请参阅下面的答案)。 你能“不接受”我的回答并接受@AdamZalcman 的回答吗?他做得比我好得多。他对 setsid() 的看法是完全正确的,你应该使用它。 @AdamZalcman 哦,对了,我实际上忘记了关闭 std*。我在考虑更改进程组 ID 会阻止所有相关信号,但这会变得不那么有意义,至少在 SIGHUP 的情况下。 【参考方案1】:另一个答案是明确的并且在技术上是正确的(因此我相应地投了赞成票)。
另一个答案是:“不,不要编写自己守护进程的代码。”
请改为使用流程监督框架(如 daemontools 或 runit 或 launchd)来为您处理此问题。
传统的 UNIX 服务器是自守护的,因此会处理很多事情:当前工作目录、进程组和会话独立性、信号掩码和处置、文件系统根目录、权限、umask、打开文件描述符等。
但是,这些进程属性中的大部分或全部都是通过exec()
继承的,这意味着服务器进程通常可以“出生”与所需的进程组、工作目录、根等。几乎不需要做一切由您自己完成,尽管您通常仍需要自己管理特权操作和特权撤销。
(实际上,我认为编写自我守护程序存在长期风险。样板“背景”例程被复制和粘贴,并匆忙移植和扩展,程序员将时间花在辅助代码上而不是程序的主要目的。)
【讨论】:
很好的答案!守护进程应该是调用者的选择,而不是应用程序本身的强制。而对于调用者来说,现有的工具很多,无需在应用程序中重新发明。【参考方案2】:您必须解除您的守护进程与终端的关联,以避免发送与终端操作相关的信号(例如终端会话结束时的 SIGHUP 以及潜在的 SIGTTIN 和 SIGTTOU)。
但是请注意,使用 TIOCNOTTY ioctl
与终端解除关联的方式在很大程度上已经过时。你应该改用setsid()
。
守护进程离开其原始进程组的原因是没有接收到发送到该组的信号。请注意,setsid()
也会将您的进程置于其自己的进程组中。
【讨论】:
+1。警告:即使在 setid(2) 之后,如果不小心(再次 fork() 或 O_NOCTTY),也可能获得控制终端。 你的实验室能解决这个问题吗?为什么后台进程不能忽略这些信号?以上是关于为啥在编写 linux 守护进程时必须与 tty 分离?的主要内容,如果未能解决你的问题,请参考以下文章