下面的简短程序旨在遍历从命令行传递的argv并执行每个参数。这不是我的作业,而是我正在做的准备作业。

第一个参数从STDIN和STDOUT获取输入,并将其写入管道。在每次迭代结束时(最后一次除外),文件描述符都将交换,以便最后一个exec写入的管道将在下一个执行时读取。这样,我打算例如

./a.out /bin/pwd /usr/bin/wc

仅打印出工作目录的长度。代码如下
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>

main(int argc, char * argv[]) {

  int i;
  int left[2], right[2], nbytes; /* arrays for file descriptors */

  /* pointers for swapping */
  int (* temp);
  int (* leftPipe) = left;
  int (* rightPipe) = right;

  pid_t childpid;
  char readbuffer[80];

  /* for the first iteration, leftPipe is STDIN */
  leftPipe[0] = STDIN_FILENO;
  leftPipe[1] = STDOUT_FILENO;

  for (i = 1; i < argc; i++) {

    /* reopen the right pipe (is this necessary?) */
    pipe(rightPipe);
    fprintf(stderr, "%d: %s\n", i, argv[i]);
    fprintf(stderr, "%d %d %d %d\n", leftPipe[0], leftPipe[1], rightPipe[0], rightPipe[1]);
    if ((childpid = fork()) == -1) {
      perror("fork");
      exit(1);
    }

    if (childpid == 0) {

      /* read input from the left */
      close(leftPipe[1]); /* close output */
      dup2(leftPipe[0], STDIN_FILENO);
      close(leftPipe[0]); /* is this necessary? A tutorial seemed to be doing this */

      /* write output to the right */
      close(rightPipe[0]); /* close input */
      dup2(rightPipe[1], STDOUT_FILENO);
      close(rightPipe[1]);

      execl(argv[i], argv[i], NULL);
      exit(0);
    }

    wait();

    /* on all but the last iteration, swap the pipes */
    if (i + 1 < argc) {

      /* swap the pipes */
      fprintf(stderr, "%d %d %d %d\n", leftPipe[0], leftPipe[1], rightPipe[0], rightPipe[1]);
      temp = leftPipe;
      leftPipe = rightPipe;
      rightPipe = temp;
      fprintf(stderr, "%d %d %d %d\n", leftPipe[0], leftPipe[1], rightPipe[0], rightPipe[1]);
    }
  }

    /* read what was last written to the right pipe */
    close(rightPipe[1]); /* the receiving process closes 1 */

    nbytes = read(rightPipe[0], readbuffer, sizeof(readbuffer));
    readbuffer[nbytes] = 0;
    fprintf(stderr, "Received string: %s\n", readbuffer);

  return 0;
}

更新:在以下所有测试用例中,我最初使用/bin/wc,但该wc揭示了抽水马桶根本不是我想的那样。我正在修改结果。

普通情况下(./a.out/bin/pwd)的输出是预期的:
1: /bin/pwd
Received string: /home/zeigfreid/Works/programmatical/Langara/spring_2012/OS/labs/lab02/play

使用第一个示例(./a.out/bin/pwd/usr/bin/wc)运行该程序的输出:
1: /bin/pwd
0 1 3 4
3 4 0 1
2: /bin/wc

此时,终端挂起(可能正在等待输入)。

如您所见,该字符串没有被接收。我想象的是,在交换指针时,或者在我不理解UNIX文件描述符的情况下,我在上面做错了什么。最后,我的任务是解释任意长的管道,这是我解决该问题的想法之一。我在判断我是否在树皮吠叫的正确轨道上遇到了麻烦。我了解UNIX文件描述符吗?

更新:

使用/bin/ls作为第二个参数运行它,我得到以下结果(数字是各个点的文件描述符):
1: /bin/pwd
0 1 3 4
0 1 3 4
3 4 0 1
2: /bin/ls
3 4 5 6
Received string: a.out
log
pipe2.c
play.c
@

最后还有一些垃圾,但是我现在更担心我不懂指针!尽管这两个命令彼此独立,但它们实际上并没有利用管道。

UPDATE :垃圾字符来自未关闭字符串。现在我将其关闭,没有任何垃圾。

最佳答案

挂起是由于以下事实造成的:在 fork 后的主要过程中,“右”管的书写端未正确关闭。因此,wc将永远不会停止读取(毕竟,主进程仍可以将内容写入管道!)。只有在写入端的文件描述符的所有副本都已关闭之后,它才会停止读取。

这是固定版本:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char * argv[])
{
  int i;
  int left[2], right[2], nbytes; /* arrays for file descriptors */

  /* pointers for swapping */
  int (* temp);
  int (* leftPipe) = left;
  int (* rightPipe) = right;

  pid_t childpid;
  char readbuffer[80];

  leftPipe[0] = STDIN_FILENO;
  // no need to assign leftPipe[1] here, it will not be used

  for (i = 1; i < argc; i++) {
    pipe(rightPipe); // create new pipe

    fprintf(stderr, "%d: %s\n", i, argv[i]);
    fprintf(stderr, "%d %d %d %d\n", leftPipe[0], leftPipe[1], rightPipe[0], rightPipe[1]);
    if ((childpid = fork()) == -1) {
      perror("fork");
      exit(1);
    }

    if (childpid == 0) {
      // use the reading end of the left pipe as STDIN
      dup2(leftPipe[0], STDIN_FILENO);
      // use the writing end of the right pipe as STDOUT
      dup2(rightPipe[1], STDOUT_FILENO);
      // close reading end of the right pipe
      close(rightPipe[0]);
      execl(argv[i], argv[i], NULL);
      exit(0);
    }
    // IMPORTANT!! close writing end of the right pipe, otherwise
    // the program will hang (this is the main bug in your original
    // implementation)
    close(rightPipe[1]);

    // wait properly!
    waitpid(childpid, NULL, 0);

    /* on all but the last iteration, swap */
    if (i + 1 < argc) {
      fprintf(stderr, "%d %d %d %d\n", leftPipe[0], leftPipe[1], rightPipe[0], rightPipe[1]);
      temp = leftPipe;
      leftPipe = rightPipe;
      rightPipe = temp;
      fprintf(stderr, "%d %d %d %d\n", leftPipe[0], leftPipe[1], rightPipe[0], rightPipe[1]);
    }
  }

  nbytes = read(rightPipe[0], readbuffer, sizeof(readbuffer));
  readbuffer[nbytes] = 0;
  fprintf(stderr, "Received string: %s\n", readbuffer);

  return 0;
}

输出:
 >> ./a.out /bin/ls /bin/cat /usr/bin/wc
1: /bin/ls
0 32767 3 4
0 32767 3 4
3 4 0 32767
2: /bin/cat
3 4 4 5
3 4 4 5
4 5 3 4
3: /usr/bin/wc
4 5 5 6
Received string:     266     294    4280

如果您对此解决方案有特定疑问,请告诉我:)原始代码还存在其他一些小问题:
  • 不需要使用指针,我们可以在管道周围进行复制(性能肯定不会有问题;)
  • 使用int代替size_t
  • 您没有解决使用-Wall标志
  • 进行编译时会向您显示的所有警告

    如果您有兴趣,这就是我的写法:
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/wait.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(int argc, char **argv) {
      size_t i, nbytes;
      int left[2], right[2], tmp[2];
      pid_t childpid;
      char readbuffer[80];
    
      left[0] = STDIN_FILENO;
    
      for (i = 1; i < argc; ++i) {
        pipe(right);
    
        switch ((childpid = fork())) {
          case -1:
            perror("fork");
            exit(1);
          case 0:
            dup2(left[0], STDIN_FILENO);
            dup2(right[1], STDOUT_FILENO);
            close(right[0]);
            execl(argv[i], argv[i], NULL);
          default:
            close(right[1]);
            waitpid(childpid, NULL, 0);
        }
    
        if (i == argc - 1) break;
        memcpy(tmp,   left,  sizeof tmp);
        memcpy(left,  right, sizeof left);
        memcpy(right, tmp,   sizeof right);
      }
    
      nbytes = read(right[0], readbuffer, sizeof readbuffer);
      readbuffer[nbytes] = 0;
      fprintf(stderr, "Received string: %s\n", readbuffer);
    
      return 0;
    }
    

    关于c - 我了解Unix文件描述符如何在C中工作吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/9254198/

    10-12 18:26