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

C语言中mmap写入FUSE inode结构体返回MAP_FAILED问题求助

问题

我正在开发FUSE文件系统驱动以创建挂载点,为实现数据持久化,计划将inode保存至二进制文件。以下是我的inode结构体定义:

struct s_fuseInode {
        int                     id; /* Identificador del inodo */
        char                    name[MAX_NAME]; /* Nombre del archivo */
        char                    path[MAX_PATH]; /* Ruta del archivo */
        int                     size; /* Tamaño del archivo */
        int                     block_count; /* Número de bloques ocupados por el archivo */
        int                     block[MAX_BLOCKS]; /* Bloques ocupados por el archivo */
        struct s_fuseInode      parent; /* Inodo padre */
        int                     parent_id; /* Identificador del inodo padre */
        struct s_fuseInode      *children; /* Inodos hijos */
        int                     children_count; /* Número de inodos hijos */
        int                     is_dir; /* Indica si el inodo es un directorio */
        char                    data; /* Datos del archivo */
        time_t                  creation_time; /* Fecha de creación del archivo */
        time_t                  modification_time; /* Fecha de modificación del archivo */
        time_t                  access_time; /* Fecha de acceso al archivo */
        int                     links; /* Número de enlaces al archivo */
        mode_t                  mode; /* Permisos del archivo */
        uid_t                   uid; /* Identificador de usuario */
        gid_t                   gid; /* Identificador de grupo */
};

用于保存inode的函数如下:

static int      save_inode(struct s_fuseInode *inode)
{
        int fd = open("virtual_disk.bin", O_RDWR, S_IRUSR | S_IWUSR); // Abrimos el archivo de disco virtual
        if (fd == -1) // Si no se ha podido abrir
        {
                fprintf(stderr, "save_inode: Error al abrir el archivo de disco virtual\n");
                return (1);
        }
        off_t offset = lseek(fd, 0, SEEK_END); // Nos situamos al final del archivo
        fprintf(stderr, "offset %ld\n", offset);
        if (offset == -1) // Si no se ha podido situar
        {
                fprintf(stderr, "save_inode: Error al situarse al final del archivo\n");
                return (1);
        }
        ftruncate(fd, offset + sizeof(struct s_fuseInode)); // Truncamos el archivo
        fprintf(stderr, "size %ld\n", offset + sizeof(struct s_fuseInode));

        struct s_fuseInode *map = mmap(0, sizeof(struct s_fuseInode), PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset); // Mapeamos el archivo
        if (map == MAP_FAILED) // Si no se ha podido mapear
        {
                close(fd);
                fprintf(stderr, "save_inode: Error al mapear el archivo\n");
                return (1);
        }
        memcpy(map, inode, sizeof(struct s_fuseInode)); // Copiamos el inodo en el archivo
        if (munmap(map, sizeof(struct s_fuseInode)) == -1) // Si no se ha podido desmapear
        {
                close(fd);
                fprintf(stderr, "save_inode: Error al desmapear el archivo\n");
                return (1);
        }
        close(fd); // Cerramos el archivo
        return (0);
}

调用该函数时,mmap返回MAP_FAILED错误。已知该inode结构体大小为256字节,系统PAGE_SIZE为4096字节,我怀疑是页对齐问题,但作为首次使用mmap的开发者,不知如何解决。尝试添加char padding[3840]将结构体大小补至4096字节后可正常工作,但这样每个inode会占用4KB空间,希望能优化存储空间。

解决方案

核心问题原因

mmap要求映射的文件偏移量必须是系统页大小的整数倍,你的inode大小是256字节,多次写入后offset会是256、512、768...这些值都不是4096的倍数,所以mmap调用失败。另外注意:你的结构体里包含struct s_fuseInode parent成员,这会导致结构体无限递归(自身嵌套),实际大小远不止256字节,必须改成指针或者仅用parent_id关联父节点。

优化解决方法

不需要给结构体加大量padding,换用以下两种更高效的方式:

方式1:直接用write代替mmap

既然只是写入一个256字节的结构体,完全没必要用mmap,直接调用write函数写入更简单高效,还能避免页对齐问题:

static int save_inode(struct s_fuseInode *inode)
{
        int fd = open("virtual_disk.bin", O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
        if (fd == -1)
        {
                fprintf(stderr, "save_inode: Error al abrir el archivo de disco virtual\n");
                return 1;
        }

        ssize_t written = write(fd, inode, sizeof(struct s_fuseInode));
        if (written != sizeof(struct s_fuseInode))
        {
                fprintf(stderr, "save_inode: Error al escribir el inodo\n");
                close(fd);
                return 1;
        }

        close(fd);
        return 0;
}

注:用O_APPEND模式打开文件,会自动把写入位置定位到文件末尾,省去手动lseek的步骤。

方式2:如果坚持用mmap,映射完整页并定位写入

如果必须使用mmap,需要映射完整的一页,然后计算inode在页内的偏移位置进行写入:

#include <unistd.h> // 用于获取PAGE_SIZE

static int save_inode(struct s_fuseInode *inode)
{
        int fd = open("virtual_disk.bin", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
        if (fd == -1)
        {
                fprintf(stderr, "save_inode: Error al abrir el archivo de disco virtual\n");
                return 1;
        }

        off_t file_size = lseek(fd, 0, SEEK_END);
        if (file_size == -1)
        {
                fprintf(stderr, "save_inode: Error al obtener tamaño del archivo\n");
                close(fd);
                return 1;
        }

        // 计算下一个inode的起始偏移
        off_t inode_offset = file_size;
        // 计算需要映射的页起始偏移(对齐到页大小)
        off_t map_offset = inode_offset & ~(PAGE_SIZE - 1);
        // 计算映射的长度:如果inode跨页,需要映射两页,否则一页
        size_t map_len = ((inode_offset + sizeof(struct s_fuseInode) - map_offset) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);

        // 确保文件大小足够容纳映射的区域
        if (ftruncate(fd, map_offset + map_len) == -1)
        {
                fprintf(stderr, "save_inode: Error al truncar el archivo\n");
                close(fd);
                return 1;
        }

        struct s_fuseInode *map = mmap(NULL, map_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, map_offset);
        if (map == MAP_FAILED)
        {
                fprintf(stderr, "save_inode: Error al mapear el archivo\n");
                close(fd);
                return 1;
        }

        // 计算inode在映射区域内的相对偏移
        size_t inode_rel_offset = inode_offset - map_offset;
        memcpy((char*)map + inode_rel_offset, inode, sizeof(struct s_fuseInode));

        if (munmap(map, map_len) == -1)
        {
                fprintf(stderr, "save_inode: Error al desmapear el archivo\n");
                close(fd);
                return 1;
        }

        close(fd);
        return 0;
}

这种方式既满足mmap的页对齐要求,又不会浪费空间,每个inode只占用实际的256字节。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 09:10:59