【问题标题】:How to share existing memory?如何共享现有内存?
【发布时间】:2016-05-08 20:27:39
【问题描述】:

我想写一些函数void* share(void*, int),它应该设置共享内存来共享指针处的数据。

我的第一次尝试看起来像(没有检查等):

void* share(void *toBeShared, int size) {
    int fd = shm_open(SHM_NAME, O_CREAT | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR);
    ftruncate(fd, size);
    return mmap(toBeShared, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
}

但这似乎并不如我所愿。第二次尝试是这样的:

void* share(void *toBeShared, int size) {
    void *mem = NULL;
    int fd = shm_open(SHM_NAME, O_CREAT | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR);
    ftruncate(fd, size);
    mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)
    memcpy(mem, toBeShared, size);
    return mem;
}

这确实有效,但我需要复制整个数据,我想避免这样做。

因此我的问题是:有没有办法共享已经分配的内存(如果可能的话,不必复制太多),如果可以,怎么做?

提前致谢。

PS:我见过更多这样的问题(例如herehere),但那里没有给出答案。


编辑:

我想如何使用它:

typedef struct {
    char *name;
    int status;
} MyTask;

int main(int argc, char** argv) {
    MyTask* taskList = NULL, sharedTaskList = NULL;
    int length = 0;
    ...
    readFile(&taskList, &length, ...);
    sharedTaskList = share(taskList, length * sizeof(MyTask));
    // or maybe even better: without needing to assign it to new variable
    for(i = 0; i < NR_WORKERS; i++) {
        switch(pid = fork()) {
            //etc...
        }
    }
    ...
    return 0;
}

【问题讨论】:

  • 答案取决于现有的映射是什么。是文件映射吗?是匿名的吗?试图回答每一个可能的情况将是非常乏味的。这将有助于了解您的实际应用。它也将是特定于操作系统的,因为并非所有应该工作的东西都在每个操作系统上实现。 (例如,由于某种原因,Linux 不允许您映射 /proc/*/mem。)
  • @DavidSchwartz 它实际上只是一个带有结构的简单数组。我试图将我的函数写得尽可能通用......
  • @MrTsjolder 这个数组是怎么分配的?为什么一开始没有在共享内存中分配它?它是否包含任何指针?
  • 您无法做到这一点,因为您无法满足的边界条件太多。

标签: c posix shared-memory


【解决方案1】:

如何共享现有的内存?

不要共享现有的内存。获取一些(少量)“新鲜”共享内存并在以后使用(即填充或读取)它。

假设您使用的是 Linux,请阅读 shm_overview(7)

我猜你的某些功能可能会失败。您应该在每次调用时测试失败,例如

int fd = shm_open(SHM_NAME, O_CREAT | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR);
if (fd<0) {perror("shm_open"); exit(EXIT_FAILURE);};

等等。或许也可以使用strace(1)

有没有办法共享已经分配的内存

简短的回答,不!(或者不容易,而且不是以便携的方式)。您通常会做相反的事情:获取一些已知大小的共享段,并使用一些指向它的指针。 (同一个共享段在不同进程中可能有不同的虚拟地址,例如因为ASLR)。

您可以在一些已经使用的虚拟地址空间子段上使用mmap(2)MAP_FIXED(这将覆盖并替换新映射,而不是共享一个现有 em> 映射!),但我建议避免这种情况。请注意,虚拟地址空间是在多个页面中管理的,因此无法共享一些不是页面对齐的数据。所以你的share 函数是不可能,除非toBeSharedsize 都是page-aligned。您可以考虑特定于 Linux 的 mremap(2)

换句话说,您的应用程序应该首先分配一些共享内存,然后在获得的共享段中放入/使用一些数据,而不是尝试共享一些现有的未共享虚拟内存范围。因此,您可能想要编写一些 void* get_my_shared_memory(); 代码(假设大小是编译时间常数,并且您在每个进程中调用该函数一次,其生成的虚拟地址通常会因进程而异)

在实践中,内存是一种有限的资源,而共享内存是一种稀缺且非常有限的资源。在大多数系统上,您只能共享几十兆字节...因此共享任意大量内存是不合理的。

也许您的整个应用程序可能只使用一些服务器,例如一些数据库服务器à la PostGreSQL,通过向该服务器发出请求(并使用 DBMS 的 ACID 属性)来共享信息。或者,您可以将其组织为与从属进程在管道或套接字或 fifos 上交换消息(例如要处理的 URL)的监视进程。但我们不知道您在编写什么样的应用程序。

顺便说一句,共享内存是不够的。 您需要同步您的流程

【讨论】:

  • 您不能使用 MAP_FIXED 共享现有映射。如果您使用 MAP_FIXED,现有的映射将被丢弃。我看你根本没有回答他的问题。 (他为什么要阅读 shm_overview?你认为这与他的问题有关吗?)
猜你喜欢
  • 2014-03-17
  • 2021-04-23
  • 2014-04-11
  • 2014-03-22
  • 2010-10-01
  • 2014-10-08
  • 2014-04-20
  • 1970-01-01
  • 2021-06-15
相关资源
最近更新 更多