C linux中共享内存中的多个链接列表

C linux中共享内存中的多个链接列表,c,linux,shared-memory,C,Linux,Shared Memory,我使用C语言和Linux作为平台。 我想在多个进程中共享一些结构,这些结构有链接列表的头(这些列表也应该共享)和指向彼此的指针。此数据所需的内存最高可达1Mb。 因为我不能在共享内存中使用指针,因为它们对于不同的进程无效 有两种选择: 1) 使用偏移量值而不是指针。 2) 否则,使用不同的共享内存,并使用共享内存ID(由shmget返回)而不是指针 由于要共享的内存大小很大,哪个选项更好? 你能建议另一种选择吗 谢谢 使用偏移值 使用size\u t从共享内存区域开始的偏移量(以字符为单位),而

我使用C语言和Linux作为平台。 我想在多个进程中共享一些结构,这些结构有链接列表的头(这些列表也应该共享)和指向彼此的指针。此数据所需的内存最高可达1Mb。 因为我不能在共享内存中使用指针,因为它们对于不同的进程无效

有两种选择: 1) 使用偏移量值而不是指针。 2) 否则,使用不同的共享内存,并使用共享内存ID(由shmget返回)而不是指针

由于要共享的内存大小很大,哪个选项更好? 你能建议另一种选择吗

谢谢

使用偏移值

使用
size\u t
从共享内存区域开始的偏移量(以字符为单位),而不是指针。在访问或操作这些列表的任何地方都需要这样做

编辑以添加:

以这种方式使用偏移量可以在大多数体系结构上编译成非常高效的代码,并且您可以使用内置程序以原子方式访问和修改它们。请记住对所有访问(包括读取)使用内置访问:否则,该值可能在非原子读取期间被原子修改(反之亦然),从而导致数据损坏

如果您知道共享内存的大小永远不会超过4GB,那么您可以使用
uint32\u t
作为偏移量类型,在64位体系结构上为每个“指针”节省四个字节。如果将所有目标与32位边界对齐,则可以将其四倍于8GB

使用
uint32\u t
的一个非常好的副作用是,您可以在所有64位和一些32位体系结构上原子地操作指针对(两个连续的偏移量)。假设共享内存中的所有内容都与32位边界对齐,并使用每个32位单元的偏移量,则可以使用

static inline void get_pair(void *const base, const uint32_t offset, uint32_t *const pair)
{
    uint64_t *const ptr = (uint64_t *)(offset + (uint32_t *)base);
    uint64_t        val;

    val = __sync_or_and_fetch(ptr, (uint64_t)0);

    memcpy(pair, &val, sizeof val);
}

static inline void switch_pair(void *const base, const uint32_t offset, const uint32_t *const new, uint32_t *const old)
{
    uint64_t *const ptr = (uint64_t *)(offset + (uint32_t *)base);
    uint64_t        oldval, newval;

    memcpy(newval, &new, sizeof newval);

    do {
        /* Note: this access does not need to be atomic, */
        memcpy(oldval, ptr, sizeof oldval);
        /* because the next one verifies it. */
    } while (!__sync_bool_compare_and_swap(ptr, oldval, newval));

    if (old)
        memcpy(old, &oldval, sizeof oldval);
}
内置的
\uuu sync…()
至少在GCC和Intel CC中可以工作。新的C11标准也采用了C++11风格的内置程序,但是在当前的编译器中实现这些功能需要一些时间

如果您编写库代码,或者您希望维护几年的代码,那么您可能会节省时间来查找这两种内置类型,并为自己(或在两种内置类型之间转换时维护它的人)添加注释,以描述如果它们已经可用,您将使用哪种原子内置类型


最后,请记住,像这样使用共享内存意味着您必须遵守相同的注意事项,就像您有多个线程同时访问内存一样。原子操作会有所帮助,如果你能以原子方式操纵指针对,你可以用列表做一些非常聪明的技巧,但是,您仍然需要非常清楚具体的情况和可能的争用条件。

封送/解封操作是可以考虑的吗?您可以安排在特定的地址(所有进程中的地址相同)加载共享内存,以便只要所有指针都指向共享内存中的数据,它们在所有过程中都同样有效。关键点是确保共享内存段在所有进程中加载在同一地址。如果有一个共同的祖先进程,它加载了共享的记忆,那么一切都是甜蜜和光明;没有其他问题了。如果进程在没有共同祖先的情况下启动,那么您必须就地址达成一致,并确保系统适合您。@JonathanLeffler,我如何确保进程以共同祖先进程启动?如果您有一个创建共享内存的主守护进程,然后分叉工作进程,您知道它们是这样做的。如果您需要随机启动的进程来连接到共享内存,那么您需要一种机制来指定连接共享内存的地址,并在使用或其类似物时指定该地址。感谢您的建议,我使用了带有特定地址的shmat()。共享内存在我的Linux主机上工作正常。链表数据和指针适用于所有进程。非常感谢。这种偏移方式很有用。