STDOUT_FILENO关闭时执行系统功能时出错

时间:2016-11-09 16:31:36

标签: c daemon

我有一个奇怪的问题。我不是很擅长C语言。我正在尝试创建一个守护进程来在Linux中执行我的基于bash脚本的服务。为了方便起见,我使代码变得简单。以下是我的代码。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <syslog.h>

int main(int argc, char* argv[])
{
    pid_t process_id = 0;
    pid_t sid = 0;
    process_id = fork();
    if (process_id < 0)
    {
        printf("fork failed!\n");
        exit(1);
    }
    if (process_id > 0)
    {
        printf("daemon creatd with process id %d \n", process_id);
        exit(0);
    }

    umask(0);
    sid = setsid();
    if(sid < 0)
    {
        fprintf(stderr, "Return error\n");
        exit(1);
    }

    chdir("/");
    close(STDIN_FILENO);
    close(STDOUT_FILENO);
    close(STDERR_FILENO);



    int status = system("ls");

    openlog("slog", LOG_PID|LOG_CONS, LOG_USER);
    syslog(LOG_INFO, "Returned status is %d", status);
    closelog();

    return (0);
}

如您所见,上述程序将执行system函数来执行ls命令并将退出代码输出到系统日志。

编译程序并运行后,在日志中我可以看到状态代码是512.但如果我注释掉以下行,

close(STDOUT_FILENO);

然后它完美无缺,我可以在日志中看到状态代码为0,

我可能做错了什么?

更新

我的程序非常大,我在实际环境中没有使用ls。我使程序简单,重现我面临的问题。另外,要查看程序的状态,我不是在查看输出,而是查看syslog中的状态代码。

2 个答案:

答案 0 :(得分:2)

你的守护进程创建看起来很好。但是,遵循约定的守护进程没有控制终端,您可以通过关闭所有标准文件描述符来完成,并调用setsid()来创建新会话以使守护进程成为会话领导者。因此,您无法使守护程序在stdout流上生成任何输出。如果你不关闭stdout流,它显然有效。

所以,你正在做的是尝试从终端向tty写东西。所以,你要么不需要为此目的守护进程,要么你需要写一个不同的文件(而不是stdout)。

答案 1 :(得分:2)

如果从其他评论和答案中不清楚,ls将其输出写入stdout。如果它无法写入stdout,它会终止并且(显然)设置状态代码为512(在任何情况下都为非零)。

子进程将从父进程继承stdout,因此如果不在父进程中关闭stdout,则子进程将有一个stdout写入,命令将成功。如果你关闭stdout,那么孩子无处可写输出并发生错误。

所以,虽然ls只是这个问题的一个例子,但是你的实际子进程正在写入stdout,导致同样的问题。

最简单的解决方法是假设你不想要孩子的输出,就是在命令行上重定向孩子的标准输出:

int status = system("ls >/dev/null 2>&1");

这会将stdout和stderr重定向到/dev/null,有效地丢弃孩子的输出,同时仍然给它一个可写的位置。