C 如何将结构映射为共享匿名内存?

C 如何将结构映射为共享匿名内存?,c,struct,mmap,C,Struct,Mmap,我正在尝试将结构映射为匿名共享内存,以便它可以跨多个子进程共享。但是,每当我在创建结构的函数之外访问该结构时,就会得到一个segfault。代码来自trinity fuzzer,可以在这里找到 首先,我们有一个头的摘录,它声明并定义了我们将要使用的结构。注意:shm在结构声明下面声明为全局 struct shm_s { struct childdata **children; struct stats_s stats; unsigned int running_childs; /* rng

我正在尝试将结构映射为匿名共享内存,以便它可以跨多个子进程共享。但是,每当我在创建结构的函数之外访问该结构时,就会得到一个segfault。代码来自trinity fuzzer,可以在这里找到

首先,我们有一个头的摘录,它声明并定义了我们将要使用的结构。注意:shm在结构声明下面声明为全局

struct shm_s {
struct childdata **children;

struct stats_s stats;

unsigned int running_childs;

/* rng related state */
unsigned int seed;

#ifdef ARCH_IS_BIARCH
/* Check that 32bit emulation is available. */
unsigned int syscalls32_succeeded;
unsigned int syscalls32_attempted;
#endif

/* pids */
pid_t mainpid;
pid_t last_reaped;

/* various flags. */
enum exit_reasons exit_reason;
bool dont_make_it_fail;
bool spawn_no_more;
bool ready;
bool postmortem_in_progress;

/* Use dispatch queue instead of locks. */

/* main<>watchdog serial queue, for reap_child()
 *  provides exclusion so they don't both try at the same time. */
dispatch_queue_t reaper_queue;

/* to protect from multiple child processes from
 * trying to disable the same syscall at the same time. */
dispatch_queue_t syscalltable_queue;

/* child<>child serial queue, used so only one child spews debug output */
dispatch_queue_t bugQueue;

/* global debug flag.
 * This is in the shm so we can do things like gdb to the main pid,
 * and have the children automatically take notice.
 * This can be useful if for some reason we don't want to gdb to the child.
 */
bool debug;

};

extern struct shm_s *shm;
这是alloc_shared函数,它实际上创建了共享内存

void * alloc_shared(unsigned int size)
{
    void *ret;

    ret = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
    if (ret == MAP_FAILED) {
        printf("mmap %u failure\n", size);
        exit(EXIT_FAILURE);
    }
    /* poison, to force users to set it to something sensible. */
    memset(ret, rand(), size);
    return ret;
}
最后,在单独的文件中调用create_shm和init_shm函数

create_shm();

init_shm();
每当程序到达shm->stats.total\u syscalls\u done=1时,程序就会崩溃;在init_shm内部。我打印了shm的地址,它在alloc_shared和init_shm中创建后是相同的,所以我认为指针没有损坏。
无论如何,我都不明白为什么我无法访问create_shm()之外的结构。

根据OP在评论中的回答,printf的输出是:

shm: redzone:0x10a0ad000. shmdata:0x10a0cb000. redzone:0x10a0cb000 end:0x10a0e9000
请注意,
shmdata
与后面的红色区域位于同一地址(
redafter
),两者都位于
0x10a0cb000
。这意味着
shm_页面
为零

这一行很可能是问题所在:

shm_pages = ((sizeof(struct shm_s) + page_size - 1) & PAGE_MASK) / page_size;
我猜
PAGE\u MASK
0xfff
。如果是,则该表达式将始终求值为0。正如chmike所指出的,由于您无论如何都要除以页面大小,因此根本不需要使用
page\u MASK
。在这种情况下,您的代码应为:

shm_pages = (sizeof(struct shm_s) + page_size - 1) / page_size;

create\u shm()
之前的
page\u size
值是多少?确保将其设置为
getpagesize()
#include
)。我测试了你的代码,除了这个问题之外,我没有崩溃,
init\u shm
page\u size
在校准
create\u shm()
之前设置为
getpagesize()
。您使用的是什么操作系统?如果是Linux,我想这就解释了为什么它适合你而不是我。我在FreeBSD10.1和MacOSX10.10.1上都试过,但Linux还没有。我认为,从c的角度来看,这段代码可能依赖于未定义的行为,但实际上,它在使用gcc的Linux上定义得很好,所以这就是它在Linux上工作的原因。我将编译它并在Linux上运行并测试这一理论。只是一些简单的问题:1)printf“shm:redzone:shmdata:redzone:end”的输出是什么?2) 如果将
shm->stats.total\u syscalls\u done=1
放在
create\u shm()
的末尾,它还会崩溃吗?3) 您的程序是否在
create_shm()
init_shm()
之间执行任何操作?4) 您可以
printf(“%p”,&shm->stats.total\u syscalls\u done)
看到了吗?输出是shm:redzone:0x10a0ad000。shmdata:0x10a0cb000。redzone:0x10a0cb000 end:0x10a0e9000,将
shm->stats.total\u syscalls\u done=1
放在末尾仍会导致崩溃。基本上,访问shm成员的第一件事会导致崩溃。在
create_shm()
init_shm
之间不会发生其他任何事情。
&shm->stats.total\u syscalls\u done
的printf是0x10a0cb008。如果我们除以页面大小,这个&PAGE\u掩码是无用的。OP应该检查PAGE_MASK的值。谢谢,更改该行修复了问题,但是为什么它会在linux上运行呢?@2trill2spill我不确定。要么
PAGE\u MASK
不一样,要么
mprotect()
有不同的行为。如果你好奇的话,你应该在Linux上调试它来找到答案。在我的测试中,我将PAGE_MASK定义为PAGE_size-1,这是一个错误的值。我再次检查,确实
shm==redafter
。显然,mprotect并不能阻止阅读@奇美奇。。。您是否检查了
mprotect
的返回值以查看它是否返回了某种错误?也许有什么原因它不允许这样做?
shm_pages = (sizeof(struct shm_s) + page_size - 1) / page_size;