【问题标题】:A Linux Daemon and the STDIN/STDOUTLinux 守护进程和 STDIN/STDOUT
【发布时间】:2011-06-19 16:24:54
【问题描述】:

我正在开发一个 linux 守护程序,但在 stdin/stdout 上有一些问题。通常由于守护进程的性质,您没有任何标准输入或标准输出。但是,我的守护程序中确实有一个函数,该函数在守护程序第一次运行时调用,以指定守护程序成功运行所需的不同参数。当调用此函数时,终端变得如此缓慢,以至于我必须启动一个单独的 shell 并使用 top 杀死守护程序才能获得响应提示。现在我怀疑这与关闭 stdin/stdout 的分叉过程有关,但我不太确定如何解决这个问题。如果你们能对这种情况有所了解,那将不胜感激。谢谢。

编辑:

int main(argc, char *argv[]) {

/* setup signal handling */

/* check command line arguments */

pid_t pid, sid;

pid = fork();

if (pid < 0) {
exit(EXIT_FAILURE);
}

if(pid > 0){
exit(EXIT_SUCCESS);
}

sid = setsid();

if(sid < 0) {
exit(EXIT_FAILURE);
}

umask(027);

/* set syslogging */

/* do some logic to determine wether we are running the daemon for the first time and if we are call the one time function which uses fgets() to recieve some input */

while(1) {

/* do required work */

}

/* do some clean up procedures and exit */

return 0;
}

你们提到使用配置文件。这正是我存储通过输入接收的参数所做的。但是,我最初仍然需要通过标准输入从用户那里获取这些。判断我们是否是第一次运行的逻辑是基于配置文件的存在。

【问题讨论】:

  • 我们不知道没有看到一些代码。但听起来你做错了。您是说您希望守护程序能够采用标准输入/标准输出吗?您必须将它们通过套接字或其他东西管道。
  • 是的 Falmarri,但只有在调用该特定函数时,就像我说的那样,这是一次性的事情。在我的代码中,我在 fork(); 之后调用了这个函数;一个新的过程。
  • 您的终端并不是很慢,您生成的子进程继承了相同的 fd 并窃取了您的输入(您现在有多个阅读器),结果未定义(竞争条件)。

标签: c linux bash


【解决方案1】:

如果你想独立运行你的程序,使用the shell:(setsid &lt;command&gt; &amp;)。不要在你的程序中fork(),这将cause sysadmin nightmare。

Don't use syslog() 或 redirect stdout or stderr。

更好的是,使用daemon manager,例如守护程序工具、runit、OpenRC 和 systemd,为您的程序守护程序。

【讨论】:

    【解决方案2】:

    通常,守护程序的标准输入应该连接到/dev/null,这样如果从标准输入读取任何内容,您就会立即得到一个 EOF。通常,标准输出应该连接到一个文件——日志文件或/dev/null。后者意味着所有写入都会成功,但不会存储任何信息。同样,标准错误应该连接到/dev/null 或日志文件。

    所有程序,包括守护程序,都有权假定标准输入、标准输出和标准错误是适当打开的文件流。

    守护程序通常适合控制其输入的来源和输出的去向。很少有输入来自/dev/null 以外的情况。如果代码被编写为在没有标准输出或标准错误的情况下生存(例如,它打开一个标准日志通道,或者可能使用syslog(3)),那么关闭标准输出和标准错误可能是合适的。否则,可能适合将它们重定向到/dev/null,同时仍将消息记录到日志文件中。或者,您可以将 stdout 和 stderr 都重定向到日志文件 - 请注意不断增长的日志文件。

    您的反应迟缓到不可能的时间可能是因为您的程序在某处的读取循环中没有注意 EOF。它可能会提示用户在 /dev/null 上输入,并从 /dev/null 读取响应,但没有得到“y”或“n”,它会再次尝试,这会严重破坏您的系统。当然,代码在不处理 EOF 以及计算它获得无效响应的次数以及在合理的尝试次数(16、32、64)后停止愚蠢方面存在缺陷。如果程序期望得到有意义的输入并且继续没有得到它,它应该理智而安全地关闭商店。

    【讨论】:

      【解决方案3】:

      你们提到使用配置文件。这正是我存储通过输入接收的参数所做的。但是,我最初仍然需要通过标准输入从用户那里获取这些。判断我们是否是第一次运行的逻辑是基于配置文件的存在。

      不是读取标准输入,而是让用户自己编写配置文件;在分叉之前检查它是否存在,如果不存在则退出并返回错误。在守护程序中包含一个示例配置文件,并在守护程序的联机帮助页中记录其格式。你确实有一个联机帮助页,是吗?你的配置文件是文本的,是吗?

      此外,您的守护程序逻辑缺少关键步骤。在 fork 之后,但在调用 setsid 之前,您需要关闭 fds 0、1 和 2 并将它们重新打开到 /dev/null(不要尝试使用 fclose 和 fopen 执行此操作)。这应该可以解决您缓慢的终端问题。

      【讨论】:

      • 哎哟靠用户写配置文件?此外,我确实尝试在分叉过程之前进行任何 I/O,但我仍然遇到与缓慢终端相同的问题。
      • 是的,完全依赖用户编写配置文件。这就是 Unix 的做法。重新缓慢的终端,请参阅编辑。
      • "不要不要尝试使用fclose 和fopen 执行此操作。"为什么会这样?我们应该如何尝试这样做?
      • 您可以使用操作系统原语close 和open 来实现。如果你用fclose 和fopen 做这件事,I/O 缓冲区会搞砸。
      【解决方案4】:

      如果您坚持使用标准输入/键盘输入来启动守护程序(例如,获取一些您不想存储在文件中的神奇密码)然后在 之前处理所有 I/O fork().

      【讨论】:

      • 好吧,我想到了这一点,只是尝试确认一下,我仍然遇到与缓慢终端相同的问题。这让我很困惑。
      • 您的代码 sn-p 似乎另有说明。如果您想解决问题,请分享真实代码。
      【解决方案5】:

      使用配置文件。不要将 STDIN 或 STDOUT 与守护程序一起使用。守护进程旨在在后台中运行,无需用户交互。

      【讨论】:

        【解决方案6】:

        你的设计是错误的。守护进程不应通过 stdin 获取输入或将输出传递到 stdout/stderr。作为守护进程的一部分,您将关闭这些描述符。守护进程应该从命令行、配置文件或两者中获取配置参数。如果需要运行时输入,则必须读取文件、打开套接字等,但守护进程的重点是它应该能够在没有用户出现在控制台的情况下运行并执行其操作。

        【讨论】:

        • 除非这是由 inetd 运行的守护进程... :-)
        猜你喜欢
        • 2011-03-06
        • 1970-01-01
        • 2023-01-02
        • 2016-12-21
        • 2012-10-22
        • 2014-12-05
        • 2012-03-30
        • 2021-06-02
        • 2013-07-31
        相关资源
        最近更新 更多