在C中实现流水线操作.最好的方法是什么?
作者:互联网
我想不出有任何方法可以在c中实现真正有效的流水线操作.这就是我决定写在这里的原因.我不得不说,我明白管道/前叉/ mkfifo是如何工作的.我见过很多实现2-3个管道的例子.这很容易.我的问题开始了,当我必须实现shell时,管道计数是未知的.
我现在得到了什么:
例如.
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现在,我相信一切都好.当我试图将这些函数输入/输出重定向到彼此时,问题开始了.
这就是我这样做的方式:
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);
}
}
谢谢.
解决方法:
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(...);
在每个分叉shell(close,dup2,pipe等)中运行的序列看起来像一个函数(获取所需进程的名称和参数).请注意,在每个exec调用之前,shell的分叉副本正在运行.
编辑:
Patryk:
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开始之前运行完成了吗?这不是上图所示的真正含义.在启动tr之前,你的shell不会等待ls完成.它按顺序启动管道中的所有进程,为每个进程设置stdin和stdout,以便进程链接在一起,stdout of ls到stdin of tr; stdout到下一个tr的stdin.这就是dup2调用正在做的事情.
进程运行的顺序由操作系统(调度程序)确定,但很明显,如果tr运行并从空stdin读取,则必须等待(阻塞),直到前面的进程将某些内容写入管道.很可能ls可能会在tr甚至从stdin读取之前完成运行,但同样可能它不会.例如,如果链中的第一个命令是连续运行并且沿途产生输出的东西,那么管道中的第二个命令将不时被安排到任何第一个沿管道发送的任务.
希望能澄清一点:-)
标签:c-3,pipeline,linux,pipe,mkfifo 来源: https://codeday.me/bug/20190929/1831798.html