in Blog Posts, C/C++, Solution, 文章

dup, pipe 和 fork

(好久没更新了,呼…一大波死线刚刚结束…)

我几乎一直在用Bash,可是却少有接触到Unix系的系统编程,对系统调用还是知之甚少。这两天实验室里讨论了一个比较基础的问题: 在自己写的程序中,怎么样得到另一个可执行文件的输出?

比如我们有/bin/pwd这个可执行文件,我们可以在自己的程序中用

system("/bin/pwd");

或者是

execl("/bin/pwd",".",NULL);

调用它。

如果想要得到它的输出,应该怎么做比较好?

这个其实是作业题的范畴,涉及到了几个Unix的系统调用:

     #include 

     pid_t
     fork(void);
     #include 

     int
     pipe(int fildes[2]);
     #include 

     int
     dup(int fildes);

     int
     dup2(int fildes, int fildes2);

手册里有各个函数的详细解释。
在这个例子里,pipe用来生成一对文件描述子 (file descriptor,我觉得描述子这个翻译不是很好…),往第二个描述子里写内容,从第一个里面读内容。fork用来得到一个子进程,这里,我们会让子进程来执行pwd,并且把输出写到pipe的一端,然后父进程可以从另一端读入。

但是pwd默认输出到屏幕,也就是标准输出,我们需要使用dup2,把标准输出指向pipe的一端,这样就可以完成任务了。

所以,最终的代码是这样的

#include 
#include 
#include 
#include 

int main(int argc, char **argv)
{
    int fd[2];
    int pid;
    pipe(fd);
    int rpipe = fd[0];
    int wpipe = fd[1];
    pid = fork();
    if (pid == 0)
    {   
        /* 子进程关掉读的那端,只用写的一端 */
        close(rpipe); 

        /* 把标准输入指向pipe的写的一端 */
        dup2(wpipe, STDOUT_FILENO);

        /* 执行pwd */
        execl("/bin/pwd",".",NULL);

        /* 嗯 */
        exit(0);
    }   
    else
    {   
        /* 父进程关掉写的那端,只用读的一端 */
        close(wpipe);

        printf("begin parent process\n");
        char readbuffer[1024];

        /* 从读的这端读出pwd的输出 */
        int nbytes = read(rpipe, readbuffer, sizeof(readbuffer));

        printf("Received string: %s | %d\n", readbuffer, nbytes);
        printf("end parent process\n");

        wait(&pid);
    }   
    return 0;
}

所以最后的输出就是像这样的

begin parent process
Received string: /Users/XXX/XXXX
 | 16
end parent process

话说fork除了考试之外,似乎就没有用过了,代码还是写得太少了…

(Image from Github)

Write a Comment

Comment

  1. 在Linux,如Ubuntu,需要增加两行:

    #include

    不然编译器找不到wait()的头文件

    在printf行之前增加:
    readbuffer[nbytes-1]=’x0′;
    因为pwd的输出不是以0结尾的字符串,需要手工增加结尾的0字节。