我编写了一个C程序,它使用多个管道来模拟shell。问题是我可以运行大多数命令,如ls | cat
等,但我无法使用ls | wc
。有wc
无效的情况吗?
int pipefd[4];
int p1 = pipe(pipefd); // Open pipe 1
int p2 = pipe(pipefd + 2); // Open pipe 2
pid_t pid;
for(i = 0; i < n_commands; i++)
{
fflush(stdout);
pid = fork();
if(pid == 0)
{
int command_no = i;
int prev_pipe = ((command_no - 1) % 2) * 2;
int current_pipe = (command_no % 2) * 2;
// If current command is the first command, close the
// read end, else read from the last command's pipe
if(command_no == 0)
{
close(pipefd[0]);
}
else
{
dup2(pipefd[prev_pipe], 0);
close(pipefd[current_pipe]);
}
// If current command is the last command, close the
// write end, else write to the pipe
if(command_no == n_commands - 1)
{
close(pipefd[current_pipe + 1]);
}
else
{
dup2(pipefd[current_pipe + 1], 1);
}
int p = execvp(tokens[cmd_pos[command_no]], tokens + cmd_pos[command_no]);
close(pipefd[current_pipe]);
close(pipefd[prev_pipe]);
close(pipefd[prev_pipe + 1]);
close(pipefd[current_pipe + 1]);
_exit(0);
}
}
如果/usr/bin
中的程序不是管道中的第一个命令,那么它们似乎没有被执行。
答案 0 :(得分:1)
您正在错误地连接管道。
这个逻辑:
int prev_pipe = ((command_no - 1) % 2) * 2;
int current_pipe = (command_no % 2) * 2;
不起作用 - 模数的结果将始终为0
或1
,因此prev_pipe
和current_pipe
将为0
或{{ 1}} ...
好吧,除非我错过了一些隐藏的概念,因为你没有粘贴任何创建管道的代码。
答案 1 :(得分:1)
这是一个从您的代码创建的非常简单的程序 - 猜测如何创建管道并简化命令argv
处理:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static char *argv_ls[] = { "ls", 0 };
static char *argv_wc[] = { "wc", 0 };
static char **cmds[] = { argv_ls, argv_wc };
int main(void)
{
int n_commands = 2;
int pipefd[2];
pipe(&pipefd[0]); // Error check!
fflush(stdout);
for (int i = 0; i < n_commands; i++)
{
int pid = fork();
if (pid == 0)
{
int command_no = i;
int prev_pipe = ((command_no - 1) % 2) * 2;
int current_pipe = (command_no % 2) * 2;
printf("cmd %d: prev pipe %d, curr pipe %d\n", i, prev_pipe, current_pipe);
fflush(stdout);
// If current command is the first command, close the
// read end, else read from the last command's pipe
if (command_no == 0)
{
close(pipefd[0]);
}
else
{
dup2(pipefd[prev_pipe], 0);
close(pipefd[current_pipe]); // Line 40
}
// If current command is the last command, close the
// write end, else write to the pipe
if (command_no == n_commands - 1)
close(pipefd[current_pipe + 1]); // Line 46
else
dup2(pipefd[current_pipe + 1], 1);
execvp(cmds[i][0], cmds[i]);
fprintf(stderr, "Failed to exec: %s (%d: %s)\n", cmds[i][0], errno, strerror(errno));
_exit(1);
}
}
return 0;
}
当GCC 4.7.1(在Mac OS X 10.7.4上)编译它时,它会发出警告:
pipes-12133858.c: In function ‘main’:
pipes-12133858.c:40:22: warning: array subscript is above array bounds [-Warray-bounds]
pipes-12133858.c:46:22: warning: array subscript is above array bounds [-Warray-bounds]
当我运行它时,我得到输出:
Isis JL: pipes-12133858
cmd 0: prev pipe -2, curr pipe 0
cmd 1: prev pipe 0, curr pipe 2
Isis JL: wc: stdin: read: Bad file descriptor
由于代码中的父级不等待子代完成,因此提示出现在wc
的错误消息之前,但是打印的诊断编号显示存在各种问题(并且编译器是能够发现一些问题。)
请注意,无需检查任何exec*()
系列函数的返回值。如果他们成功了,他们就不会回来;如果他们回来,他们就失败了。在调用_exit(0);
之前也没有必要关闭,因为系统无论如何都会关闭它们。此外,当您未能执行某些操作时,打印一条消息指示您未能执行的操作并以非零退出状态退出是有礼貌的。
所以,正如Michał Górny所说,问题的一个主要部分是你的管道处理代码至少是神秘的,因为你没有显示它并且可能是错误的。
我也很容易确定您的代码中没有足够的close()
次调用。作为指导原则,在其中将打开管道的每个进程中,将成为管道的一部分,pipe()
系统调用返回的所有文件描述符应在任何给定的子进程使用{{1功能。不关闭管道会导致进程挂起,因为管道的写入端已打开。如果打开写入结束的进程是尝试从管道的读取端读取的进程,那么它将不会找到任何要读取的数据。