【问题标题】:Forked process writing to closed file descriptor stderr分叉进程写入已关闭的文件描述符 stderr
【发布时间】:2015-09-25 06:18:40
【问题描述】:

我遇到了一个需要帮助理解的场景。示例代码如下。

fclose(stdin);
fclose(stdout);
fclose(stderr);
int sockets[2];
assert(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == 0);
int socketA = sockets[0];
int socketB = sockets[1];
assert(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == 0);
int socketC = sockets[0];
int socketD = sockets[1];
assert(socketA == 0);
assert(socketB == 1);
assert(socketC == 2);
assert(socketD == 3);
unblock_socket(socketA);
unblock_socket(socketB);
unblock_socket(socketC);
unblock_socket(socketD);
unsigned char buffer = 0;
assert(::recv(socketA, &buffer, sizeof(buffer), 0) == -1);
assert(::recv(socketC, &buffer, sizeof(buffer), 0) == -1);
assert(::recv(socketB, &buffer, sizeof(buffer), 0) == -1);
assert(::recv(socketD, &buffer, sizeof(buffer), 0) == -1);
pid_t pid = fork();
if (pid == -1)
    assertfalse;
else if (pid > 0)
{
    int status = 0;
    waitpid(pid, &status, 0);
}
else
{
    assert(execv("/usr/sbin/chown", nullptr) == 0) // Prints "usage: chown [-fhv] [-R [-H | -L | -P]]..." to stderr
    _exit(EXIT_FAILURE);
}
assert(::recv(socketA, &buffer, sizeof(buffer), 0) == -1);
assert(::recv(socketC, &buffer, sizeof(buffer), 0) == -1);
assert(::recv(socketB, &buffer, sizeof(buffer), 0) == -1);
assert(::recv(socketD, &buffer, sizeof(buffer), 0) == -1); // Assertion failed!. Reading from socket D we see "usage: chown [-fhv] [-R [-H | -L | -P]]..."

我已经关闭了 stderr(文件描述符 2)并随后打开了套接字 C,它采用了现在可用的文件描述符 2。但是 chown 似乎仍然设法将其错误输出写入文件描述符 2。如果 chown 没有意识到 stderr 已关闭并且文件描述符 2 被重新分配,因此不写入?

谢谢

【问题讨论】:

    标签: sockets unix exec fork file-descriptor


    【解决方案1】:

    这完全符合其应有的表现。

    你关闭 stderr,然后调用 socketpair()。 SocketC == 2(即现在的标准错误)。

    chown() 继承了这些文件描述符,因此当它写入 stderr 时,它实际上是在写入父级从 SocketD 读取的 SocketC。

    【讨论】:

    • 那么关闭标准错误是不好的做法吗?因为如果我关闭 stderr 然后它的文件描述符被重用(例如,对于套接字,在这种情况下),那么第三方代码可以在任何时候写入该套接字?谢谢!
    【解决方案2】:

    已解决 - 我应该将 stdin/stdout/stderr 重定向到 /dev/null,因此不会重新分配文件描述符 0-2。这是一个守护程序应用程序。

    但是我仍然觉得奇怪的是 chown 正在打印到文件描述符 2。在我看来 chown 打印使用

    dprintf(STDOUT_FILEERR, "usage: chown [-fhv] [-R [-H | -L | -P]]...");
    

    或许

    const char error[] = "usage: chown [-fhv] [-R [-H | -L | -P]]...";
    write(STDOUT_FILENO, error, sizeof(error) - 1);
    

    而不是

    fprintf(stderr, "usage: chown [-fhv] [-R [-H | -L | -P]]...");
    

    或类似的东西...如第一个示例中 dprintf 写入 socketC(文件描述符 2)会导致问题,但在第二个示例中 fprintf 意识到 stderr FILE* 对象已关闭并且不执行写入。

    任何进一步的见解将不胜感激。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-12
      • 2012-04-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-22
      • 1970-01-01
      相关资源
      最近更新 更多