C实现Minibash:execvp与父进程管道调用read命令卡顿问题
管道末尾的read内置函数阻塞问题
这是大学项目任务,无法分享完整代码,致歉。需实现read内置函数用于创建环境变量,逻辑本身不难,但当它作为管道最后一项时(例如ls | grep aux.txt | read a,预期环境变量a的值为aux.txt),程序会卡在fgets(value, sizeof(value),stdin);处,甚至终端无法恢复。
补充说明:若前序命令为内置命令(例如echo hi | echo hi2 | read a),$a可正常设为hi2,无异常。已尝试修改管道、替换fgets为read等方法,均未解决,这是代码中唯一未修复的部分。
简化主函数示例
char **argvv; int fd[2][2]; int pid; void fun_close(int **fd){ close(fd[0][0]); close(fd[0][1]); close(fd[1][0]); close(fd[1][1]); } int main(int argc, char ***argvv) { argvv[0][0] = "echo"; argvv[0][1] = "elpmaxe"; argvv[1][0] = "rev"; argvv[2][0] = "read"; argvv[2][1] = "a"; for (int i = 0; i < 2; i++) { pipe(fd[i]); } for(int i = 0; i< 3; i++){ pid = fork(); if(pid == 0){ if(i ==0){ dup2(fd[0][1], 1); fun_close((int**)fd); execvp(argvv[0][0], argvv[0]); } if(i == 1){ dup2(fd[0][0], 0); dup2(fd[1][1], 1); fun_close((int**)fd); execvp(argvv[1][0], argvv[0]); } }else{ if(i == 2){ close(fd[0][1]); close(fd[0][0]); fun_read("read a", 3, fd[1]); } } } int corpse; int status; while ((corpse = wait(&status)) > 0) printf("Child %d exited with status 0x%.4X\n", corpse, status); return 0; }
fun_read函数实现
int fun_read(char **command, int argc, int fd[]){ char **env_varv; char value[1024]; char last_var[1024]; char *token; int status; char *delim = " \t\n"; env_varv = malloc((argc-1) * sizeof(char *)); for(int i = 1; i < argc; i++){ env_varv[i-1] = strdup(command[i]); wait(status); } if (fd[0] != 0){ printf("%d\n", fd[0]); dup2(fd[0], 0); close(fd[0]); close(fd[1]); } fgets(value, sizeof(value), stdin); int i = 0; token = strtok(value, delim); last_var[0] = '\0'; while(token != NULL){ if(i == argc-2){ while (token != NULL){ strcat(last_var,token); setenv(env_varv[i],last_var,1); token = strtok(NULL,delim); strcat(last_var," "); } } else if (env_varv[i] != NULL){ setenv(env_varv[i],token,1); token = strtok(NULL,delim); i++; } else{ break; } } return 0; }
问题原因分析
- execvp参数错误:第二个子进程执行
rev命令时,传入了argvv[0](echo的参数数组)而非argvv[1],导致rev命令执行异常,管道中无输出数据。 - 管道写端未关闭:父进程在调用
fun_read前未关闭fd[1][1](管道的写端),此时管道写端仍有打开的文件描述符,读端(fd[1][0])会一直等待数据,不会触发EOF,导致fgets阻塞。 - wait调用错误:
fun_read中的wait(status)参数应为指针(wait(&status)),且在此处等待子进程时机错误,会导致父进程提前阻塞,打乱管道数据流转。
修复方案
- 修正execvp参数:将第二个子进程的
execvp(argvv[1][0], argvv[0]);改为execvp(argvv[1][0], argvv[1]);,确保rev命令拿到正确的参数。 - 关闭管道写端:父进程在调用
fun_read前,关闭fd[1][1]:if(i == 2){ close(fd[0][1]); close(fd[0][0]); close(fd[1][1]); // 新增:关闭管道写端 fun_read("read a", 3, fd[1]); } - 修复wait调用:移除
fun_read循环中的wait(status),改为在父进程中等待所有子进程完成后再处理,或使用waitpid等待特定子进程,确保管道数据已全部写入。 - 确保管道描述符正确关闭:检查所有进程中不需要的管道描述符是否都已关闭,避免因残留的写端导致读阻塞。
内容的提问来源于stack exchange,提问作者Rodri R
相关产品推荐
相关产品推荐

