Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/excel/27.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typo3/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C中的fork()、pipe()、dup2()和exec()有问题_C_Exec_Fork_Pipe_Dup2 - Fatal编程技术网

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