0

我实际上正在研究在 shell 中实现多管道,我必须说我在解决项目中遇到的问题时遇到了一些问题。

一个特殊的细节使这种多管道实现非常困难,它必须在文件描述符的限制非常低的情况下工作。

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

我的 multipipe 也必须这样做。

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

在真正的 shell 中,“wc”应该显示它的结果,然后“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));
}
4

1 回答 1

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 变为wc)。您有父进程等待 4627 退出;你有 4627 等待 4629 ( cat); 所以在你输入 at 之前什么都不会发生cat

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

这棵树有点奇怪,因为它被设置为将管道中最左边的进程 ( cat) 作为树中最右边的进程。

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

无论它是否是您真正想要的,代码似乎都按照实现的设计运行。

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

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

仪器仪表

#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

#define PIPE    1
#define CMD     2

typedef struct s_tree
{
    char          *cmd;
    char           type;
    struct s_tree *left;
    struct s_tree *right;
}               t_tree;

enum { DESCRIPTORS_PER_LINE = 64 };

static void open_fds(int max_fd)
{
    int fd;
    struct stat buff;

    fprintf(stderr, "%d: ", (int)getpid());
    for (fd = 0; fd < max_fd; fd++)
    {
        if (fstat(fd, &buff) < 0)
            putc('-', stderr);
        else
            putc('o', stderr);
        if (fd % DESCRIPTORS_PER_LINE == DESCRIPTORS_PER_LINE - 1)
            putc('\n', stderr);
    }
    if (fd % DESCRIPTORS_PER_LINE != 0)
        putc('\n', stderr);
}

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

static void dump_node(t_tree *tree)
{
    assert(tree != 0);
    fprintf(stderr, "%d: Node type %d\n", (int)getpid(), tree->type);
    if (tree->cmd != 0)
        fprintf(stderr, "%d: Command: %s\n", (int)getpid(), tree->cmd);
    fprintf(stderr, "%d: Left: 0x%.12" PRIXPTR ", Right: 0x%.12" PRIXPTR "\n",
            (int)getpid(), (uintptr_t)tree->left, (uintptr_t)tree->right);
}

static int execute_cmd(t_tree *tree)
{
    //usleep((10 + rand() % 100) * 1000);
    assert(tree->type == CMD);
    dump_node(tree);
    fprintf(stderr, "%d: Command: %s\n", (int)getpid(), tree->cmd);
    open_fds(10);
    execlp(tree->cmd, tree->cmd, NULL);
    fprintf(stderr, "%d: Exec fail (%s)\n", (int)getpid(), tree->cmd);
    my_exit(102);
    return(-1);
}

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

    assert(tree->type == PIPE);
    dump_node(tree);
    if (pipe(fd) < 0)
        my_exit(1);
    fprintf(stderr, "%d: Pipe: r %d, w %d\n", (int)getpid(), fd[0], fd[1]);
    if ((pidleft = fork()) < 0)
        my_exit(2);
    if (pidleft == 0)
    {
        fprintf(stderr, "%d: L-Child\n", (int)getpid());
        open_fds(10);
        fflush(0);
        if (close(fd[1]) < 0)
            my_exit(3);
        if (dup2(fd[0], 0) < 0)
            my_exit(4);
        close(fd[0]);
        if (tree->left->type == PIPE)
            return(execute_tree(tree->left));
        else if (tree->left->type == CMD)
            return(execute_cmd(tree->left));
        else
            my_exit(100);
    }
    else
    {
        if (tree->right != NULL)
        {
            if ((pidright = fork()) < 0)
                my_exit(5);
            if (close(fd[0]) < 0)
                my_exit(6);
            if (pidright == 0)
            {
                fprintf(stderr, "%d: R-Child\n", (int)getpid());
                open_fds(10);
                if (dup2(fd[1], 1) < 0)
                    my_exit(7);
                close(fd[1]);
                if (tree->right->type == PIPE)
                    return(execute_tree(tree->right));
                else if (tree->right->type == CMD)
                    return(execute_cmd(tree->right));
                else
                    my_exit(101);
            }
            else
            {
                fprintf(stderr, "Parent %d - waiting for R-Child (%d)\n", (int)getpid(), pidright);
                open_fds(10);
                if (close(fd[1]) < 0)
                    my_exit(8);
                if (waitpid(pidright, &statusright, 0) < 0)
                    my_exit(9);
                fprintf(stderr, "Parent %d - R-Child died %d = 0x%.4X\n",
                       (int)getpid(), pidright, statusright);
            }
        }
        fprintf(stderr, "Parent %d - waiting for L-Child (%d)\n", (int)getpid(), pidleft);
        open_fds(10);
        if (waitpid(pidleft, &statusleft, 0) < 0)
            my_exit(10);
        fprintf(stderr, "Parent %d - L-Child died %d = 0x%.4X\n",
               (int)getpid(), pidleft, statusleft);
    }
    return(0);
}

/*
** Pipes at RHS to avoid issues with slaash, backslash, newline, start
** being a comment start symbol
**
**                  PIPE         |
**                  /\           |
**                 /  \          |
**                /    \         |
**              wc     PIPE      |
**                     /\        |
**                    /  \       |
**                   /    \      |
**                 ls     cat    |
**
** cat | ls | wc
*/
int main(void)
{
    t_tree        *tree;

    setvbuf(stderr, 0, _IOLBF, BUFSIZ);

    printf("%d: Main shell\n", (int)getpid());
    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;
    return(execute_tree(tree));
}
于 2013-09-29T22:35:11.117 回答