我实际上正致力于在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));
}
答案 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,ls
和cat
(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
重定向它,它可以正常工作。我不明白我得到的错误(这至少可以说是令人厌烦的。)
<<<