gpt4 book ai didi

c - 递归 pipe() 调用后 I/O 挂起

转载 作者:行者123 更新时间:2023-12-04 06:13:11 25 4
gpt4 key购买 nike

这是我之前 question 的后续.我正在编写一个 linux shell,所以我需要处理用户输入多个管道命令的可能性。它几乎可以正常工作,除了在 I/O 挂起的最后一个命令上调用 execvp() 之后。我的提示再也不会出现,我必须按 ctrl+C 才能离开 shell。我不认为这是一个无限循环的发生,而是我没有正确关闭我的流。我想不出正确的方法来做到这一点。

示例 - 如果我根本不使用管道,shell 将正确运行:

ad@ubuntu:~/Documents$ gcc mash.c -o mash
ad@ubuntu:~/Documents$ ./mash
/home/ad/Documents> ls
a.out bio1.odt blah.cpp controller.txt mash.c
bio1.doc blahblah.txt Chapter1Notes.odt mash
/home/ad/Documents>

但是,如果我输入:
/home/ad/Documents> ls -l | grep sh
-rwxr-xr-x 1 ad ad 13597 2011-09-26 00:03 mash
-rw-r--r-- 1 ad ad 3060 2011-09-25 23:58 mash.c

提示不再出现。 main() 最初使用 stdin 和 stdout 调用 execute()。

谢谢你的时间!

代码:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>

int MAX_PATH_LENGTH = 1024; //Maximum path length to display.
int BUF_LENGTH = 1024; // Length of buffer to store user input
char * delims = " \n"; // Delimiters for tokenizing user input.
const int PIPE_READ = 0;
const int PIPE_WRITE = 1;

void execute(char **argArray, int read_fd, int write_fd){
dup2(read_fd, 0);
dup2(write_fd, 1);
//Problem when entering only newline character
char **pA = argArray;
int i = 0;
while(*pA != NULL) {
if(strcmp(argArray[i],"<") == 0) {
int input = open(argArray[i+1], O_RDWR | O_CREAT);
pid_t pid = fork();
if(pid == 0) {
dup2(input, 0);
argArray[i] = 0;
execvp(argArray[0], &argArray[0]);
printf("Error redirecting input.\n");
exit(1);
}
wait(pid);
}
else if(strcmp(argArray[i],">") == 0) {
int output = open(argArray[i+1], O_RDWR | O_CREAT);
pid_t pid = fork();
if(pid == 0){
dup2(output,1);
close(output);
argArray[i] = 0;
execvp(argArray[0], &argArray[0]);
printf("Error redirecting output.\n");
exit(1);
}
close(output);
wait(NULL);

}
else if(strcmp(argArray[i],"|") == 0) {
int fds[2];
pipe(fds);
pid_t pid = fork();
if(pid == 0) {
dup2(fds[PIPE_WRITE], 1);
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
argArray[i] = 0;
execvp(argArray[0], &argArray[0]);
printf("%s: command not found.\n", argArray[0]);
exit(1);
} else {
dup2(fds[PIPE_READ], 0);
execute(&argArray[i+1], 0, 1);
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
wait(pid);
printf("herp\n");
}
}
*pA++;
i++;
}
pid_t pid = vfork();
if(pid == 0){
execvp(argArray[0], &argArray[0]);
printf("%s: command not found.\n", argArray[0]);
exit(1);
}
else {
wait(NULL);
}
}

int main () {

char path[MAX_PATH_LENGTH];
char buf[BUF_LENGTH];
char* strArray[BUF_LENGTH];
/**
* "Welcome" message. When mash is executed, the current working directory
* is displayed followed by >. For example, if user is in /usr/lib/, then
* mash will display :
* /usr/lib/>
**/
getcwd(path, MAX_PATH_LENGTH);
printf("%s> ", path);
fflush(stdout);

/**
* Loop infinitely while waiting for input from user.
* Parse input and display "welcome" message again.
**/
while(1) {
fgets(buf, BUF_LENGTH, stdin);
char *tokenPtr = NULL;
int i = 0;
tokenPtr = strtok(buf, delims);

if(strcmp(tokenPtr, "exit") == 0){

exit(0);
}
else if(strcmp(tokenPtr, "cd") == 0){
tokenPtr = strtok(NULL, delims);
if(chdir(tokenPtr) != 0){
printf("Path not found.\n");
}
getcwd(path, MAX_PATH_LENGTH);
}
else if(strcmp(tokenPtr, "pwd") == 0){
printf("%s\n", path);
}
else {
while(tokenPtr != NULL) {
strArray[i++] = tokenPtr;
tokenPtr = strtok(NULL, delims);
}
execute(strArray, 0, 1);
}

bzero(strArray, sizeof(strArray)); // clears array
printf("%s> ", path);
fflush(stdout);
}

}

最佳答案

此行 - dup2(fds[PIPE_READ], 0); - 用引用管道的描述符覆盖您当前的标准输入文件描述符。一旦管道命令完成,任何从 stdin 读取的尝试都将失败。

此行 - fgets(buf, BUF_LENGTH, stdin); - 不检查错误情况。

最后 - 在开始第二个进程之前,您等待管道中的第二个进程完成。这就是导致您陷入僵局的原因; “grep”命令正在等待输入,但您还没有执行“ls”命令。您等待 grep 命令完成,但它无法完成,因为它正在等待输入。

在您最新的代码化身中:调用 execute() 函数时,它会扫描参数并找到管道;然后它派生并运行第一个命令(“ls”):

        pid_t pid = fork();
if(pid == 0) {
dup2(fds[PIPE_WRITE], 1);
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
argArray[i] = 0;
execvp(argArray[0], &argArray[0]);
printf("%s: command not found.\n", argArray[0]);
exit(1);

然后它递归,再次调用execute():
        } else {
dup2(fds[PIPE_READ], 0);
execute(&argArray[i+1], 0, 1); // <--- HERE

...这当然会在返回之前 fork 并运行“grep”。请注意,它使用/both/管道字段描述符/open/这样做。因此,grep 进程本身将保持管道的两端打开。 execute() 在返回之前执行 wait(NULL) ;然而,这实际上会等待“ls”完成(因为这是首先完成的过程)。然后它返回,并继续:
            close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
wait(pid); // <--- WRONG, compile with -Wall to see why
printf("herp\n");
}

我指出了一个错误。尝试使用“-Wall”进行编译,或阅读 wait() 函数的文档!如果您将其更改为 wait(NULL) 它将是正确的,但是,在这种情况下它会阻塞。原因是“grep”命令尚未完成,仍在读取输入。它仍在读取输入的原因是因为 grep 进程本身已打开管道的写入端!!因此,grep 永远不会看到来自管道的输入的“结束”。一个简单的解决方法是在递归调用 execute() 之前关闭管道 fds(但是,您的代码仍然存在其他问题,包括,正如我已经指出的,您正在破坏标准输入描述符)。

关于c - 递归 pipe() 调用后 I/O 挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7550856/

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