gpt4 book ai didi

c - 读取时 C block 上的 UNIX 管道

转载 作者:太空狗 更新时间:2023-10-29 17:12:00 26 4
gpt4 key购买 nike

我正在努力为类实现带有管道的 shell。

typedef struct {
char** cmd;
int in[2];
int out[2];
} cmdio;

cmdio cmds[MAX_PIPE + 1];

管道中的命令被读取并存储在cmds中。

cmdio[i].inpipe() 返回的输入管道的一对文件描述符。对于从终端输入读取的第一个命令,它只是 {fileno(stdin), -1}。 cmdin[i].out类似于输出管道/终端输出。 cmdio[i-1].incmd[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);
}

将管道复制到 stdinstdout 之后(通过 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)创建了管道,它必须关闭管道的两端。没有关闭足够的管道可能是开始学习管道管道时最常见的错误。

关于c - 读取时 C block 上的 UNIX 管道,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10958022/

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