gpt4 book ai didi

c - 在 C 中使用 fork()、pipe()、dup2() 和 exec() 时遇到问题

转载 作者:太空狗 更新时间:2023-10-29 16:50:45 25 4
gpt4 key购买 nike

这是我的代码:

#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;
}

(代码已更新以反射(reflect)下面两个答案提出的更改,它仍然无法正常工作...)

这是失败的测试用例:

nazgulled ~/Projects/SO/G08 $ ls -l
total 8
-rwxr-xr-x 1 nazgulled nazgulled 7181 2009-05-27 17:44 a.out
-rwxr-xr-x 1 nazgulled nazgulled 754 2009-05-27 01:42 data.h
-rwxr-xr-x 1 nazgulled nazgulled 1305 2009-05-27 17:50 main.c
-rwxr-xr-x 1 nazgulled nazgulled 320 2009-05-27 01:42 makefile
-rwxr-xr-x 1 nazgulled nazgulled 14408 2009-05-27 17:21 prog
-rwxr-xr-x 1 nazgulled nazgulled 9276 2009-05-27 17:21 prog.c
-rwxr-xr-x 1 nazgulled nazgulled 10496 2009-05-27 17:21 prog.o
-rwxr-xr-x 1 nazgulled nazgulled 16 2009-05-27 17:19 test
nazgulled ~/Projects/SO/G08 $ ./a.out
Shell> ls -l|grep prog
[4804] TERMINATED (Status: 0)
-rwxr-xr-x 1 nazgulled nazgulled 14408 2009-05-27 17:21 prog
-rwxr-xr-x 1 nazgulled nazgulled 9276 2009-05-27 17:21 prog.c
-rwxr-xr-x 1 nazgulled nazgulled 10496 2009-05-27 17:21 prog.o

问题是在那之后我应该返回我的 shell,我应该看到“Shell>”等待更多输入。您还可以注意到您没有看到类似于“[4804] TERMINATED (Status: 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)

您可以很容易地看到两个“终止”消息...

那么,我的代码有什么问题?

最佳答案

即使在您的管道的第一个命令退出后(并因此关闭 stdout=~fdPipe[1]),父级仍然打开 fdPipe[1]

因此,管道的第二个命令有一个永远不会得到 EOF 的 stdin=~fdPipe[0],因为管道的另一个端点仍然打开。

您需要为每个|创建一个新的pipe(fdPipe),并确保关闭父节点中的两个端点;即

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])

此外,为了更安全,您应该在执行任何关闭之前处理fdPipe{STDIN_FILENO,STDOUT_FILENO}重叠的情况和 dup2 操作。如果有人设法在关闭 stdin 或 stdout 的情况下启动您的 shell,就会发生这种情况,并且会导致此处代码非常困惑。

编辑

   fdPipe1           fdPipe3
v v
cmd1 | cmd2 | cmd3 | cmd4 | cmd5
^ ^
fdPipe2 fdPipe4

除了确保关闭父级管道的端点外,我还试图指出 fdPipe1fdPipe2 等。不能pipe() 相同。

/* 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);

我知道您在当前代码中没有使用 close(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]);         }

关于c - 在 C 中使用 fork()、pipe()、dup2() 和 exec() 时遇到问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/916900/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com