【问题标题】:How Do I Store and Retrieve a Struct into a Shared Memory Area in C如何在 C 中将结构存储和检索到共享内存区域
【发布时间】:2011-05-14 21:19:32
【问题描述】:

对于 uni 分配,我需要创建一个最多包含 10 个文件名的循环列表,然后将它们存储在共享内存区域中,以便 2 个子进程可以读取/写入列表(使用信号量来控制访问)。麻烦的是,我是一个完全 C 的新手,我感到失落和绝望,因为这完全超出了我的理解范围。我需要一些帮助来“填补我的知识空白”。

现在,我只是一次只关注一个问题,而目前,我只是试图将我的循环列表放入共享内存区域。

到目前为止我有:

typedef struct FILE
{
   struct FILE *f_link;  /* forward link for linked list */
   char name[255];       /* name of the file */

} FILE_entry;

作为我的结构,它将保存对下一个文件 (f_link) 的引用。这样我就可以调用 ->f_link 来获取列表中的下一项,并且第 10 个元素将简单地将其 f_link 指向第一个。我这样做的原因是我可以在没有迭代器的情况下简单地遍历列表(并且永远不必像使用数组一样检查列表的结尾)。

我也知道我需要使用shmget 来获取内存区域,我理解它,我传递shmget 一个键、一个大小和一个标志(我没有得到),它返回一个标识符int 的类型。

所以我的问题是 2 倍。如何将我的链表存储到共享内存区域 - 以及如何从共享内存区域访问它?

【问题讨论】:

    标签: c unix shared-memory


    【解决方案1】:

    shmget 只是保留一些共享内存——比如在磁盘上创建一个固定大小的文件。这些标志是低 9 位的权限掩码(如 openmode 参数)加上一些额外的标志,IPC_CREATIPC_EXCL,对应于 O_CREATO_EXCLopen。要实际访问该内存,您需要将其映射到进程的地址空间(“附加”它 - 类似于文件的 mmap)。这是使用shmat(返回一个指针)完成的。然后,您需要从该指针分配您的 FILE 结构。整个过程看起来是这样的:

    int id;
    FILE_entry *entries;
    
    id = shmget(key, N * sizeof(FILE_entry), IPC_CREAT | 0644);
    entries = (FILE_entry *) shmat(id, NULL, 0);
    
    // you can now access entries as if it was a N-element array.
    // to turn it into a circular list, link the entries appropriately.
    

    映射后,您可以像使用常规内存一样使用它 - 因为它常规内存。这就是重点!

    编辑:我忘了提一个重要的警告。像这样将链表放入共享内存段只有在所有相关进程都将其映射到同一地址时才有效!因此,您要么需要这样做(使用shmat 的第二个参数),要么从指针切换到相对于共享内存范围的基地址的偏移量。这意味着将您的 next 字段从指向 ptrdiff_t 的指针转换,并在加载时添加映射内存范围的基地址(并在存储时减去它)。

    【讨论】:

    • 如果只有一个进程修改列表,而另一个进程只读取(并且从不同时),您在答案底部突出显示的指针问题会不会是一个问题?
    • 这不是并发问题。指针取决于上下文——它们位于它们所在的地址空间的本地。进程 A 可能会设置列表并将其映射到 0x1000000(因此指针在该范围内),而进程 B 可能会将其映射到 0x1100000。如果 B 试图跟踪共享内存区域中的指针,最好的情况是它会崩溃,因为相关的内存范围根本没有映射,最坏的情况是它会读取一些其他随机不相关的数据,这些数据恰好在 Process 中的那个地址B 并且行为不可预测。
    • 但是如果共享内存区域是一个静态连续的内存块,那么该共享空间的byte[0]的内存地址一定要一样!?
    • 除此之外,使用上面定义的结构,每个项目的长度不会是 287 个字节(不管“名称”如何)?那么,当您可以迭代该字节数时,为什么还需要一个偏移量呢?
    • @Ash:不——共享内存可以附加到每个进程中不同的虚拟地址范围。
    【解决方案2】:

    您调用shmat(identifier, NULL, 0),获取指向共享内存在您的进程中映射到的位置的指针。在该位置创建您的对象,或memcpy/memmove 将它们创建到该位置。

    您可能会发现Beej's Unix IPC guide 很有用,它包含专门关于共享内存的部分。

    【讨论】:

      【解决方案3】:

      shmget 获取密钥后,您必须使用它通过shmat 获取实际指针。这是一些示例:

      int shmid;
      key_t key;
      FILE* shm;
      
      shmid = shmget(key, sizeof(FILE) * 10, IPC_CREAT | 0666);
      shm = shmat(shmid, NULL, 0);
      

      这样,您将能够使用shm 指针访问共享内存。在此链接中,您可以看到 shmgetshmat 的手册页:

      http://linux.die.net/man/2/shmget

      http://linux.die.net/man/2/shmat

      请参阅http://www.cplusplus.com 以获取更多参考。它还包含 C 引用。

      不知道我说的够不够清楚,所以,给一些cmet,如果有必要我会尽力为你开导。

      编辑:在此站点中,您可以找到一个非常简单的示例:http://simplestcodings.blogspot.com/2010/08/ipc-shared-memory-implementation-in-c.html

      【讨论】:

        【解决方案4】:

        如果你想分配一个新的共享内存段,你需要至少指定 IPC_CREATE 的标志,否则,它会寻找已运行的现有段,如果找不到则失败。

        其次,由于共享内存段是一个连续的内存块,您需要能够将所有 10 个 FILE_entry 结构存储在一起(或分配 10 个共享内存段——糟糕!)。

        因此,您确实需要为至少 10 个 FILE 结构的数组获取足够的内存。

        最后 FILE 和 FILE_entry 是非常糟糕的名字!使用不那么通用的东西,例如 MY_FILE_REF 和 MyFileRefEntry。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-08-25
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多