请看这段代码。它运行在 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 的参数并运行它,程序将在调用daemon(0, 1) 后向stderr 写入数据时退出。假设二进制名称是 myapp。我跑
ssh localhost myapp
我只会看到这些消息:
output 1
output 2
abc
output 3
output 4
output 5
output 6
abc
有人知道为什么吗?根据调试,程序只做了三件事就退出了:
- 调用守护进程 (0, 1)。
- 调用系统运行另一个应用程序或 bash 命令。
- 写一些东西到 stderr。
非常感谢!
最佳答案
如果您从 shell 运行此命令,您可能会在输出 4 和输出 5 之间看到一个新的 shell 提示(如果输出行之间有休眠,这会更加明显)。
那是因为daemon()
系统调用导致程序 split 成两个独立的进程。这称为“fork”,可以使用 fork()
系统调用更紧密地控制它。在 fork 之后,两个进程都保留指向打开文件描述符的指针:stdin、stdout 和 stderr。根据“man 3 daemon”,父进程在fork之后调用exit()
。
当您从 SSH 调用可执行文件时,SSH session 将运行一个进程。它 fork 出一个 child ,然后主进程退出。 SSH 看到您发出的命令已经完成,因此它关闭了 SSH 连接。这将关闭 stdout 和 stderr。不幸的是,您的子进程仍有一些工作要做,但它无法写入共享标准错误,因为该文件描述符已关闭。如果您打印更多调试信息,例如 printf()
和 fprintf()
调用的返回值,您会发现它无法写入关闭的文件描述符。
如果您不打印到 stderr,而是打印到日志文件(大多数守护进程都这样做),那么您会看到子进程将继续在后台运行并按照您的预期进行写入。
如果您选择使用 fork()
而不是 daemon()
,您可以让父进程等待子进程完成。您可以使用 pid_t waitpid(pid_t pid, int *stat_loc, int options);
。
您可能还想查看父子之间发送的信号。当一个子进程死亡时,它会发送 SIGCHILD 给父进程。如果您想要反向通知,您可以使用 prctl(PR_SET_PDEATHSIG, SIGHUP);
设置一个(仅在 Linux 上),以便 parent 向 child 发送一个 SIGHUP。
关于c - 向stderr写入数据使程序成为守护进程后退出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22295279/