Linux Ubuntu共享内存shmat映射失败问题排查
共享内存shmat调用失败排查问题
问题描述
在Linux Ubuntu系统中实现基于共享内存的进程间通信(IPC),逻辑为:先尝试获取已存在的共享内存,若不存在则创建,调整至目标大小后映射使用。但每次运行时shmat调用均失败,报错“Fatal error mapping shared memory”,相关代码及运行错误信息如下:
错误代码
#include <iostream> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/sem.h> #include <sys/stat.h> #include <fcntl.h> #include <cstring> #define SHARED_MEMORY_KEY "/mysharedmemory" typedef struct { int code; char buffer[500]; } shared_data_t; shared_data_t *shared_mem_ptr; void init_shared_memory() { // Open shared memory int shmid = shm_open(SHARED_MEMORY_KEY, O_CREAT | O_RDWR, 0644); std::cout << "OPEN: " << shmid << std::endl; if (shmid < 0) { std::cout << "Not found... creating..." << std::endl; key_t key = ftok(SHARED_MEMORY_KEY, 1); // Does not exist - create it shmid = shmget(key, sizeof(shared_data_t), 0644 | IPC_CREAT); if (shmid == -1) { std::cout << "Fatal error creating shared memory." << std::endl; return; } } int rc = ftruncate(shmid, sizeof(shared_data_t)); if (rc == -1) { std::cout << "Fatal error resizing shared memory." << std::endl; return; } // Attach data struct to the segment to get a pointer to it. shared_mem_ptr = (shared_data_t *) shmat(shmid, NULL, 0); if (shared_mem_ptr == (void *)-1) { std::cout << "Fatal error mapping shared memory." << std::endl; return; } }
运行错误信息
Fatal error mapping shared memory
错误原因分析
代码混用了两种完全不兼容的共享内存API:
shm_open属于POSIX共享内存接口,返回的是文件描述符,后续需用mmap映射内存,而非shmatshmget/shmat属于System V共享内存接口,依赖key_t标识,和POSIX接口完全不互通
当shm_open成功获取文件描述符后,直接传给shmat必然失败——shmat需要的是System V共享内存ID(shmget返回值),而非POSIX共享内存的文件描述符。此外,ftok要求传入已存在的文件路径,若/mysharedmemory不存在,ftok会返回错误,导致后续shmget也失败。
修正方案
选择其中一种API实现即可,以下提供两种修正版本:
方案1:使用POSIX共享内存(推荐)
#include <iostream> #include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> #include <cstring> #include <unistd.h> #include <errno.h> #define SHARED_MEMORY_KEY "/mysharedmemory" typedef struct { int code; char buffer[500]; } shared_data_t; shared_data_t *shared_mem_ptr; void init_shared_memory() { // 打开或创建POSIX共享内存 int fd = shm_open(SHARED_MEMORY_KEY, O_CREAT | O_RDWR, 0644); if (fd < 0) { perror("shm_open failed"); return; } // 调整共享内存大小 if (ftruncate(fd, sizeof(shared_data_t)) == -1) { perror("ftruncate failed"); close(fd); return; } // 映射共享内存到进程地址空间 shared_mem_ptr = (shared_data_t*)mmap(NULL, sizeof(shared_data_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (shared_mem_ptr == MAP_FAILED) { perror("mmap failed"); close(fd); return; } // 映射完成后可关闭文件描述符(内存依然有效) close(fd); }
方案2:使用System V共享内存
#include <iostream> #include <sys/ipc.h> #include <sys/shm.h> #include <cstring> #include <unistd.h> #include <errno.h> #define SHARED_MEMORY_FILE "/tmp/mysharedmemory" #define PROJECT_ID 1 typedef struct { int code; char buffer[500]; } shared_data_t; shared_data_t *shared_mem_ptr; void init_shared_memory() { // 确保文件存在,用于生成key int fd = open(SHARED_MEMORY_FILE, O_CREAT | O_RDWR, 0644); if (fd < 0) { perror("open failed"); return; } close(fd); // 生成System V共享内存的key key_t key = ftok(SHARED_MEMORY_FILE, PROJECT_ID); if (key == -1) { perror("ftok failed"); return; } // 获取或创建System V共享内存 int shmid = shmget(key, sizeof(shared_data_t), 0644 | IPC_CREAT); if (shmid == -1) { perror("shmget failed"); return; } // 映射共享内存到进程地址空间 shared_mem_ptr = (shared_data_t*)shmat(shmid, NULL, 0); if (shared_mem_ptr == (void*)-1) { perror("shmat failed"); return; } }
额外提示
- POSIX共享内存接口更现代,用文件系统路径标识,管理更直观,推荐使用
- 调试时用
perror打印具体错误原因,比自定义信息更利于排查问题 - 使用完共享内存后,POSIX接口需调用
munmap解除映射、shm_unlink删除共享内存;System V接口需调用shmdt解除映射、shmctl删除共享内存
内容的提问来源于stack exchange,提问作者Mendes
相关产品推荐
相关产品推荐

