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
相关产品推荐
相关产品推荐

