如何在C语言中将结构体存储到共享内存区域并检索出来

7
作为一项大学作业,我需要创建一个最多包含10个文件名的循环列表,并将它们存储在共享内存区域中,以便两个子进程可以读写该列表(使用信号量来控制访问)。问题是,我完全不懂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指向第1个元素。我这样做的原因是可以简单地遍历列表而不需要迭代器(并且不必像数组那样检查列表的末尾)。
我还知道我需要使用shmget获取内存区域,我理解它,我传递shmget一个键、一个大小和一个标志(我不理解),它返回一个int类型的标识符。
所以我的问题有两个。如何将我的链接列表存储到共享内存区域中-以及如何从共享内存区域中访问它?
4个回答

10

shmget只是保留一些共享内存,类似于在磁盘上创建一个固定大小的文件。标志位是低9位中的权限掩码(就像open函数的mode参数),加上一些额外的标志:IPC_CREATIPC_EXCL,对应于open函数的O_CREATO_EXCL。如果要实际访问该内存,则需要将其映射到您进程的地址空间中("附加"它 - 类似于文件的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 并且每当加载它时添加映射内存范围的基地址(并在存储它时减去它)。


如果只有一个进程修改列表,而另一个进程仅读取(且从未同时进行),那么您在答案底部强调的指针问题是否会减少? - Ash
这不是并发问题。指针取决于上下文 - 它们局限于其所在的地址空间。进程A可能设置了列表并将其映射到0x1000000(因此指针在该范围内),而进程B可能将其映射到0x1100000。如果B尝试跟随共享内存区域中的指针,最好的情况是会崩溃,因为相关的内存范围根本没有映射,而最坏的情况则是读取一些其他随机无关的数据,这些数据恰好位于进程B中的该地址,并表现出不可预测的行为。 - Fabian Giesen
但是,如果共享内存区域是一个静态的连续内存块,那么该共享空间的byte[0]内存地址肯定是相同的吧!? - Ash
而且,使用我上面定义的结构体,每个项目都会是287个字节长(不管“名称”是什么)。所以,既然可以沿着这个字节数迭代,为什么还需要偏移量呢? - Ash
1
@Ash:不是的——共享内存可以在每个进程中附加到不同的虚拟地址范围。 - caf
好的,至少我对每个结构体的固定大小是正确的吧? - Ash

2

当你调用shmat(identifier, NULL, 0)时,会得到一个指针,指向共享内存在你的进程中映射到的位置。你可以在这个位置创建你的对象,或者使用memcpy/memmove将它们复制到这个位置。

你可能会发现Beej's Unix IPC guide很有用,其中包含了一个关于共享内存的专门章节。


2

在使用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/shmgethttp://linux.die.net/man/2/shmat
此外,您还可以参考http://www.cplusplus.com。它也包含C语言参考资料。
如果我表述不够清晰,请留言,我会尽力为您解答。
编辑:在这个网站上,您可以找到一个非常简单的示例。

1

对于标志,您需要至少指定IPC_CREATE,如果要分配新的共享内存段,否则,它将查找现有的段,并在找不到时失败。

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

因此,您真的需要为至少10个FILE结构体数组获得足够的内存。

最后,FILE和FILE_entry是非常糟糕的名称!使用一些不那么通用的名称,如MY_FILE_REF和MyFileRefEntry。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接