我正在努力用类管道实现一个
shell.
typedef struct { char** cmd; int in[2]; int out[2]; } cmdio; cmdio cmds[MAX_PIPE + 1];
读取管道中的命令并将其存储在cmds中.
cmdio [i] .in是pipe()返回的输入管道的文件描述符对.对于从终端输入读取的第一个命令,它只是{fileno(stdin),– 1}. cmdin [i] .out对输出管道/终端输出类似. cmdio [i] .in与cmd [i-1] .out相同.例如:
$ls -l | sort | wc CMD: ls -l IN: 0 -1 OUT: 3 4 CMD: sort IN: 3 4 OUT: 5 6 CMD: wc IN: 5 6 OUT: -1 1
我们将每个命令传递给process_command,它执行许多操作:
for (cmdi = 0; cmds[cmdi].cmd != NULL; cmdi++) { process_command(&cmds[cmdi]); }
现在,在process_command中:
if (!(pid_fork = fork())) { dup2(cmd->in[0],fileno(stdin)); dup2(cmd->out[1],fileno(stdout)); if (cmd->in[1] >= 0) { if (close(cmd->in[1])) { perror(NULL); } } if (cmd->out[0] >= 0) { if (close(cmd->out[0])) { perror(NULL); } } execvp(cmd->cmd[0],cmd->cmd); exit(-1); }
问题是永远从管道中读取:
COMMAND $ls | wc Created pipe,in: 5 out: 6 Foreground pid: 9042,command: ls,Exited,info: 0 [blocked running read() within wc]
如果,而不是与execvp交换过程,我只是这样做:
if (!(pid_fork = fork())) { dup2(cmd->in[0],fileno(stdout)); if (cmd->in[1] >= 0) { if (close(cmd->in[1])) { perror(NULL); } } if (cmd->out[0] >= 0) { if (close(cmd->out[0])) { perror(NULL); } } char buf[6]; read(fileno(stdin),buf,5); buf[5] = '\0'; printf("%s\n",buf); exit(0); }
它碰巧工作:
COMMAND $cmd1 | cmd2 | cmd3 | cmd4 | cmd5 Pipe creada,in: 11 out: 12 Pipe creada,in: 13 out: 14 Pipe creada,in: 15 out: 16 Pipe creada,in: 17 out: 18 hola! Foreground pid: 9251,command: cmd1,info: 0 Foreground pid: 9252,command: cmd2,info: 0 Foreground pid: 9253,command: cmd3,info: 0 Foreground pid: 9254,command: cmd4,info: 0 hola! Foreground pid: 9255,command: cmd5,info: 0
可能是什么问题呢?
解决方法
你没有足够的关闭.在代码中:
if (!(pid_fork = fork())) { dup2(cmd->in[0],cmd->cmd); exit(-1); }
在将管道复制到stdin和stdout(通过fileno())之后,需要关闭管道:
if (!(pid_fork = fork())) { dup2(cmd->in[0],fileno(stdout)); if (cmd->in[1] >= 0) { if (close(cmd->in[1])) { perror(NULL); } } if (cmd->out[0] >= 0) { if (close(cmd->out[0])) { perror(NULL); } } close(cmd->in[0]); // Or your error checked version,but I'd use a function close(cmd->out[1]); execvp(cmd->cmd[0],cmd->cmd); exit(-1); }
程序没有完成,因为文件的写入结束仍然打开.另外,不要忘记,如果父进程(shell)创建管道,它必须关闭管道的两端.在开始学习管道管道时,没有关闭足够的管道可能是最常见的错误.