[英]UNIX pipes on C block on read
我正在努力用類管道實現一個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(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);
}
}
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, Exited, info: 0
Foreground pid: 9252, command: cmd2, Exited, info: 0
Foreground pid: 9253, command: cmd3, Exited, info: 0
Foreground pid: 9254, command: cmd4, Exited, info: 0
hola!
Foreground pid: 9255, command: cmd5, Exited, info: 0
可能是什么問題呢?
你沒有足夠的關閉。 在代碼中:
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);
}
在將管道復制到stdin
和stdout
(通過fileno()
)之后,需要關閉管道:
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);
}
}
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)創建管道,它必須關閉管道的兩端。 在開始學習管道管道時,沒有關閉足夠的管道可能是最常見的錯誤。
好吧,我終於解決了。
在父進程上,就在整個子fork之后,我把:
f (cmd->in[0] != fileno(stdin)) {
close(cmd->in[0]);
close(cmd->in[1]);
}
瞧。 之前我曾做過類似的事情,但我輸入錯誤並close(cmd->out[0]);
代替。 就是這樣了。 現在有道理。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.