gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-30 17:47:01 28 4
gpt4 key购买 nike

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

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

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

我的多管也必须这样做。

我一直在研究这个问题并提出了几个不同的版本。最后一个似乎可以部分工作,但不符合真实外壳的显示。我所做的是创建一棵树来执行命令“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));
}

最佳答案

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

输出

$ 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 );因此,在您输入 cat 之前,不会发生任何事情。 .

当您输入 cat 时,由于管道关闭而退出; 4629 由 4627 收集,然后 4627 也继续收集 4628 ( ls )。现在 4625 开始收集 4627,然后是 4626,然后自身终止。

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

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

无论这是否是您真正想要的,代码似乎都是根据实现的设计运行的。

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

我不确定您想如何解决这个问题。正如评论中所指出的,我倾向于将其结构更像 C Minishell Adding Pipelines 中的代码。 。然而,当管道中的第一个进程从终端读取时(例如 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));
}

关于c - 具有文件描述符限制的多管道实现无法正常工作。 (壳),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19070890/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com