在C.实现流水线什么是做到这一点的最好方法是什么?(Implementing pipelining

2019-07-03 17:24发布

我想不出什么办法来实现在C,将实际工作流水线。 这就是为什么我决定在这里写英寸 我不得不说,我知道该怎么办管/叉/ mkfifo工作。 我见过实现2-3管道的大量实例。 这很简单。 我的问题开始,当我已经有了实现的外壳,和管道数是未知。

我必须得:如。

ls -al | tr a-z A-Z | tr A-Z a-z | tr a-z A-Z

我的变换,例如行成这样的事情:

array[0] = {"ls", "-al", NULL"}
array[1] = {"tr", "a-z", "A-Z", NULL"}
array[2] = {"tr", "A-Z", "a-z", NULL"}
array[3] = {"tr", "a-z", "A-Z", NULL"}

所以,我可以使用

execvp(array[0],array)

稍后的。

现在Untli,我相信一切都OK。 问题开始,当我尝试这些功能的输入/输出重定向到海誓山盟。

以下是我正在做的是:

    mkfifo("queue", 0777);

    for (i = 0; i<= pipelines_count; i++) // eg. if there's 3 pipelines, there's 4 functions to execvp
    {
    int b = fork();             
    if (b == 0) // child
        {           
        int c = fork();

        if (c == 0) 
        // baby (younger than child) 
        // I use c process, to unblock desc_read and desc_writ for b process only
        // nothing executes in here
            {       
            if (i == 0) // 1st pipeline
                {
                int desc_read = open("queue", O_RDONLY);
                // dup2 here, so after closing there's still something that can read from 
                // from desc_read
                dup2(desc_read, 0); 
                close(desc_read);           
                }

            if (i == pipelines_count) // last pipeline
                {
                int desc_write = open("queue", O_WRONLY);
                dup2(desc_write, 0);
                close(desc_write);                              
                }

            if (i > 0 && i < pipelines_count) // pipeline somewhere inside
                {
                int desc_read = open("queue", O_RDONLY);
                int desc_write = open("queue", O_WRONLY);
                dup2(desc_write, 1);
                dup2(desc_read, 0);
                close(desc_write);
                close(desc_read);
                }               
            exit(0); // closing every connection between process c and pipeline             
            }
        else
        // b process here
        // in b process, i execvp commands
        {                       
        if (i == 0) // 1st pipeline (changing stdout only)
            {   
            int desc_write = open("queue", O_WRONLY);               
            dup2(desc_write, 1); // changing stdout -> pdesc[1]
            close(desc_write);                  
            }

        if (i == pipelines_count) // last pipeline (changing stdin only)
            {   
            int desc_read = open("queue", O_RDONLY);                                    
            dup2(desc_read, 0); // changing stdin -> pdesc[0]   
            close(desc_read);           
            }

        if (i > 0 && i < pipelines_count) // pipeline somewhere inside
            {               
            int desc_write = open("queue", O_WRONLY);       
            dup2(desc_write, 1); // changing stdout -> pdesc[1]
            int desc_read = open("queue", O_RDONLY);                            
            dup2(desc_read, 0); // changing stdin -> pdesc[0]
            close(desc_write);
            close(desc_read);                               
            }

        wait(NULL); // it wait's until, process c is death                      
        execvp(array[0],array);         
        }
        }
    else // parent (waits for 1 sub command to be finished)
        {       
        wait(NULL);
        }       
    }

谢谢。

Answer 1:

Patryk,你为什么要使用一个FIFO,而且同一个fifo为管道的每个阶段?

在我看来,你需要在每个阶段之间的管道。 因此,流动会是这样的:

Shell             ls               tr                tr
-----             ----             ----              ----
pipe(fds);
fork();  
close(fds[0]);    close(fds[1]);
                  dup2(fds[0],0); 
                  pipe(fds);
                  fork();         
                  close(fds[0]);   close(fds[1]);  
                  dup2(fds[1],1);  dup2(fds[0],0);
                  exex(...);       pipe(fds);
                                   fork();     
                                   close(fds[0]);     etc
                                   dup2(fds[1],1);
                                   exex(...);  

在每个叉状壳(接近,DUP2,管等)上运行的序列似乎是一个函数(以名称和期望的处理的参数)。 需要注意的是,直到exec在每次调用,壳的分叉副本运行。

编辑:

帕特里克:

Also, is my thinking correct? Shall it work like that? (pseudocode): 
start_fork(ls) -> end_fork(ls) -> start_fork(tr) -> end_fork(tr) -> 
start_fork(tr) -> end_fork(tr) 

我不知道你所说的start_fork和end_fork的意思。 你的意思是ls运行完成之前tr开始? 这是不是真的什么是上图中的意思。 你的shell不会等待ls在开始之前完成tr 。 它开始的所有过程的按顺序配管,设置stdinstdout为每一个,以使工艺连接在一起, stdoutlsstdintr ; stdouttrstdin接下来的tr 。 这正是DUP2调用正在做的。

其中的处理运行的顺序是由操作系统(调度器)来确定,但很明显,如果tr运行并从空读取stdin它必须等待(到块),直到前面的过程中写入的东西到管道。 这是很可能的是ls可能运行完毕之前tr甚至从它的读取stdin ,但它同样可能的是它不会。 例如,如果在链中的第一个命令是什么,不断跑和生产一路输出,在管道中的第二个将获得计划,不时来prcess无论第一沿管道发送。

希望澄清事情有点:-)



Answer 2:

它可能使用是值得libpipeline 。 这需要对您的部分负责所有的努力,你甚至可以在您的管道功能。



Answer 3:

问题是你想一次做的一切。 分解成更小的步骤来代替。

1)将给您的投入,得到ls -al | 出来。 1A)从这个你知道你需要创建一个管道,将其移动到stdout,并开始ls -al 。 然后将管转移到标准输入。 还有,当然更多的到来,但你不代码担心呢。

2)解析下一个段获得tr az AZ | 。 回去为你的下一个到产卵命令的输出被地方输送到只要步骤1A。



Answer 4:

在C.实现流水线什么是做到这一点的最好方法是什么?

这个问题是有点老了,但在这里是一个从未提供了一个答案。 使用libpipeline 。 libpipeline是流水线操作库。 用例是一个man页面维护者谁不得不经常使用如下命令(和解决相关的OS错误):

zsoelim < input-file | tbl | nroff -mandoc -Tutf8

这里的libpipeline方式:

pipeline *p;
int status;

p = pipeline_new ();
pipeline_want_infile (p, "input-file");
pipeline_command_args (p, "zsoelim", NULL);
pipeline_command_args (p, "tbl", NULL);
pipeline_command_args (p, "nroff", "-mandoc", "-Tutf8", NULL);
status = pipeline_run (p);

该libpipeline主页有更多的例子。 该库也包含在许多发行版,包括凯旋门,Debian的,Fedora的, Linux的划痕和Ubuntu。



文章来源: Implementing pipelining in C. What would be the best way to do that?