gpt4 book ai didi

c - 尝试用 "ls | grep r"运行 "execvp()"

转载 作者:行者123 更新时间:2023-11-30 14:22:30 25 4
gpt4 key购买 nike

  1. 我在两个子进程之间创建了一个管道,首先,我运行 ls,它写入正确的 fd,然后,我运行 grep r,它从正确的 fd 中读取,

  2. 我可以在终端中看到 grep 命令运行良好(输出)

  3. 问题是 grep 不会退出,它会留在那里,即使 ls 不再运行

对于其他程序,管道工作正常..

for (i = 0; i < commands_num ; i++) {   //exec all the commands instants
if (pcommands[i]._flag_pipe_out == 1) { //creates pipe if necessary
if (pipe(pipe_fd) == -1) {
perror("Error: \"pipe()\" failed");
}
pcommands[i]._fd_out = pipe_fd[1];
pcommands[i+1]._fd_in = pipe_fd[0];
}
pid = fork(); //the child exec the commands
if (pid == -1) {
perror("Error: \"fork()\" failed");
break;
} else if (!pid) { //child process

if (pcommands[i]._flag_pipe_in == 1) { //if there was a pipe to this command
if (dup2(pcommands[i]._fd_in, STDIN) == -1) {
perror("Error: \"dup2()\" failed");
exit(0);
}
close(pcommands[i]._fd_in);
}

if (pcommands[i]._flag_pipe_out == 1) { //if there was a pipe from this command
if (dup2(pcommands[i]._fd_out, STDOUT) == -1) {
perror("Error: \"dup2()\" failed");
exit(0);
}
close(pcommands[i]._fd_out);
}
execvp(pcommands[i]._commands[0] , pcommands[i]._commands); //run the command

perror("Error: \"execvp()\" failed");
exit(0);
} else if (pid > 0) { //father process
waitpid(pid, NULL, WUNTRACED);
}
}
//closing all the open fd's
for (i = 0; i < commands_num ; i++) {
if (pcommands[i]._fd_in != STDIN) { //if there was an other stdin that is not 0
close(pcommands[i]._fd_in);
}
if (pcommands[i]._fd_out != STDOUT) { //if there was an other stdout that is not 1
close(pcommands[i]._fd_out);
}
}

所以,我有一个“命令”即时 pcommands[i]它有:管道输入、管道输出的标志fdin,fdout,和一个 char** (对于真正的命令,如“ls -l”)

可以说一切都很好,这意味着:

pcommands[0]:
pipein=0
pipeout=1
char** = {"ls","-l",NULL}

pcommands[1]:
pipein=1
pipeout=0
char** = {"grep","r",NULL}

现在,循环将进行两次(因为我有两个命令瞬间)第一次,它会看到pcommands[0]有pipeout==1创建管道做 fork pcommands[0] 有 pipelineout==1子: dup2 到标准输出execvp

第二次:不创建管道做 fork child :pcomands[1] 有 pipelinein==1然后: dup2 到输入执行程序..

这个命令有效,我的输出是:

errors.log exer2.pdf multipal_try

(所有带有“r”的内容)但随后它就卡住了,并且无法退出grep..在另一个终端中我可以看到 grep 仍在工作

我希望我关闭所有需要关闭的 fd...

我不明白为什么它不起作用,看起来我做得对(嗯,它适用于其他命令..)

有人可以帮忙吗?谢谢

最佳答案

您没有关闭足够的管道文件描述符。

经验法则:

  • 如果您使用 dup()dup2() 将管道文件描述符复制到标准输入或标准输出,则应关闭两者 原始管道文件描述符。

您还需要确保,如果父 shell 创建管道,它会关闭管道文件描述符的两个副本。

另请注意,管道中的进程应允许同时运行。特别是,管道的容量有限,当管道中没有剩余空间时,进程就会阻塞。该限制可以非常小(POSIX 要求它必须至少为 4 KiB,但仅此而已)。如果您的程序处理兆字节的数据,则必须允许它们在管道中同时运行。因此,waitpid() 应该发生在启动子进程的循环之外。等待之前还需要关闭父进程中的管道;否则,读取管道的子进程将永远不会看到 EOF(因为理论上,父进程可以写入管道,即使它不会)。

您的结构成员的名称以下划线开头。那很危险。以下划线开头的名称保留用于实现。 C 标准说:

ISO/IEC 9899:2011 §7.1.3 Reserved Identifiers

— All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use.
— All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces.

这意味着如果您遇到问题,那么问题是您的,而不是系统的。显然,您的代码可以工作,但您应该意识到可能遇到的问题,并且最好避免它们。

<小时/>

示例代码

这是一个基于上面代码的固定 SSCCE:

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

typedef struct Command Command;
struct Command
{
int _fd_out;
int _fd_in;
int _flag_pipe_in;
int _flag_pipe_out;
char **_commands;
};

typedef int Pipe[2];

enum { STDIN = STDIN_FILENO, STDOUT = STDOUT_FILENO, STDERR = STDERR_FILENO };

int main(void)
{
char *ls_cmd[] = { "ls", 0 };
char *grep_cmd[] = { "grep", "r", 0 };
Command commands[] =
{
{
._fd_in = 0, ._flag_pipe_in = 0,
._fd_out = 1, ._flag_pipe_out = 1,
._commands = ls_cmd,
},
{
._fd_in = 0, ._flag_pipe_in = 1,
._fd_out = 1, ._flag_pipe_out = 0,
._commands = grep_cmd,
}
};
int commands_num = sizeof(commands) / sizeof(commands[0]);

/* Allow valgrind to check memory */
Command *pcommands = malloc(commands_num * sizeof(Command));
for (int i = 0; i < commands_num; i++)
pcommands[i] = commands[i];

for (int i = 0; i < commands_num; i++) { //exec all the commands instants
if (pcommands[i]._flag_pipe_out == 1) { //creates pipe if necessary
Pipe pipe_fd;
if (pipe(pipe_fd) == -1) {
perror("Error: \"pipe()\" failed");
}
pcommands[i]._fd_out = pipe_fd[1];
pcommands[i+1]._fd_in = pipe_fd[0];
}
pid_t pid = fork(); //the child exec the commands
if (pid == -1) {
perror("Error: \"fork()\" failed");
break;
} else if (!pid) { //child process

if (pcommands[i]._flag_pipe_in == 1) { //if there was a pipe to this command
assert(i > 0);
assert(pcommands[i-1]._flag_pipe_out == 1);
assert(pcommands[i-1]._fd_out > STDERR);
if (dup2(pcommands[i]._fd_in, STDIN) == -1) {
perror("Error: \"dup2()\" failed");
exit(0);
}
close(pcommands[i]._fd_in);
close(pcommands[i-1]._fd_out);
}

if (pcommands[i]._flag_pipe_out == 1) { //if there was a pipe from this command
assert(i < commands_num - 1);
assert(pcommands[i+1]._flag_pipe_in == 1);
assert(pcommands[i+1]._fd_in > STDERR);
if (dup2(pcommands[i]._fd_out, STDOUT) == -1) {
perror("Error: \"dup2()\" failed");
exit(0);
}
close(pcommands[i]._fd_out);
close(pcommands[i+1]._fd_in);
}
execvp(pcommands[i]._commands[0] , pcommands[i]._commands); //run the command

perror("Error: \"execvp()\" failed");
exit(1);
}
else
printf("Child PID %d running\n", (int)pid);
}

//closing all the open pipe fd's
for (int i = 0; i < commands_num; i++) {
if (pcommands[i]._fd_in != STDIN) { //if there was another stdin that is not 0
close(pcommands[i]._fd_in);
}
if (pcommands[i]._fd_out != STDOUT) { //if there was another stdout that is not 1
close(pcommands[i]._fd_out);
}
}

int status;
pid_t corpse;
while ((corpse = waitpid(-1, &status, 0)) > 0)
printf("Child PID %d died with status 0x%.4X\n", (int)corpse, status);

free(pcommands);

return(0);
}

Just for my knowledge, how would you do it, so it won't get "indisputably messy"?

我可能会保留管道信息,以便 child 不需要担心断言中包含的条件(在管道中访问 child 之前或之后的 child 信息)。如果每个 child 只需要访问自己数据结构中的信息,那就更干净了。我将重新组织“struct Command”,使其包含两个管道,以及哪个管道包含需要关闭的信息的指示器。在很多方面,与你所拥有的并没有根本不同;只是那个 child i更整洁,只需要查看pcommands[i]

您可以在不同的上下文中看到部分答案:C Minishell adding pipelines .

关于c - 尝试用 "ls | grep r"运行 "execvp()",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13693446/

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