【发布时间】:2020-11-14 07:43:29
【问题描述】:
我有 2 个或更多进程访问共享内存。 我想在每个进程中创建一个全局变量,然后使用带有 MAP_FIXED 标志的 mmap API 将该变量的地址映射到共享内存。 因此,在读取/写入此共享内存时,我只需要访问全局变量(与我们在线程之间共享全局变量的方式相同,但这里我想在进程之间共享全局变量)。
我在每个进程中定义全局变量如下:
typedef struct // This struct define the shared memory area
{
int data1;
int data2;
// ...
} SharedMemory;
// the following attribute (supported by GCC) make the start address of the variable aligned to 4KB (PAGE_SIZE)
__attribute__((aligned(4096))) SharedMemory gstSharedMemory; // shared global variable
int giOtherVar = 10; // Another normal global variable
然后使用 mmap 将共享内存映射到这个全局变量:
void* lpShmAddr = mmap(&gstSharedMemory,
sizeof(gstSharedMemory),
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_FIXED,
iFd, // File descriptor to the shared memory
0);
但是,如果sizeof(gstSharedMemory) 不是 PAGE_SIZE (4kb) 的倍数,并且由于操作系统将始终将映射大小四舍五入为页面大小的倍数,则四舍五入区域中的所有字节都将初始化为 0。
如果其他全局变量(例如:giOtherVar)的地址在这个四舍五入的区域内,可能会导致其数据为零。
为了克服这种情况,我使用一个字节数组来备份四舍五入的区域并恢复它,如下所示:
unsigned char byBkupShm[PAGE_SIZE] = { 0 } ;
memcpy(&gbyBkupShm[0],
((unsigned char*)&gstSharedMemory+ sizeof(gstSharedMemory)),
PAGE_SIZE - (sizeof(gstSharedMemory)% PAGE_SIZE));
void* lpShmAddr = mmap(&gstSharedMemory,
sizeof(gstSharedMemory),
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_FIXED,
iFd, // File descriptor to the shared memory
0);
memcpy( ((unsigned char*)&gstSharedMemory+ sizeof(gstSharedMemory)),
&byBkupShm[0],
PAGE_SIZE - (sizeof(gstSharedMemory)% PAGE_SIZE));
最后,我像这样访问共享内存:
// Write to shared memory:
gstSharedMemory.data1 = 5;
// Read from shared memory;
printf("%d", gstSharedMemory.data1);
我的问题是:这个实现有什么潜在的问题吗?
已编辑: 感谢@None 和他的想法,我定义了一个如下宏来使我的结构对齐并向上舍入到 PAGE_SIZE,但同时,如果我需要,仍然提供结构的实际大小:
#define PAGE_SIZE (4 * 1024) // Page Size: 4KB
#define SHM_REG __attribute__((aligned(PAGE_SIZE))) // Aligned to 4KB boundary
#define DEFINE_SHM( structName_, shmSizeVar_, structContent_) \
typedef struct SHM_REG structContent_ structName_; \
int shmSizeVar_ = sizeof(struct structContent_);
// Using
DEFINE_SHM(
MySharedMemory, // Struct Name of shared memory
giSizeOfMySharedMemory, // Global Variable
{
int a;
int b;
char c;
}
);
printf("Rounded Size: %d\n", sizeof(MySharedMemory)); // = 4096
printf("Acutal Size: %d\n", giSizeOfMySharedMemory); // = 12
【问题讨论】:
-
为什么需要使用
MAP_FIXED?你认为这会给你带来什么好处?为什么不让操作系统决定使用哪个地址? -
我的项目是将源代码从vxWorks(任务通信)迁移到Linux(进程通信)。任务之间的全局变量成为进程之间的共享内存。所以我想用MAP_FIXED来重用旧的源代码(访问全局变量)
-
为什么不将 gstSharedMemory 对齐为页面大小的倍数?将对齐的属性附加到结构定义,它应该可以工作。如果你愿意,你也可以将“共享内存”放在一个额外的部分中。
-
你确定为什么 vxWorks版本使用
MAP_FIXED了吗?如果这种推理甚至适用于 Linux?我强烈怀疑您正在尝试做的事情不会复制在 vxWorks 中使用MAP_FIXED的可能原因,这意味着它无法在 Linux 上运行。 -
更清楚一点:您使用的对齐属性错误。应该是
typedef struct __attribute__((aligned(4096)))。这样就不需要备份阵列了。我没有使用 vxWorks 作品的经验,所以我无法说出任何关于意外行为的信息,但它应该可以工作 (TM)。
标签: c linux process shared-memory mmap