如何解决具有不同输出的管道的分叉进程
我有一些代码练习分叉进程(在命令行中模拟 (|))。但是,每次的输出都不相同。例如,对于 ./pipe ls cat wc
的输入,它应该与 ls | cat | wc
相同。但是,有时我的代码会输出
Child with PID 12126 exited with status 0x0.
Child with PID 12127 exited with status 0x0.
7 7 52
Child with PID 12128 exited with status 0x0.
但有时它也会输出:
Makefile
pipe
#pipe.c#
pipe.c
pipe.o
README.md
test
Child with PID 12138 exited with status 0x0.
Child with PID 12139 exited with status 0x0.
0 0 0
Child with PID 12140 exited with status 0x0.
第一个输出是正确的(与ls | cat | wc
相比)。我认为通过第二个输出,程序 ls
和 cat
的管道输出没有被 wc
处理。我想知道我的程序出了什么问题,因为似乎我正确设置了管道 - 第一个程序将从 stdin 获取输入并输出到管道的写入端,最后一个程序将从读取端获取输入管道和输出到标准输出。任何输入表示赞赏。
代码(./pipe
):
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
int fd[2];
if (pipe(fd) == -1) {
int err = errno;
perror("pipe");
return err;
}
pid_t pid[argc-1];
int n = argc;
if(argc <= 1){
return EINVAL;
}
for (int i = 1; i < argc; i++){
if ((pid[i] = fork()) == -1){
int err = errno;
perror("fork");
return err;
}else if(pid[i] == 0){
//open(fd[1]);
if(i == 1){
dup2(fd[1],STDOUT_FILENO);
if (execlp(argv[i],argv[i],NULL) == -1) {
printf("Failed to search for the provided executed program. \n");
return errno;
}
}else if(i == argc-1){
dup2(fd[0],STDIN_FILENO);
if (execlp(argv[i],NULL) == -1) {
printf("Failed to search for the provided executed program. \n");
return errno;
}
}else{
dup2(fd[0],STDIN_FILENO);
dup2(fd[1],STDOUT_FILENO);
if (execlp(argv[i],NULL) == -1) {
printf("Failed to search for the provided executed program. \n");
return errno;
}
}
}
close(fd[1]);
}
int wstatus;
pid_t pids;
while (n > 1) {
pids = wait(&wstatus);
printf("Child with PID %ld exited with status 0x%x.\n",(long)pids,wstatus);
--n;
}
}
解决方法
您正在越界访问可变长度数组 pid
,因此您的程序具有未定义行为。
您可以通过将数组放大一个元素来解决这个问题:
pid_t pid[argc]; /* not argc - 1 */
另一个问题是每对命令之间需要一个新管道。总共,一个管道比命令的数量少。也就是说,一个命令的管道数为零。
stdin - ls - pipe - cat - pipe - wc - stdout // 3 commands,2 pipes
这里有一个想法,我在(几乎)循环的每次迭代中创建一个管道,并为下一次迭代保存管道的输入端。我根本不费心保存进程 id:s 并且我没有为 dup2
添加错误检查。但是,您应该检查它是否真的成功。
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int exec(const char* prog) {
if(execlp(prog,prog,NULL) == -1) {
perror("execlp");
}
return 1;
}
int main(int argc,char* argv[]) {
pid_t pid;
int in; // for saving the input side of the pipe between iterations
for(int i = 1; i < argc; ++i) {
int fd[2];
// must have at least 2 commands to create a pipe
// and we need one pipe less than the number of commands
if(argc > 1 && i < argc - 1) pipe(fd);
if((pid = fork()) == -1) {
perror("fork");
return 1;
} else if(pid == 0) {
if(i == 1) { // first command
if(argc > 1) { // must have more than one command to dup
dup2(fd[1],STDOUT_FILENO);
close(fd[1]);
close(fd[0]);
}
} else if(i == argc - 1) { // last command
dup2(in,STDIN_FILENO);
close(in);
} else { // middle commands
dup2(in,STDIN_FILENO);
close(in);
dup2(fd[1],STDOUT_FILENO);
close(fd[1]);
close(fd[0]);
}
return exec(argv[i]);
}
// parent
if(i > 1) close(in); // close old in
in = fd[0]; // save the stdin side of the pipe for next loop iteration
close(fd[1]); // no use for the stdout end
}
int wstatus;
while((pid = wait(&wstatus)) != -1) {
printf("Child with PID %ld exited with status 0x%x.\n",(long)pid,wstatus);
}
}
,
您需要更多管道,因为您当前的代码中只有一个。如果 ls
和 cat
都在写入该管道,并且 cat
和 wc
都从该管道读取,则您无法同步 cat
读取哪些数据wc
读取的数据。也就是说,wc
可能直接从 ls
或 cat
读取数据。而 cat
可能会从自身读取数据。 (这似乎是有时会发生的事情)。进程从管道读取数据的顺序取决于它们被安排运行的顺序。
你需要更多的管道。 (比进程总数少一个。)
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。