将数据写入stderr会使程序在程序成为守护程序后退出

时间:2014-03-10 08:29:39

标签: c linux unix daemon stderr

请查看此代码。它运行在CentOS6 64位。

#include<stdio.h>

int main(int argc, char **argv)
{
         fprintf(stderr, "output 1\n");
         printf("output 2\n");
         fflush(stdout);
         system("echo abc");
         fprintf(stderr, "output 3\n ");
         printf("output 4\n"); 
         fflush(stdout);

         daemon(0, 1);

         fprintf(stderr, "output 5\n");
         printf("output 6\n");
         fflush(stdout);
         system("echo abc");
         fprintf(stderr, "output 7\n");
         printf("output 8\n");
         fflush(stdout);

}

如果我运行它,我会看到这些消息:

output 1
output 2
abc
output 3
output 4
output 5
output 6
abc
output 7
output 8

如果我使用ssh登录并运行它,我会看到相同的结果。

但是,如果我使用二进制名称作为ssh的参数并运行它,程序将在调用守护进程(0,1)后向stderr写入数据时退出。假设二进制名称是myapp。我跑

ssh localhost myapp

我只会看到这些消息:

output 1
output 2
abc
output 3
output 4
output 5
output 6
abc

有谁知道为什么?根据调试,程序只在做完三件事后退出:

  1. 调用守护进程(0,1)。
  2. 调用系统以运行另一个应用程序或bash命令。
  3. 向stderr写点东西。
  4. 非常感谢!

1 个答案:

答案 0 :(得分:0)

如果从shell运行此命令,您可能会在输出4和输出5之间看到一个新的shell提示符(如果输出行之间有睡眠,这将更加明显。)

那是因为daemon()系统调用导致程序分成两个独立的进程。这称为“分叉”,可以使用fork()系统调用更紧密地控制。在fork之后,两个进程都保持指向打开文件描述符的指针:stdin,stdout和stderr。根据“man 3守护进程”,父进程在fork之后调用exit()

当您从SSH调用可执行文件时,SSH会话将运行一个进程。它会让孩子离开,主要过程退出。 SSH看到您发出的命令已完成,因此它会关闭SSH连接。这关闭了stdout和stderr。遗憾的是,您的子进程仍有一些工作要做,但它无法写入共享stderr,因为该文件描述符已关闭。如果您打印了一些调试信息,例如printf()fprintf()调用的返回值,您会发现它无法写入已关闭的文件描述符。

如果不是打印到stderr,而是打印到日志文件(大多数守护程序都会这样做),那么您将看到子进程将继续在后台运行并按预期编写。

如果您选择使用fork()代替daemon(),则可以让父母等到孩子完成。您可以使用pid_t waitpid(pid_t pid, int *stat_loc, int options);执行此操作。

您可能还想查看父级和子级之间发送的信号。当子进程终止时,它会将SIGCHILD发送给父进程。如果您想要反向通知,可以使用prctl(PR_SET_PDEATHSIG, SIGHUP);设置一个(仅限Linux),以便父级向孩子发送SIGHUP。