使用mmap with hint address是一个全局变量的地址并且MAP_FIXED
Using mmap with hint address is the address of a global variable and MAP_FIXED
我有 2 个或更多进程访问共享内存。
我想在每个进程中创建一个全局变量,然后使用 mmap API 和 MAP_FIXED 将这个变量的地址映射到共享内存 标志。
因此,当读取/写入此共享内存时,我只需要访问全局变量(与我们在线程之间共享全局变量的方式相同,但在这里我想在进程之间共享全局变量)。
我在每个进程中定义全局变量如下:
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),并且由于 OS 总是将映射大小四舍五入为页面大小的倍数,所有字节在上舍入区域中被初始化为 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
是的,潜在的问题是 giOtherVar
现在也被共享了,因为整个页面都被共享了。
正常的做法是不使用MAP_FIXED
,让mmap
选择一个位置,并存储指针。你说你不能这样做,因为这将是一个巨大的代码更改。
可能有一种方法可以使用 linker script 强制 gstSharedMemory
单独出现在页面上,但链接器脚本很棘手。
您可以向 SharedMemory
添加 4096 字节的填充,因此它总是大于 4096 字节,然后不共享最后一页(可能与其他全局变量重叠)。
您可以在 gstSharedMemory
之后添加一个未使用的 4096 字节数组,希望 编译器将它放在 gstSharedMemory
之后。
您可以使 next 变量也对齐 4096 字节,希望编译器不会决定将其他变量放在间隙中。
... 或你可以只使用指针设计,然后#define gstSharedMemory (*gpstSharedMemory)
这样你就没有更改所有代码。
确保共享内存结构与页面大小对齐且大小为页面大小的倍数:
#include <stdlib.h>
#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
#endif
typedef struct __attribute__((aligned (PAGE_SIZE))) {
/*
* All shared memory members
*/
} SharedMemory;
在运行时,在映射共享内存之前,先验证一下:
SharedMemory blob;
if (PAGE_SIZE != sysconf(_SC_PAGESIZE)) {
ABORT("program compiled for a different page size");
} else
if (sizeof blob % PAGE_SIZE) {
ABORT("blob is not sized properly");
} else
if ((uintptr_t)(&blob) % PAGE_SIZE) {
ABORT("blob is not aligned properly");
} else
if (MAP_FAILED == mmap(...)) {
ABORT("could not map shared memory over blob");
}
虽然这是一个 hack,但至少在 Linux.
中这样是安全的
我有 2 个或更多进程访问共享内存。 我想在每个进程中创建一个全局变量,然后使用 mmap API 和 MAP_FIXED 将这个变量的地址映射到共享内存 标志。 因此,当读取/写入此共享内存时,我只需要访问全局变量(与我们在线程之间共享全局变量的方式相同,但在这里我想在进程之间共享全局变量)。
我在每个进程中定义全局变量如下:
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),并且由于 OS 总是将映射大小四舍五入为页面大小的倍数,所有字节在上舍入区域中被初始化为 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
是的,潜在的问题是 giOtherVar
现在也被共享了,因为整个页面都被共享了。
正常的做法是不使用MAP_FIXED
,让mmap
选择一个位置,并存储指针。你说你不能这样做,因为这将是一个巨大的代码更改。
可能有一种方法可以使用 linker script 强制 gstSharedMemory
单独出现在页面上,但链接器脚本很棘手。
您可以向 SharedMemory
添加 4096 字节的填充,因此它总是大于 4096 字节,然后不共享最后一页(可能与其他全局变量重叠)。
您可以在 gstSharedMemory
之后添加一个未使用的 4096 字节数组,希望 编译器将它放在 gstSharedMemory
之后。
您可以使 next 变量也对齐 4096 字节,希望编译器不会决定将其他变量放在间隙中。
... 或你可以只使用指针设计,然后#define gstSharedMemory (*gpstSharedMemory)
这样你就没有更改所有代码。
确保共享内存结构与页面大小对齐且大小为页面大小的倍数:
#include <stdlib.h>
#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
#endif
typedef struct __attribute__((aligned (PAGE_SIZE))) {
/*
* All shared memory members
*/
} SharedMemory;
在运行时,在映射共享内存之前,先验证一下:
SharedMemory blob;
if (PAGE_SIZE != sysconf(_SC_PAGESIZE)) {
ABORT("program compiled for a different page size");
} else
if (sizeof blob % PAGE_SIZE) {
ABORT("blob is not sized properly");
} else
if ((uintptr_t)(&blob) % PAGE_SIZE) {
ABORT("blob is not aligned properly");
} else
if (MAP_FAILED == mmap(...)) {
ABORT("could not map shared memory over blob");
}
虽然这是一个 hack,但至少在 Linux.
中这样是安全的