父进程调用mini_read时fgets不等待输入,子进程正常的问题求助
问题描述
我有一个用于设置环境变量的函数mini_read,代码如下:
int mini_read(char ** variables){ if(variables[1] == NULL){ fprintf(stderr, "error\n"); return 1; } char buf[100]; fgets(buf, 100, stdin); //I doubt the bug is below here buf[strcspn(buf, "\n")] = 0; char ** valores = NULL; char * valor = strtok(buf, " \t"); valores[0] = valor; int i = 1; // Guardar valores en array while(valor != NULL){ valor = strtok(NULL, " \t"); valores[i] = valor; i++; } for(int i = 1; ; i++){ if(valores[i-1] == NULL) break; //printf("variables[%d]: %s\n valor[%d]: %c\n", i, variables[i], i, valor[i]); if(variables[i] == NULL){ char*fin = NULL; //Si hay más valores que variables, concatenar valores for(int j = i-1; valores[j] != NULL; j++){ printf("%s\n", fin); strcat(fin, valores[j]); } printf("%s\n", fin); setenv(variables[i-1], fin, 1); break; } setenv(variables[i], valores[i-1], 1); } return 0; }
由于子进程调用该函数无法全局设置环境变量,我希望在父进程中调用它。但测试发现:子进程调用时fgets能正常等待输入,父进程调用时fgets却直接跳过不等待输入。主函数代码如下:
int main(void){ pid_t pid = fork(); if(pid == 0){ //Option 1 if(strcmp(argv[0], "read") == 0) mini_read(argv); } else{ //Option 2 if(strcmp(argv[0], "read") == 0) mini_read(argv); waitpid(pid, &wstatus, 0); } }
请问该如何解决父进程中fgets不等待输入的问题?
解决方法
1. 核心问题:父子进程的stdin竞争与逻辑错误
fork()会让父子进程共享同一个标准输入文件描述符,你的代码中如果argv[0]为"read",父子进程会同时进入mini_read调用,两者竞争读取stdin,导致父进程的fgets出现异常。正确逻辑是只在父进程中执行mini_read,子进程不处理该逻辑。
修改后的主函数代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/wait.h> #include <unistd.h> int main(int argc, char **argv){ pid_t pid = fork(); int wstatus; if(pid == 0){ // 子进程执行其他逻辑,不处理read命令 exit(0); } else if(pid > 0){ // 仅父进程处理read命令 if(strcmp(argv[0], "read") == 0){ mini_read(argv); } waitpid(pid, &wstatus, 0); } else{ perror("fork failed"); exit(1); } return 0; }
2. 修复mini_read的致命内存错误
原mini_read存在多个内存操作错误,即使解决fgets问题也会崩溃:
- 空指针
valores直接赋值,触发段错误; - 空指针
fin调用strcat,非法内存访问; strtok循环逻辑错误,导致数组越界。
修复后的mini_read示例:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int mini_read(char ** variables){ if(variables[1] == NULL){ fprintf(stderr, "error: no variable name provided\n"); return 1; } char buf[100]; // 清空stdin残留的输入 int c; while((c = getchar()) != '\n' && c != EOF); rewind(stdin); if(fgets(buf, sizeof(buf), stdin) == NULL){ fprintf(stderr, "error reading input\n"); return 1; } buf[strcspn(buf, "\n")] = 0; // 统计token数量 int token_count = 0; char * temp = strdup(buf); if(temp == NULL){ perror("strdup failed"); return 1; } char * token = strtok(temp, " \t"); while(token != NULL){ token_count++; token = strtok(NULL, " \t"); } free(temp); // 分配valores数组内存 char ** valores = malloc((token_count + 1) * sizeof(char*)); if(valores == NULL){ perror("malloc failed"); return 1; } // 填充valores数组 token = strtok(buf, " \t"); int i = 0; while(token != NULL){ valores[i] = token; i++; token = strtok(NULL, " \t"); } valores[i] = NULL; // 设置环境变量 for(i = 1; ; i++){ if(valores[i-1] == NULL) break; if(variables[i] == NULL){ // 拼接剩余参数 size_t total_len = 0; for(int j = i-1; valores[j] != NULL; j++){ total_len += strlen(valores[j]) + 1; } char * fin = malloc(total_len); if(fin == NULL){ perror("malloc failed"); free(valores); return 1; } fin[0] = '\0'; for(int j = i-1; valores[j] != NULL; j++){ if(j > i-1) strcat(fin, " "); strcat(fin, valores[j]); } setenv(variables[i-1], fin, 1); free(fin); break; } setenv(variables[i], valores[i-1], 1); } free(valores); return 0; }
3. 额外检查
可以在父进程中检查stdin是否连接到终端,避免非终端输入导致的异常:
if(!isatty(STDIN_FILENO)){ fprintf(stderr, "error: stdin is not a terminal\n"); return 1; }
内容的提问来源于stack exchange,提问作者miguelrr_11
相关产品推荐
相关产品推荐

