C语言父子进程通信:子进程无法正确创建指定文件求助
修复进程间管道通信的文件写入程序
问题根源
原程序的异常由以下多个问题共同导致:
- 管道映射错误:需求中长字符串需发送给子进程2(对应
pipe_2),短字符串发送给子进程1(对应pipe_1),但代码中写入逻辑完全颠倒,导致子进程接收错误数据。 - 文件名包含无效换行符:从终端读取文件名时,未过滤用户输入的换行符
\n,导致创建的文件名带无效字符,显示为乱码。 - 管道未正确关闭导致阻塞:父进程发送完文件名后未关闭管道写端,子进程会一直阻塞在后续的字符串读取操作,无法完成文件创建。
- 长度计算变量未重置:父进程中
k和str_len未在每次循环开始时重置,导致后续字符串长度计算累积错误。 - 类型不匹配:子进程用
int接收父进程发送的size_t类型文件名长度,因字节数差异导致长度读取错误,进而读入乱码文件名。 - 子进程仅处理单次输入:原代码中子进程仅读取一次字符串,无法处理多个输入内容。
修复后的代码
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #include <sys/wait.h> const int BUFF = 1024; int main() { int id1, id2, pipe_1[2], pipe_2[2]; if (pipe(pipe_1) == -1){ perror("pipe1"); return -1;} if (pipe(pipe_2) == -1){ perror("pipe2"); return -1;} // 修正fork逻辑,避免子进程额外创建进程 id1 = fork(); if (id1 == 0) { goto child1; } else if (id1 == -1) { perror("child1 fork failed"); return -1; } id2 = fork(); if (id2 == -1) { perror("child2 fork failed"); return -1; } child1: if (id1 == 0){ // 子进程1逻辑 int str_len; size_t file_name_len; int fd_file; // 读取文件名长度 if (read(pipe_1[0], &file_name_len, sizeof(size_t)) <= 0) { perror("child1 read file length failed"); close(pipe_1[0]); return -1; } // 读取文件名并添加结束符 char file_name[file_name_len + 1]; if (read(pipe_1[0], file_name, file_name_len) <= 0) { perror("child1 read file name failed"); close(pipe_1[0]); return -1; } file_name[file_name_len] = '\0'; // 关闭未使用的写端 close(pipe_1[1]); // 循环读取字符串并写入文件 while (read(pipe_1[0], &str_len, sizeof(int)) > 0) { char str[str_len + 1]; if (read(pipe_1[0], str, str_len) <= 0) { perror("child1 read string failed"); break; } str[str_len] = '\0'; fd_file = open(file_name, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR); if (fd_file == -1) { perror("child1 open file failed"); close(pipe_1[0]); return -1; } write(fd_file, str, str_len); close(fd_file); } close(pipe_1[0]); } else if (id2 == 0){ // 子进程2逻辑 int str_len; size_t file_name_len; int fd_file; if (read(pipe_2[0], &file_name_len, sizeof(size_t)) <= 0) { perror("child2 read file length failed"); close(pipe_2[0]); return -1; } char file_name[file_name_len + 1]; if (read(pipe_2[0], file_name, file_name_len) <= 0) { perror("child2 read file name failed"); close(pipe_2[0]); return -1; } file_name[file_name_len] = '\0'; close(pipe_2[1]); while (read(pipe_2[0], &str_len, sizeof(int)) > 0) { char str[str_len + 1]; if (read(pipe_2[0], str, str_len) <= 0) { perror("child2 read string failed"); break; } str[str_len] = '\0'; fd_file = open(file_name, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR); if (fd_file == -1) { perror("child2 open file failed"); close(pipe_2[0]); return -1; } write(fd_file, str, str_len); close(fd_file); } close(pipe_2[0]); } else { // 父进程逻辑 char str[BUFF]; // 读取并发送两个文件名 for(int i = 0; i < 2; i++){ char file_name[BUFF]; ssize_t read_len = read(STDIN_FILENO, file_name, BUFF); if (read_len <= 0) { perror("parent read file name failed"); return -1; } // 截断换行符,获取有效文件名长度 size_t file_len = strcspn(file_name, "\n"); file_name[file_len] = '\0'; file_len = strlen(file_name); if (i == 0){ write(pipe_1[1], &file_len, sizeof(size_t)); write(pipe_1[1], file_name, file_len); } else { write(pipe_2[1], &file_len, sizeof(size_t)); write(pipe_2[1], file_name, file_len); } } // 关闭文件名发送后的管道写端,避免子进程阻塞 close(pipe_1[1]); close(pipe_2[1]); // 读取输入字符串并分发 while(1){ int k = 0, str_len = 0; // 每次循环重置长度变量 ssize_t read_len = read(STDIN_FILENO, str, BUFF); if (read_len <= 0) break; // 空行退出循环 if (str[0] == '\n') break; // 计算有效字符串长度(到换行符为止) while(k < read_len && str[k] != '\n'){ k++; str_len++; } // 修正管道映射:长字符串发子进程2,短字符串发子进程1 if (str_len > 10){ write(pipe_2[1], &str_len, sizeof(int)); write(pipe_2[1], str, str_len); } else { write(pipe_1[1], &str_len, sizeof(int)); write(pipe_1[1], str, str_len); } } // 关闭所有管道写端 close(pipe_1[1]); close(pipe_2[1]); // 等待子进程完成任务 wait(NULL); wait(NULL); } return 0; }
核心修复要点
- 修正管道映射:将长字符串写入
pipe_2(子进程2),短字符串写入pipe_1(子进程1),匹配需求逻辑。 - 清理文件名无效字符:用
strcspn截断输入中的换行符,确保文件名合法。 - 统一类型匹配:子进程用
size_t接收文件名长度,与父进程发送类型一致,避免读取错误。 - 重置循环变量:每次读取字符串时重置
k和str_len,避免长度计算累积错误。 - 正确管理管道:父进程发送完文件名后立即关闭对应管道写端,子进程关闭未使用的写端,防止阻塞。
- 支持多输入处理:子进程改为循环读取字符串,可处理任意数量的输入内容。
- 等待子进程结束:父进程添加
wait调用,确保子进程完成写入后再退出。
内容的提问来源于stack exchange,提问作者Determined Girl
相关产品推荐
相关产品推荐

