C中的fork()、pipe()、dup2()和exec()有问题
这是我的密码:C中的fork()、pipe()、dup2()和exec()有问题,c,exec,fork,pipe,dup2,C,Exec,Fork,Pipe,Dup2,这是我的密码: #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <wait.h> #include <readline/readline.h> #define NUMPIPES 2 int main(int argc, char *argv[]) { char *bBuffer, *sPtr, *aPtr = NULL, *pipeCom
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
#include <readline/readline.h>
#define NUMPIPES 2
int main(int argc, char *argv[]) {
char *bBuffer, *sPtr, *aPtr = NULL, *pipeComms[NUMPIPES], *cmdArgs[10];
int fdPipe[2], pCount, aCount, i, status, lPids[NUMPIPES];
pid_t pid;
pipe(fdPipe);
while(1) {
bBuffer = readline("Shell> ");
if(!strcasecmp(bBuffer, "exit")) {
return 0;
}
sPtr = bBuffer;
pCount = -1;
do {
aPtr = strsep(&sPtr, "|");
pipeComms[++pCount] = aPtr;
} while(aPtr);
for(i = 0; i < pCount; i++) {
aCount = -1;
do {
aPtr = strsep(&pipeComms[i], " ");
cmdArgs[++aCount] = aPtr;
} while(aPtr);
cmdArgs[aCount] = 0;
if(strlen(cmdArgs[0]) > 0) {
pid = fork();
if(pid == 0) {
if(i == 0) {
close(fdPipe[0]);
dup2(fdPipe[1], STDOUT_FILENO);
close(fdPipe[1]);
} else if(i == 1) {
close(fdPipe[1]);
dup2(fdPipe[0], STDIN_FILENO);
close(fdPipe[0]);
}
execvp(cmdArgs[0], cmdArgs);
exit(1);
} else {
lPids[i] = pid;
/*waitpid(pid, &status, 0);
if(WIFEXITED(status)) {
printf("[%d] TERMINATED (Status: %d)\n",
pid, WEXITSTATUS(status));
}*/
}
}
}
for(i = 0; i < pCount; i++) {
waitpid(lPids[i], &status, 0);
if(WIFEXITED(status)) {
printf("[%d] TERMINATED (Status: %d)\n",
lPids[i], WEXITSTATUS(status));
}
}
}
return 0;
}
问题是,我应该在返回shell之后,看到“shell>”正在等待更多输入。您还可以注意到,您没有看到类似于“[4804]已终止(状态:0)”(但具有不同的pid)的消息,这意味着第二个进程没有终止
我认为这与grep有关,因为这是有效的:
nazgulled ~/Projects/SO/G08 $ ./a.out
Shell> echo q|sudo fdisk /dev/sda
[4838] TERMINATED (Status: 0)
The number of cylinders for this disk is set to 1305.
There is nothing wrong with that, but this is larger than 1024,
and could in certain setups cause problems with:
1) software that runs at boot time (e.g., old versions of LILO)
2) booting and partitioning software from other OSs
(e.g., DOS FDISK, OS/2 FDISK)
Command (m for help):
[4839] TERMINATED (Status: 0)
您可以很容易地看到两条“终止”消息
那么,我的代码怎么了?我认为您的分叉进程将继续执行 尝试以下任一方法:
- 将其更改为“return execvp”
- 添加“出口(1);”执行后
- 一个潜在的问题是cmdargs的末尾可能有垃圾。在将数组传递给execvp()之前,应该使用空指针终止该数组
不过,看起来grep正在接受STDIN,因此这可能不会导致任何问题。在execvp()之后应该会有一个错误退出-它有时会失败
exit(EXIT_FAILURE);
正如@uncleo指出的,参数列表必须有一个空指针来指示结束:
cmdArgs[aCount] = 0;
我不清楚您是否让两个程序都免费运行—您似乎要求管道中的第一个程序在启动第二个程序之前完成,如果第一个程序因管道已满而阻塞,这不是成功的秘诀。乔纳森的想法是正确的。您依赖第一个进程来分叉所有其他进程。每一个都必须运行到完成,然后才能分叉下一个 相反,像您正在做的那样在循环中分叉进程,但是在内部循环之外等待它们(在大循环的底部等待shell提示)
即使在管道的第一个命令退出(并且thust关闭
stdout=~fdPipe[1]
)之后,父管道仍然打开fdPipe[1]
因此,管道的第二个命令有一个stdin=~fdPipe[0]
,它永远不会获得EOF,因为管道的另一个端点仍然打开
您需要为每个|
创建一个新的管道(fdPipe)
,并确保关闭父节点中的两个端点;i、 e
for cmd in cmds
if there is a next cmd
pipe(new_fds)
fork
if child
if there is a previous cmd
dup2(old_fds[0], 0)
close(old_fds[0])
close(old_fds[1])
if there is a next cmd
close(new_fds[0])
dup2(new_fds[1], 1)
close(new_fds[1])
exec cmd || die
else
if there is a previous cmd
close(old_fds[0])
close(old_fds[1])
if there is a next cmd
old_fds = new_fds
if there are multiple cmds
close(old_fds[0])
close(old_fds[1])
此外,为了更安全,在执行任何close
和dup2
操作之前,应处理fdPipe
和{STDIN\u FILENO,STDOUT\u FILENO}
重叠的情况。如果有人试图在stdin或stdout关闭的情况下启动您的shell,则可能会发生这种情况,这将导致此处的代码非常混乱
编辑
除了确保在父级中关闭管道的端点外,我还试图指出fdPipe1
、fdPipe2
等不能是相同的pipe()
这对管道中的多个命令不起作用;为此,您需要这样的东西:(未经测试,因为您还必须修复其他东西)
@@ -9,9 +9,7 @@
int main(int argc,char*argv[]){
char*bBuffer、*sPtr、*aPtr=NULL、*pipeComms[NUMPIPES]、*cmdArgs[10];
-int fdPipe[2],pCount,aCount,i,status,lpid[NUMPIPES];
+int fdPipe[2],fdPipe2[2],pCount,aCount,i,status,lpid[NUMPIPES];
pid_t pid;
-管道(fdPipe);
-
而(1){
bBuffer=readline(“Shell>”);
@@ -32,4 +30,7 @@
a数量=-1;
+如果(i+1管道中的文件描述符是引用计数的,并且随着每个fork而递增。对于每个fork,您必须对两个描述符发出一个close,以便将引用计数减少到零并允许管道关闭。我猜。Hmm可能不会。但无论如何尝试一下=)可能使其“return execvp”我认为这不起作用。到目前为止据我所知,execvp()用正在执行的命令替换当前进程(子进程),因此,如果execvp()调用成功,子进程将永远不会到达低于该值的任何代码行。只是尝试了一下,正如我所怀疑的那样,不起作用:1)“exec()函数族用新的进程映像替换当前进程映像。”“如果有任何exec()函数返回,就会发生错误。”引用手册页中的内容。是的,这正是我所想的……但我搜索了execvp示例,之后它们似乎都有一个“exit(1);”。exec*()
如果成功则从不返回,如果失败则返回。因此,exit(1)
遵循exec*()
是为了处理失败(无论出于何种原因…找不到文件,没有权限等)。看起来grep没有到达末尾。它无法打印出第三个匹配行(“prog.o”)它确实打印了第三行匹配的内容,我只是没有正确地复制整个内容。我已经编辑了这篇文章来解决这个问题。我写了一个很小的(999LOC)shell示例
loop //for prompt
next prompt
loop //to fork tasks, store the pids
if pid == 0 run command
else store the pid
end loop
loop // on pids
wait
end loop
end loop
for cmd in cmds
if there is a next cmd
pipe(new_fds)
fork
if child
if there is a previous cmd
dup2(old_fds[0], 0)
close(old_fds[0])
close(old_fds[1])
if there is a next cmd
close(new_fds[0])
dup2(new_fds[1], 1)
close(new_fds[1])
exec cmd || die
else
if there is a previous cmd
close(old_fds[0])
close(old_fds[1])
if there is a next cmd
old_fds = new_fds
if there are multiple cmds
close(old_fds[0])
close(old_fds[1])
fdPipe1 fdPipe3
v v
cmd1 | cmd2 | cmd3 | cmd4 | cmd5
^ ^
fdPipe2 fdPipe4
/* suppose stdin and stdout have been closed...
* for example, if your program was started with "./a.out <&- >&-" */
close(0), close(1);
/* then the result you get back from pipe() is {0, 1} or {1, 0}, since
* fd numbers are always allocated from the lowest available */
pipe(fdPipe);
close(0);
dup2(fdPipe[0], 0);
@@ -12,6 +12,4 @@
pid_t pid;
- pipe(fdPipe);
-
while(1) {
bBuffer = readline("Shell> ");
@@ -29,4 +27,6 @@
} while(aPtr);
+ pipe(fdPipe);
+
for(i = 0; i < pCount; i++) {
aCount = -1;
@@ -72,4 +72,7 @@
}
+ close(fdPipe[0]);
+ close(fdPipe[1]);
+
for(i = 0; i < pCount; i++) {
waitpid(lPids[i], &status, 0);
@@ -9,9 +9,7 @@
int main(int argc, char *argv[]) {
char *bBuffer, *sPtr, *aPtr = NULL, *pipeComms[NUMPIPES], *cmdArgs[10];
- int fdPipe[2], pCount, aCount, i, status, lPids[NUMPIPES];
+ int fdPipe[2], fdPipe2[2], pCount, aCount, i, status, lPids[NUMPIPES];
pid_t pid;
- pipe(fdPipe);
-
while(1) {
bBuffer = readline("Shell> ");
@@ -32,4 +30,7 @@
aCount = -1;
+ if (i + 1 < pCount)
+ pipe(fdPipe2);
+
do {
aPtr = strsep(&pipeComms[i], " ");
@@ -43,11 +44,12 @@
if(pid == 0) {
- if(i == 0) {
- close(fdPipe[0]);
+ if(i + 1 < pCount) {
+ close(fdPipe2[0]);
- dup2(fdPipe[1], STDOUT_FILENO);
+ dup2(fdPipe2[1], STDOUT_FILENO);
- close(fdPipe[1]);
- } else if(i == 1) {
+ close(fdPipe2[1]);
+ }
+ if(i != 0) {
close(fdPipe[1]);
@@ -70,4 +72,17 @@
}
}
+
+ if (i != 0) {
+ close(fdPipe[0]);
+ close(fdPipe[1]);
+ }
+
+ fdPipe[0] = fdPipe2[0];
+ fdPipe[1] = fdPipe2[1];
+ }
+
+ if (pCount) {
+ close(fdPipe[0]);
+ close(fdPipe[1]);
}