使用 dup2 重定向 stdout/stderr,然后重新声明

Redirect stdout/stderr using dup2, then resinstate later

背景:我们有一个嵌入式linux系统运行 Busybox(资源有限),我们有一个进程运行通常会吐在控制台上输出大量信息(通过 stdout / stderr),但我们希望 暂时 根据命令将其重定向到系统日志(使用 syslogd / logger) - 无需重新启动进程或重新启动。

使用找到的代码 here 效果很好,直到我们尝试 stop/close 记录器 fd,此时写入 stdout/stderr 失败并且一切都中断了。下面的示例:

int main()
{

    // Got command to direct all output to syslog:

    FILE *fl;
    fl = popen("logger","w");
    if(fl == NULL)
        return 1;
    fprintf(fl,"logger test new"); // This goes to syslogd
    int nf;
    nf = fileno(fl);
    dup2(nf,STDOUT_FILENO);
    dup2(nf,STDERR_FILENO);
    fprintf(stdout,"Written in stdout\n");
    fprintf(stderr,"Written in stderr\n");

    // ...some time later when we've logged enough things:

    pclose(fl);
    fprintf(stdout,"This will fail\n");
    fprintf(stderr,"Catch fire and die\n");
}

所以问题是,(如何)我们可以在完成日志记录后恢复原始状态?

dup()dup2() 上使用 RTFM 我不清楚我们如何才能实现这一目标,如果有可能 "reopen" 或者 freopen() stdout/stderr,但作为最后的手段,也许将 nf 指向 /dev/null/ 是可以接受的,这样至少不会崩溃。

您必须在 dup2 之前复制初始文件描述符,否则 dup2 将关闭它们并且无法恢复它们。

int stdout_copy = dup(STDOUT_FILENO);
dup2(nf, STDOUT_FILENO);

/* do something with stdout */

dup2(stdout_copy, STDOUT_FILENO);
close(stdout_copy);

引入错误处理,添加 fflush 调用以确保您不会在 stdlib 缓冲区中留下未写入的数据,您应该做好。

Linux 手册页明确指出 dup2 不会关闭 oldfd。不要忘记也关闭 nf.