具有文件描述符限制的多管道实现无法正常工作。 (贝壳)

时间:2013-09-28 19:31:53

标签: c

我实际上正致力于在shell中实现multipipes,我必须说我有 解决我在项目中遇到的问题的一些问题。

一个特定的细节使得这个multipipe实现非常困难,它必须对文件描述符的限制非常低。

例如,如果在bash或tcsh上输入命令“limit descriptors 10”,你仍然可以运行一个非常长的命令,如“ls | cat | cat | cat | cat | cat | cat | cat | cat | cat | cat | cat | cat | wc“没有任何错误消息或管道损坏。

我的multipipe也必须这样做。

我一直在研究这个问题,并提出了几个不同的版本。 最后一个似乎部分工作但不符合真实shell的显示。 我所做的是创建一个执行命令行“cat | ls | wc”的树。

在一个真实的shell中,“wc”应该显示它的结果,然后“cat”应该等待在退出之前接收一个用户输入。

在我的版本中,“cat”循环首先出现,等待用户输入,然后让wc显示结果并退出。

我的“wc”显示出好的结果,似乎它的执行被延迟了。好像它正在等待从管道或其他东西接收信号...... 我无法弄清楚为什么“wc”不会立即显示 我可能没有正确关闭或重复管道?

你会在这些行后面找到我的代码。

提前感谢您提供给我的任何帮助或提示。

#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <sys/wait.h>

#define PIPE    1
#define CMD 2
typedef struct  s_tree
{
  char      *cmd;
  int       fd[2];
  char      type;
  struct s_tree *left;
  struct s_tree *right;
  struct s_tree *parent;
}       t_tree;

/*
static void test_tree(t_tree *tree)
{
  if (tree == NULL)
    {
      printf("Null\n");
      return ;
    }
  if (tree->type == PIPE)
    printf("Type pipe\n");
  else if (tree->type == CMD)
    printf("Type cmd : %s\n", tree->cmd);
  if (tree->left != NULL)
    {
      printf("Gauche\n");
      test_tree(tree->left);
    }
  if (tree->right != NULL)
    {
      printf("Droite\n");
      test_tree(tree->right);
    }
}
*/

void    my_exit(int value)
{
  fprintf(stderr, "EXITING %d\n", value);
  perror(NULL);
  exit(value);
}

int execute_cmd(t_tree *tree)
{
  if (execlp(tree->cmd, tree->cmd, NULL) < 0)
    my_exit(11);
  return (0);
}

int execute_pipe(t_tree *tree)
{
  int   statusleft;
  int   statusright;
  pid_t pidright;
  pid_t pidleft;
  int   fd[2];

  if (pipe(fd) < 0)
    my_exit(1);
  if ((pidleft = fork()) < 0)
    my_exit(2);
  if (pidleft == 0)
    {
      if (close(fd[1]) < 0
      || close(0) < 0)
    my_exit(3);
      if (dup2(fd[0], 0) < 0)
    my_exit(4);
      if (tree->left->type == PIPE)
    return (execute_pipe(tree->left));
      else if (tree->left->type == CMD)
    return (execute_cmd(tree->left));
    }
  else
    {
      if (close(fd[0]) < 0
      || close(1) < 0)
    my_exit(6);
      if ((pidright = fork()) < 0)
    my_exit(5);
      if (pidright == 0)
    {
      if (dup2(fd[1], 1) < 0)
        my_exit(7);
      if (tree->right->type == PIPE)
        return (execute_pipe(tree->right));
      else if (tree->right->type == CMD)
        return (execute_cmd(tree->right));
    }
      else
    {
      if (close(fd[1]) < 0
          || close(0) < 0)
        my_exit(8);
      if (waitpid(pidright, &statusright, 0) < 0)
        my_exit(9);
    }
      if (waitpid(pidleft, &statusleft, 0) < 0)
    my_exit(10);
    }
  return (0);
}

/*
**                  PIPE
**                  /\
**                 /  \
**                /    \
**              wc     PIPE
**                     /\
**                    /  \
**                   /    \
**                 ls     cat
**
** cat | ls | wc
*/

int     main(int ac, char **av)
{
  t_tree    *tree;

  tree = malloc(sizeof(*tree));
  tree->left = malloc(sizeof(*tree));
  tree->right = malloc(sizeof(*tree));
  tree->right->left = malloc(sizeof(*tree));
  tree->right->right = malloc(sizeof(*tree));
  tree->type = PIPE;
  tree->left->right = NULL;
  tree->left->left = NULL;
  tree->left->type = CMD;
  tree->left->cmd = malloc(strlen("/usr/bin/wc") + 1);
  strcpy(tree->left->cmd, "/usr/bin/wc");
  tree->right->type = PIPE;
  tree->right->left->right = NULL;
  tree->right->left->left = NULL;
  tree->right->left->type = CMD;
  tree->right->left->cmd = malloc(strlen("/bin/ls") + 1);
  strcpy(tree->right->left->cmd, "/bin/ls");
  tree->right->right->left = NULL;
  tree->right->right->left = NULL;
  tree->right->right->type = CMD;
  tree->right->right->cmd = malloc(strlen("/bin/cat") + 1);
  strcpy(tree->right->right->cmd, "/bin/cat");
  tree->parent = NULL;
  tree->left->parent = tree;
  tree->right->parent = tree;
  tree->right->left->parent = tree->right;
  tree->right->right->parent = tree->right;
  /*test_tree(tree);*/
  return (execute_pipe(tree));
}

1 个答案:

答案 0 :(得分:0)

以下是代码的检测版本及其输出。

输出

$ nsh
4625: Main shell
4625: Node type 1
4625: Left: 0x7FBFE8403980, Right: 0x7FBFE84039A0
4625: Pipe: r 3, w 4
Parent 4625 - waiting for R-Child (4627)
4625: ooo-o-----
4626: L-Child
4627: R-Child
4626: ooooo-----
4627: ooo-o-----
4627: Node type 1
4627: Left: 0x7FBFE84039C0, Right: 0x7FBFE84039E0
4626: Node type 2
4626: Command: /usr/bin/wc
4627: Pipe: r 3, w 4
4626: Left: 0x000000000000, Right: 0x000000000000
4626: Command: /usr/bin/wc
4626: ooo-------
Parent 4627 - waiting for R-Child (4629)
4627: ooo-o-----
4629: R-Child
4628: L-Child
4629: ooo-o-----
4629: Node type 2
4629: Command: /bin/cat
4628: ooooo-----
4629: Left: 0x000000000000, Right: 0x000000000000
4629: Command: /bin/cat
4629: ooo-------
4628: Node type 2
4628: Command: /bin/ls
4628: Left: 0x000000000000, Right: 0x000000000000
4628: Command: /bin/ls
4628: ooo-------

Parent 4627 - R-Child died 4629 = 0x000D
Parent 4627 - waiting for L-Child (4628)
4627: ooo-------
Parent 4627 - L-Child died 4628 = 0x0000
      49      49     595
Parent 4625 - R-Child died 4627 = 0x0000
Parent 4625 - waiting for L-Child (4626)
4625: ooo-------
Parent 4625 - L-Child died 4626 = 0x0000
$

解释

空行是我点击返回的地方,为cat命令提供了一些输入。正如您所看到的,如果您仔细遵循,cat有PID 4629;当它退出时,它的状态为0x000D,表示它死于SIGPIPE信号(13)。

在此示例运行中,原始shell为PID 4625.它创建一个包含文件描述符3和4的管道,然后创建4626作为左子项,将4627创建为右子项。 PID 4627创建一个管道(也在文件描述符3和4;其他已经关闭和/或重定向到标准输入)并运行4628和4629,lscat(4626变为{{ 1}})。您有父进程等待4627退出;你有4627等待4629(wc);因此,只要您输入cat,就不会发生任何事情。

当您在cat处键入时,它会退出,因为管道已关闭; 4629由4627收集,然后继续收集4628(cat)。现在4625收集4627,然后收集4626,然后自己终止。

树有点奇怪,因为它是在管道中最左边的进程(ls)设置为树中最右边的进程。

通常,管道的状态是管道中最后一个进程的状态。使用cat,您还可以获取管道中其他进程的退出状态,因此主shell必须启动管道中的所有进程(以便进程是其子进程,并且它可以等待它们和收集退出状态。)

无论你是否真的想要,代码似乎都是按照实现的设计行事。

更常见的是,您不是等待特定进程死亡,而是等待任何孩子死亡,然后相应地清理您的数据结构。但是管道在bash之前没有完成,因为你有一个专门等待cat的进程。

我不确定你想怎么解决这个问题。正如评论中所指出的,我倾向于将其结构更像C Minishell Adding Pipelines中的代码。但是,当管道中的第一个进程从终端读取时(例如cat),该代码对我来说表现得很奇怪,给我一个I / O错误。而且我没有设法调试原因!我知道如果我将输入管道输入,或者我使用cat | ls | wc重定向它,它可以正常工作。我不明白我得到的错误(这至少可以说是令人厌烦的。)

仪表

<<<