【问题标题】:Force a program created using `exec` to perform unbuffered I/O强制使用 `exec` 创建的程序执行无缓冲 I/O
【发布时间】:2014-01-01 12:33:59
【问题描述】:

我正在尝试使用 pipeforkexec 与 C 中的外部程序进行交互。我想强制外部程序执行无缓冲 I/O。到目前为止,这是我的代码中的一个相关 sn-p:

...
pid = fork();
if (pid == (pid_t) 0)
{
    /* child process */

    /* make pipe connections to standard streams */             
    dup2(wpipe[0], STDIN_FILENO);
    dup2(rpipe[1], STDOUT_FILENO);

    /* close pipe endings */
    close(wpipe[0]); close(rpipe[0]);
    close(wpipe[1]); close(rpipe[1]);

    /* unbuffered I/O */
    setvbuf(stdin, NULL, _IONBF, BUFSIZ);
    setvbuf(stdout, NULL, _IONBF, BUFSIZ); 

    if (execl("path/to/some_binary", "path/to/some_binary", (char *) NULL) == -1)
    {
        fprintf(stderr, "exec failed\n");
        return EXIT_FAILURE;
    }   
    return EXIT_SUCCESS;
}
...

这不起作用,因为流在​​exec 调用中无法生存。所以使用setvbuf 强制无缓冲I/O 不起作用,因为程序映像(some_binary)创建了它自己的标准输入和标准输出流,并且不使用我称为setvbuf 的流。

当我在其代码中添加setvbuf 调用后重新构建some_binary 时,该程序确实可以工作。但是,如果您对传递给exec 的二进制文件没有任何控制权,怎么办?如何使它适用于像 catls 这样的 unix 命令?

【问题讨论】:

  • 查看unbuffer自带的expect命令。
  • 你为什么这么问??用例是什么????
  • 当使用两个管道与子进程进行双向通信时。使用 I/O 缓冲,父进程中的读取操作会因为输出已被子子进程缓冲而被阻塞。
  • 究竟child进程中运行的程序(execve之后)是什么?
  • 相关:There are existing utilities that can do it for you。忽略 Python 特定的代码或在 C 中重新实现它,例如,在伪 tty 上有一个选择循环。

标签: c io exec fork pipe


【解决方案1】:

在一般情况下你不能做你想做的事(在任意可执行文件的execve(2) 之后取消缓冲...)

缓冲是通过代码完成的(例如,通过与<stdio.h> 相关的一些libc 代码)。代码由execve-ed 的程序定义。

您可能会使用LD_PRELOAD tricks,它可能会在execve 之后调用setvbuf(stdin, NULL, _IONBF, BUFSIZ); (但在main....之前);但这仅适用于动态链接的可执行文件。

也许在你的LD_PRELOAD-ed 共享对象的一些初始化函数中使用一些constructorfunction attribute 可能有时会奏效。或者在共享对象中重新定义printffopen、...

附录

你评论说你这样做了:

使用两个管道与子进程进行双向通信。

那么你的方法是错误的。父进程应该监视两个管道,可能使用像poll(2) 这样的多路复用调用,然后(根据多路复用的结果)决定读取或写入子进程。实际上,您需要一些event loop:要么自己实现一个简单的事件循环(例如poll [反复调用多次] 在重复的循环中)或使用一些现有的(参见libeventlibev,或由诸如GTKQt 等工具包提供的那个...)

您也可以与select(2) 复用,但我推荐poll,因为C10K problem

阅读Advanced Linux Programming不会浪费你的时间...

关于您的下一个相关问题,另请参阅 this answer

【讨论】:

  • 感谢您的补充! poll 函数在 GNU C 中可用吗?为什么没有出现在 GNU C 的文档中?
  • 你如何称呼 GNU C?是GCC 编译器(与您的问题无关)还是Glibc(又名GNU libc)标准C 库? poll 记录在 POSIX poll 和手册页中!!并且 GNU libc 没有记录所有的 POSIX(但假设它是已知的)。顺便说一句,GNU libc 文档 select - 这是一种老式的多路复用方式。
  • 我的意思是 glibc。我找不到此页面上记录的投票功能:gnu.org/software/libc/manual/html_mono/libc.html
  • 我尝试实现轮询,但由于缓冲再次遇到问题 - stackoverflow.com/questions/20581466/…
  • 您需要一个 事件循环 poll 应该在循环内重复...(与 不同当你得到poll给出的可读可写文件描述符时的动作)
【解决方案2】:

您可以尝试暂停任务,然后修改其图像。您可以通过 gdb 或 /proc 文件系统来完成。 特别是,您可以访问通过 /proc 文件系统创建的任务的所有 fd。

更新:

如果缓冲是由glibc完成的,只需重写glibc中的相关函数,通过定义自己的函数,编译成共享库并与程序一起加载。

【讨论】:

    猜你喜欢
    • 2015-07-05
    • 1970-01-01
    • 2016-06-04
    • 1970-01-01
    • 1970-01-01
    • 2012-01-14
    • 2011-10-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多