我要实现一个无名管道,并且必须在父进程中而不是在他的任何子进程中执行命令。每个“-”都等于对管道的调用(“ |”),这也是我代码的一部分。有人可以向我解释为什么它不起作用吗?
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h> // for open flags
#include <time.h> // for time measurement
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
void my_exec(char* cmd, char** argv)
{
int pipefd[2], f;
if (pipe(pipefd) < 0)
perror("pipe creation failed");
f = fork();
assert(f >= 0);
if (f == 0) {
// inside son process - connecting STDOUT to pipe write
if (dup2(pipefd[1], STDOUT_FILENO) < 0)
perror("dup2 failed");
close(pipefd[0]);
close((int)stdout);
} else {
// inside parent process - connecting STDIN to pipe read and execute command with args
if (dup2(pipefd[0], STDIN_FILENO) < 0)
perror("dup2 failed");
close(pipefd[1]);
close((int)stdin);
if (execvp(cmd, argv) < 0)
perror("execvp failed");
}
}
int main(int argc, char** argv)
{
assert(strcmp(argv[argc-1], "-"));
int i;
for (i = 1; i < argc; ++i) {
if (!strcmp(argv[i], "-")) {
argv[i] = NULL;
my_exec(argv[1], &argv[1]);
argv = &argv[i];
argc -= i;
i = 0;
}
}
char* args[argc];
args[argc-1] = NULL;
for (i = 1; i < argc; ++i) {
args[i-1] = argv[i];
}
if (execvp(args[0], args) == -1)
perror("execvp failed");
return;
}
命令 :
./mypipe.o ls -l - grep "pipe"
退货
total 24
-rw-rw-r-- 1 omer omer 1463 May 23 19:38 mypipe.c
-rwxrwxr-x 1 omer omer 7563 May 23 19:37 mypipe.o
-rw-rw-rw- 1 omer omer 873 May 23 20:01 nice.c
-rwxrwxr-x 1 omer omer 7417 May 23 19:44 nice.o
-rw-rw-r-- 1 omer omer 0 May 23 17:10 try
这显然意味着管道没有工作...有什么想法吗?
我需要确保对np_exec的每次调用都会启动一个子进程,该子进程继续解析其余参数,而原始父进程执行给定的程序和参数(使用execvp),
编辑:我认为我发现了错误:我切换了管道的“读写”末端。
正确的功能:
void np_exec(char* cmd, char** argv)
{
int pipefd[2];
int file;
if (pipe(pipefd) < 0)
perror("failed to create pipe");
file = fork();
assert(file >= 0);
if (file != 0) {
// inside parent process - connecting STDOUT to pipe write and execute command with args
if (dup2(pipefd[WRITE], STDOUT_FILENO) < 0)
perror("the function dup2 failed");
close(pipefd[READ]);
close((int)stdout);
if (execvp(cmd, argv) < 0)
perror("the function execvp failed");
} else {
// inside son process - connecting STDIN to pipe read
if (dup2(pipefd[READ], STDIN_FILENO) < 0)
perror("the function dup2 failed");
close(pipefd[WRITE]);
close((int)stdin);
}
}
更新:所以,原来我的答案是偏离基础的。直到现在我还不太了解你想做什么。
您的代码大部分是正确的。它不能工作的原因很简单:shell假定启动它的进程完成后命令已经完成。
请允许我举例说明。想象你奔跑./mypipe a - b - c
。
这是幕后故事:
Shell分叉一个要执行的过程mypipe
。这是您的程序开始执行的时间。
您的程序派生,而父级调用exec(a)
。子级将继续解析其他自变量并按预期方式处理管道创建。
因此,现在,到此为止,您有了运行中的父程序a
-在shell的眼中,它是与command相对应的进程./mypipe
,而您有一个子进程,shell完全忽略了该子进程,完成了mypipe
-设置管道,读取其余要执行的程序等。
所以,现在您有了比赛条件。由于后面的进程mypipe
已被程序替换a
,因此一旦a
终止,shell便假定您键入的命令已完成(即,它已假定mypipe
已完成),并打印提示符,并期望您键入下一个命令。
问题是:a
快速终止,您的子进程仍在其他程序列表中进行遍历并设置管道,重定向输入和输出等。因此,例如,子进程可能仍在创建管道和解析其他程序上工作,而到那时为止,该a
进程已经完成并将所有内容写入stdout-糟糕!
您如何解决这个问题?简单:反转命令执行顺序。首先执行最后一个命令,然后执行倒数第二个命令,等等。为什么?因为如果这样做,则父级将是管道上的最后一个管道,因此它将阻止等待输入到达管道读取通道。当父项终止时,这意味着管道中的最后一条命令已终止,这正是我们想要的。没有竞争条件,并且外壳程序也不会欺骗我们以为我们的命令实际上并未完成。
因此,只需要从右到左而不是从左到右解析程序列表即可。另外,如果您这样做了,就不再需要辅助args
阵列,那就太好了!
这是经过测试并可以正常工作的代码:
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h> // for open flags
#include <time.h> // for time measurement
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
void my_exec(char* cmd, char** argv)
{
int pipefd[2], f;
if (pipe(pipefd) < 0)
perror("pipe creation failed");
f = fork();
assert(f >= 0);
if (f == 0) {
if (dup2(pipefd[1], STDOUT_FILENO) < 0)
perror("dup2 failed");
close(pipefd[0]);
} else {
if (dup2(pipefd[0], STDIN_FILENO) < 0)
perror("dup2 failed");
close(pipefd[1]);
if (execvp(cmd, argv) < 0)
perror("execvp failed");
}
}
int main(int argc, char** argv)
{
assert(strcmp(argv[argc-1], "-"));
int i;
for (i = argc-1; i >= 1; i--) {
if (!strcmp(argv[i], "-")) {
argv[i] = NULL;
my_exec(argv[i+1], &argv[i+1]);
argc = i;
}
}
if (execvp(argv[0], &argv[1]) == -1)
perror("execvp failed");
return 0;
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句