请查看此代码。它运行在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
有谁知道为什么?根据调试,程序只在做完三件事后退出:
非常感谢!
答案 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。