在C中遇到fork(),pipe(),dup2()和exec()时遇到麻烦

前端之家收集整理的这篇文章主要介绍了在C中遇到fork(),pipe(),dup2()和exec()时遇到麻烦前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
这是我的代码
#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],0);

            if(WIFEXITED(status)) {
                printf("[%d] TERMINATED (Status: %d)\n",lPids[i],WEXITSTATUS(status));
            }
        }
    }

    return 0;
}

(代码更新,以反映他提出的两个答案下面的变化,它仍然不工作,因为它应该…)

以下是测试用例:

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)

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

那么我的代码怎么了?

解决方法

即使您的管道的第一个命令退出(并且thust关闭stdout =〜fdPipe [1]),父进程仍然有fdPipe [1]打开.

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

您需要为每个|创建一个新的管道(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])

另外,为了更安全,在执行任何关闭和dup2操作之前,应该处理fdPipe和{STDIN_FILENO,STDOUT_FILENO}重叠的情况.如果有人设法用stdin或stdout关闭来启动shell,这可能会发生,并且会导致这里的代码很混乱.

编辑

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

除了确保关闭管道在父级中的端点之外,我试图说明fdPipe1,fdPipe2等不能是同一个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],0);

这对于管道中的多个命令不起作用;为此,你需要这样的东西:(未经测试,因为你必须修复其他的东西)

@@ -9,9 +9,7 @@
 int main(int argc,char *argv[]) {
     char *bBuffer,*cmdArgs[10];
-    int fdPipe[2],lPids[NUMPIPES];
+    int fdPipe[2],fdPipe2[2],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,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++相关文章