unix pipe() 和dup2()的使用方法和原理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了unix pipe() 和dup2()的使用方法和原理相关的知识,希望对你有一定的参考价值。

在父子进程通信以及redirection中,经常用pipe(),close()以及dup2()函数来实现。向各位请教,两者的使用方法和原理。谢谢大家。
...可以更清楚地解释一下原理么?

参考技术A pipe(),父子进程之间通讯工具。pipe有读写两端,用int表示。0-read, 1-write.一端进程将数据写入write内,另一进程由read段读出。使用参数为一个int数组,共两个元素,用以表示读写两端的状态。
close(),用于关闭清空pipe某端,参数为表示pipe端口数组二元素之一。
dup2(),用于redirection, 将pipe某端与标准i/o相连,即将standout information 写入或者pipe内容被standin独出。如dup2(data[1], STDOUT_FINENO).是进程与外界相连的一个工具。本回答被提问者采纳
参考技术B 1. 编写程序w.c随机产生1000个的整数,把他们写到屏幕上,每行一个。
2. 编写程序r.c从键盘读入整数计算它们的平均数,遇到文件结束符^D终止。
3. 合并程序w.c和r.c为wr.c用fork产生两个进程,子进程为完成w.c的工作,父进程完成r.c的工作,使得父进程产生的整数通过pipe传递到子进程。
五、实验报告要求
给出wr.c的程序源代码。

六、参考实例
wr.c:

#include <stdio.h>

main()

int i,pid,r,x,count,p[2];

long int sum;

if(pipe(p)==-1)printf("pipeerr\n");exit(1);

if((pid=fork())==-1)printf("forkerr\n");exit(1);

if(pid==0)

close(p[0]);

dup2(p[1],1);

srand(getpid());

for(i=0;i<1000;i++)printf("%d\n",rand());

exit(0);



count=0;sum=0;

if(p<=0)printf("error open\n");exit(0);

close(p[1]);

dup2(p[0],0);

while(1)

x=0;

r=scanf("%d",&x);

if(r<=0)break;

count++;

sum+=x;



printf ("avg=%lf\n", (0.0+sum)/count);

wait(&x);

使用 stdout/stderr 以外的管道与子进程通信

【中文标题】使用 stdout/stderr 以外的管道与子进程通信【英文标题】:Communicate with a subprocess using pipes on other than stdout/stderr 【发布时间】:2015-02-13 17:46:43 【问题描述】:

这就是我 fork/exec 一个子进程并与之通信的方式(伪代码):

int cout_pipe[2];
pipe(cout_pipe);
fork();
if (child)

  dup2(cout_pipe[1],STDOUT_FILENO);
  execv();

if (parent)

  File* cout_file = fdopen(cout_pipe[0], "r");

我的问题是:

我真的不想读取子进程的标准输出,我希望子进程在标准输出以外的文件描述符上写入。而且,我希望从该文件描述符中读取上述代码。

子进程用python写,主进程用C++写。

我想我可以改变那行,从:

dup2(cout_pipe[1],STDOUT_FILENO);

dup2(cout_pipe[1],MY_OWN_RANDOM_FD_NUMBER);

并以我写入 MY_OWN_RANDOM_FD_NUMBER 而不是标准输出的方式对子进程进行编码。

这是一个好的解决方案吗?可行吗?我怎样才能找出一个好的 MY_OWN_RANDOM_FD_NUMBER?有更好的选择吗?

【问题讨论】:

【参考方案1】:

除非您需要特定的 fd,例如标准输出,否则 dup2 没有任何意义。只需将cout_pipe[1] 转换为字符串,并将其作为命令行参数传递给子进程(在exec 中)。在 Python 端,从sys.argv 中提取参数,将其转换回int,然后使用os.fdopen 为其获取file

【讨论】:

将 cout_pipe[1] 转换为字符串?所以这意味着 pipe(cout_pipe) 使 cout_pipe[1] 成为有效的 fd?。还有什么是转换 itoa(cout_pipe[1]) 的最佳方式? @Kam pipe( cout_pipe ) 将管道的两个 fd 放入 cout_pipe。您可能想要使用dup2 的唯一原因是您对 fd 的数值不满意。至于转换,任何标准解决方案都可以解决问题;如果您在子端执行此操作,就在exec 之前,并且您的系统有itoa(它不是标准的),那么即使这样也是可以接受的;如果您使用的是 C++11,则可以使用 std::to_string——只需确保在调用 .c_str() 之前将结果保存到具有足够生命周期的变量中即可。【参考方案2】:

在您描述的情况下,无需复制文件描述符。对 pipe() 的调用已经在 cout_pipe 数组中创建了您需要的文件描述符。

通常,子进程在执行之前简单地关闭管道的读取端,而父进程关闭管道的写入端。子进程将它想要的任何内容写入写入端,父进程读取或以其他方式监视管道的读取端。

如果您的问题是“子进程如何知道它执行后的 fd 编号,那么我会说当您在子进程中调用 execv() 时将其作为执行参数传递。”

类似:

int   child_pipe[2];
FILE *child_file;

void forkchild(void)

    int  ret;
    char child_pipe_fd_str[16];

    pipe(child_pipe);  /* TODO: check return value! */

    if (0 == (ret = fork())) /* child */
    
        close(child_pipe[0]);
        sprintf(child_pipe_fd_str, "%d", child_pipe[1]);
        execv(my_python_script, child_pipe_fd_str, NULL);
    
    else if (-1 != ret)  /* parent */
    
        close(cout_pipe[1]);
        child_file = fdopen(cout_pipe[0], "r");
    
    else
    
        perror("Fork failed!");
        abort();
    

【讨论】:

在上述情况下,当child写入stdout时,stdout之前被重定向到pipe,因此父进程只需从pipe读取即可读取child的stdout。我希望孩子不要写入标准输出,而是写入另一个文件,并且父进程可以读取它。 如果你想将管道的写入端包装在一个文件中,然后在你执行之后在你的子进程中调用 fdopen(write_side, "w")。诀窍是知道您执行的子 AFTER 中管道写入端的文件描述符#。一种方法是在调用 execv() 时将其作为执行参数显式传递。清楚吗? 好的,所以我应该在 fork 之后,exec 之前创建一个 FILE,然后 dup2(cout_pipe[1],this_files_fd),然后将 FILE 的 fd 作为 exec 的参数传递给子进程,并在 python 中我给这个 fd 写信? 不,您应该使用 os.fdopen 在您的新 Python 进程中创建您执行的文件结构 AFTER。诀窍是知道你需要打开什么fd#。您应该将其作为执行参数传递给子 C 进程中的 execv() 。我会在我的答案中发布一些代码。

以上是关于unix pipe() 和dup2()的使用方法和原理的主要内容,如果未能解决你的问题,请参考以下文章

UNIX环境高级编程APUE练习3.2-不用fcntl实现dup2的功能

Linux dup dup2函数理解

如何使用fgets / fread读取PIPE

python:multiprocessing.Pipe和重定向标准输出

使用 stdout/stderr 以外的管道与子进程通信

UNIX 文件描述符重用