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

使用mmap实现父子进程文件处理时的进程通信与数据损坏问题

问题描述

我编写了一个基于mmap内存映射的程序,需求如下:

  • 将文件中的大小写字母互换
  • 数字替换为***
  • 修改后的内容写回原文件
  • 父进程负责字母大小写转换,子进程负责数字替换,分前后两半依次处理文件

目前前半部分处理正常,但后半部分数据损坏。我尝试将buffer改为char*类型,以及代码中注释的多种方法,均未解决问题。

代码实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    // 检查是否提供了文件名参数
    if (argc < 2) {
    printf("Error: se debe proporcionar el nombre del archivo como argumento.\n");
    exit(EXIT_FAILURE);
    }
    // 以读写模式打开文件
    int file = open(argv[1], O_RDWR);
    if (file < 0) {
        printf("Error: no se puede abrir el archivo %s.\n", argv[1]);
        exit(EXIT_FAILURE);
    }
    
    // 获取文件状态
    struct stat estado;
    if (fstat(file, &estado) < 0) {
        printf("Error: no se puede obtener el estado del archivo %s.\n", argv[1]);
        exit(EXIT_FAILURE);// 接收信号时执行的函数
    }
    // 检查是否为普通文件
    if (!S_ISREG(estado.st_mode)) {
        printf("Error: %s no es un archivo regular.\n", argv[1]);
        exit(EXIT_FAILURE);
    }
    
    // 将文件映射到内存
    char *map = (char*) mmap(NULL, estado.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, file, 0);
    if (map == MAP_FAILED) {
        printf("Error: no se puede mapear el archivo %s en memoria.\n", argv[1]);
        exit(EXIT_FAILURE);
    }
    
    printf("\n---------------PROCESO DE LECTURA EN CURSO---------------\n");
    char texto ='\0';
    int lr = 0;
    for (int i = 0; i < estado.st_size; i++) {
        // 开始读取文件
        if ((lr = read(file, &texto, 1)) < 0) {
            printf("\nError intentando leer el archivo\n");
            exit(EXIT_FAILURE);
        }
        // 检查文件内容是否读取正确
        printf("%c", texto);
    }
    
    // 创建子进程
    pid_t pid = fork();
    if (pid < 0) {
        printf("Error: no se puede crear el proceso hijo.\n");
        exit(EXIT_FAILURE);
    }

    if (pid == 0) {
        // 子进程
        // 等待父进程处理完文件的一半
        raise(SIGSTOP); // 暂停进程等待父进程信号
        int mitadArchivo = estado.st_size / 2;
        // 计算替换数字后的文件最终大小
        int tamanioFinal = estado.st_size;
        for (int i = 0; i < estado.st_size; i++) {
            if (isdigit(map[i])) {
                tamanioFinal += 2;
            }
        }

        // 调整文件大小以匹配最终大小
        if (ftruncate(file, tamanioFinal) < 0) {
            printf("Error: no se puede redimensionar el archivo %s.\n", argv[1]);
            exit(EXIT_FAILURE);
        }
                // 处理文件的后半部分
        char buffer[/*mitadArchivo*/estado.st_size];
        int j = 0, k = 0;
        printf("\n---------IMPRIMIR DESDE EL HIJO---------\n");
        for (int i = 0; i < estado.st_size; i++) {
            if (isdigit(map[i])) {
                // 如果是数字,替换为三个星号
                buffer[j++] = '*';
                buffer[j++] = '*';
                buffer[j++] = '*';
            } else {
                // 非数字则直接复制到buffer
                buffer[j++] = map[i];
            }           
            printf("%c", buffer[i]);
            if (i == mitadArchivo) { // 到达文件中点
                    memcpy(map, buffer, /*2*(estado.st_size + j)*/ mitadArchivo + j); // 写入修改后的前半部分
                    raise(SIGSTOP); // 暂停进程等待父进程信号
                    printf("\nSegunda mitad del archivo: \n");
                    if (isdigit(map[i])) {
                        // 如果是数字,替换为三个星号
                        buffer[k++] = '*';
                        buffer[k++] = '*';
                        buffer[k++] = '*';
                    } else {
                        // 非数字则直接复制到buffer
                        buffer[k++] = map[i];
                    }  
                    memcpy(buffer, map + (mitadArchivo + j), /*2*(estado.st_size + j)*/  tamanioFinal - (mitadArchivo + j)); // 读取待处理的后半部分
                }
        }
        printf("\n");
    
        // 将buffer内容复制到文件
        //memcpy(map /*+ mitadArchivo*/, buffer, j);
    
        // 修改文件大小以适应更改
        //ftruncate(file, 2*(estado.st_size + j) /*- mitadArchivo*/);
    
        // 子进程退出
        exit(EXIT_SUCCESS);
    } else {
        // 父进程
        // 处理文件的前半部分
        char buffer[estado.st_size];
        int mitadArchivo = estado.st_size / 2;
        int j = 0;
        for (int i = 0; i < estado.st_size /*/ 2*/; i++) {
            if (islower(map[i])) {
                // 小写字母转大写
                buffer[j++] = toupper(map[i]);
            } else if (isupper(map[i])) {
                // 大写字母转小写
                buffer[j++] = tolower(map[i]);
            } else {
                // 非字母直接复制到buffer
                buffer[j++] = map[i];
            }
            if (i == estado.st_size / 2){
                sleep(1);
                // 将buffer内容复制到文件
                memcpy(map, buffer, j);
                // 通知子进程继续执行
                kill(pid, SIGCONT);
            }
        }
            // 将buffer内容复制到文件
            memcpy(buffer, map, j);
            sleep(1);
            // 通知子进程继续执行
            kill(pid, SIGCONT);
        
            // 等待子进程结束
            waitpid(pid, NULL, 0);
            
            printf("\n---------IMPRIMIR DESDE LA SALIDA DESDE MEMORIA---------\n");
            for (int i = 0; i < estado.st_size; i++)
                    printf("%c", map[i]);
            printf("\n");
            
            // 父进程退出
            exit(EXIT_SUCCESS);
        }

        
    return 0;
}
运行输出
---------------PROCESO DE LECTURA EN CURSO---------------
esta 5 es 6 la
prueba 89 para 531 ver si
35 el programa 7 funciona

---------IMPRIMIR DESDE EL HIJO---------
ESTA *** ES *** LA
PRUEBA ****** P
Segunda mitad del archivo: 
ARA ********* ARA ********* ���c�

---------IMPRIMIR DESDE LA SALIDA DESDE MEMORIA---------
ESTA *** ES *** LA
PRUEBA ****** PARA ********* ���c���of�/����

内容的提问来源于stack exchange,提问作者Ant

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 06:35:26