You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 03:50:31