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

C语言映射共享内存时偏移量非零触发Invalid argument错误

mmap映射共享内存非零偏移量触发Invalid argument错误的原因与解决

问题核心

你遇到的Invalid argument错误,根源在于mmap的offset参数必须是系统内存页大小的整数倍,而offsetof(Big, lil)得到的偏移量(示例中为8)通常不满足这个要求(多数系统的内存页大小是4096字节)。

原理说明

mmap在映射文件/共享内存时,偏移量必须对齐到内存页边界——这是内核内存管理的硬性要求,因为内核是以页为单位处理内存映射的。如果传入的offset不是页大小的整数倍,内核会直接返回EINVAL错误。

解决方法

方法1:映射整个结构体,通过指针访问目标成员

这是最简单且推荐的方式,直接映射整个Big结构体,然后通过指针访问内部的lil成员,无需单独映射偏移量:

#include<stdio.h>
#include<unistd.h>
#include<sys/mman.h>
#include<stddef.h>
#include<fcntl.h>

typedef struct test
{
    int moo;
    char hello;
}Lil;

typedef struct bigstruct
{
    int a;
    char cow;
    Lil lil;
}Big;

int main()
{
    int fd = shm_open("/moomor", O_CREAT | O_RDWR, 0666);
    if(fd == -1)
    {
        perror("shmopen");
        return 1;
    }
    ftruncate(fd, sizeof(Big));
    Big * big = (Big *)mmap(NULL, sizeof(Big), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if(big == MAP_FAILED)
    {
        perror("mmap");
        return 1;
    }
    big->lil.hello = 'h';
    printf("hello: %c\n", big->lil.hello);

    // 直接通过big指针访问lil成员,无需重新映射
    printf("HH: %c\n", big->lil.hello);

    munmap(big, sizeof(Big));
    shm_unlink("/moomor");
    close(fd);
    return 0;
}

方法2:将偏移量对齐到内存页大小

如果必须单独映射Lil部分,需要先获取系统页大小,然后把偏移量向上对齐到页大小的倍数:

#include<stdio.h>
#include<unistd.h>
#include<sys/mman.h>
#include<stddef.h>
#include<fcntl.h>
#include<stdlib.h>

typedef struct test
{
    int moo;
    char hello;
}Lil;

typedef struct bigstruct
{
    int a;
    char cow;
    Lil lil;
}Big;

// 向上对齐到页大小的宏
#define ROUND_UP(x, align) (((x) + (align) - 1) & ~((align) - 1))

int main()
{
    int fd = shm_open("/moomor", O_CREAT | O_RDWR, 0666);
    if(fd == -1)
    {
        perror("shmopen");
        return 1;
    }
    
    long page_size = sysconf(_SC_PAGESIZE);
    // 调整共享内存大小,确保能容纳对齐后的映射
    size_t shm_size = ROUND_UP(offsetof(Big, lil) + sizeof(Lil), page_size);
    ftruncate(fd, shm_size);

    Big * big = (Big *)mmap(NULL, sizeof(Big), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if(big == MAP_FAILED)
    {
        perror("mmap");
        return 1;
    }
    big->lil.hello = 'h';
    printf("hello: %c\n", big->lil.hello);
    munmap(big, sizeof(Big));

    int raw_offset = offsetof(Big, lil);
    // 对齐偏移量到页大小
    off_t aligned_offset = ROUND_UP(raw_offset, page_size);
    printf("Raw offset: %d, Aligned offset: %ld\n", raw_offset, aligned_offset);

    // 计算映射后地址的偏移修正值
    char *base_addr = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, aligned_offset);
    if(base_addr == MAP_FAILED)
    {
        perror("mmap");
        goto cleanup;
    }
    Lil *lily = (Lil *)(base_addr + (raw_offset - aligned_offset));
    printf("HH: %c\n", lily->hello);
    munmap(base_addr, page_size);

cleanup:
    shm_unlink("/moomor");
    close(fd);
    return 0;
}

这种方式需要额外处理地址偏移,复杂度较高,仅在必须单独映射部分内存时使用。

注意事项

  • 可以通过getconf PAGE_SIZE命令查看系统的内存页大小;
  • 共享内存的大小必须足够覆盖对齐后的映射范围,否则会触发其他错误;
  • 多进程共享时,所有进程必须使用相同的对齐规则,才能正确访问目标结构体成员。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 01:40:01