【问题标题】: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 的情况下。

标签: c linux terminal daemon


【解决方案1】:

另一个答案是明确的并且在技术上是正确的(因此我相应地投了赞成票)。

另一个答案是:“不,不要编写自己守护进程的代码。”

请改为使用流程监督框架(如 daemontoolsrunitlaunchd)来为您处理此问题。

传统的 UNIX 服务器是自守护的,因此会处理很多事情:当前工作目录、进程组和会话独立性、信号掩码和处置、文件系统根目录、权限、umask、打开文件描述符等。

但是,这些进程属性中的大部分或全部都是通过exec() 继承的,这意味着服务器进程通常可以“出生”与所需的进程组、工作目录、根等。几乎不需要做一切由您自己完成,尽管您通常仍需要自己管理特权操作和特权撤销。

(实际上,我认为编写自我守护程序存在长期风险。样板“背景”例程被复制和粘贴,并匆忙移植和扩展,程序员将时间花在辅助代码上而不是程序的主要目的。)

【讨论】:

  • 很好的答案!守护进程应该是调用者的选择,而不是应用程序本身的强制。而对于调用者来说,现有的工具很多,无需在应用程序中重新发明。
【解决方案2】:

您必须解除您的守护进程与终端的关联,以避免发送与终端操作相关的信号(例如终端会话结束时的 SIGHUP 以及潜在的 SIGTTIN 和 SIGTTOU)。

但是请注意,使用 TIOCNOTTY ioctl 与终端解除关联的方式在很大程度上已经过时。你应该改用setsid()

守护进程离开其原始进程组的原因是没有接收到发送到该组的信号。请注意,setsid() 也会将您的进程置于其自己的进程组中。

【讨论】:

  • +1。警告:即使在 setid(2) 之后,如果不小心(再次 fork() 或 O_NOCTTY),也可能获得控制终端。
  • 你的实验室能解决这个问题吗?为什么后台进程不能忽略这些信号?
猜你喜欢
  • 2023-03-26
  • 1970-01-01
  • 2015-05-23
  • 2011-06-05
  • 2011-05-10
  • 1970-01-01
  • 2019-09-10
  • 2017-05-18
  • 2011-03-06
相关资源
最近更新 更多