如何跨进程共享ck_list?

时间:2015-09-02 19:32:05

标签: c struct concurrency

我正在尝试在http://concurrencykit.org/中使用CK_LIST跨多个进程,但是当我这样做时,列表节点中的值是垃圾。但是,当我仅使用一个进程中的列表时,列表值是正确的。

以下是在单个进程中在结构中使用CK_LIST的示例。

#include <stdio.h>
#include <stdlib.h>
#include <ck_queue.h>

struct shared_map
{
    CK_LIST_HEAD(list, list_node) list;
};

struct list_node
{
    void *data;

    CK_LIST_ENTRY(list_node) list_entry;

};

int main(void)
{
    struct list_node *node, *node2;
    struct shared_map mapping = { .list = CK_LIST_HEAD_INITIALIZER(mapping->list) };
    struct shared_map *map = &mapping;

    node = malloc(sizeof(struct list_node));
    if(node == NULL)
    {
        perror("malloc");
        return -1;
    }

    CK_LIST_INIT(&map->list);

    int rtrn = asprintf((char **)&node->data, "test");
    if(rtrn < 0)
    {
        perror("asprintf");
        return -1;
    }

    CK_LIST_INSERT_HEAD(&map->list, node, list_entry);

    CK_LIST_FOREACH(node2, &map->list, list_entry)
    {
        printf("out: %s\n", node2->data);
    }

    return 0;
}

但是当我尝试在两个单独的进程之间使用列表时,node->data的值是垃圾并导致使用它的进程崩溃。以下是一个例子。

#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <ck_queue.h>

struct shared_map
{
    CK_LIST_HEAD(list, list_node) list;
};

struct list_node
{
    void *data;

    CK_LIST_ENTRY(list_node) list_entry;

};

static int create_shared(void **pointer, int size)
{
    *pointer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
    if(*pointer == MAP_FAILED)
    {
        perror("mmap:");
        return -1;
    }

    return 0;
}

int main(void)
{
    struct list_node *node, *node2;
    struct shared_map mapping = { .list = CK_LIST_HEAD_INITIALIZER(mapping->list) };
    struct shared_map *map = &mapping;

    int rtrn = create_shared((void **)&map, sizeof(struct shared_map));
    if(rtrn < 0)
    {
        printf("Can't create shared mapping\n");
        return -1;
    }

    CK_LIST_INIT(&map->list);

    pid_t pid;

    pid = fork();
    if(pid == 0)
    {
        /* Child. */

        node = malloc(sizeof(struct list_node));
        if(node == NULL)
        {
            perror("malloc");
            return -1;
        }

        int rtrn = asprintf((char **)&node->data, "test");
        if(rtrn < 0)
        {
            perror("asprintf");
            return -1;
        }

        CK_LIST_INSERT_HEAD(&map->list, node, list_entry);
    }
    else if(pid > 0)
    {
        /* Parent. */

        sleep(1); // Make sure child runs first.

        CK_LIST_FOREACH(node2, &map->list, list_entry)
        {
            printf("out: %s\n", node2->data);
        }
    }
    else
    {
        perror("fork");
        return -1;
    }

    return 0;
}

CK_LIST被列为多读者单作者链接列表,因此我认为我只需要锁定写入而不是读取。那么为什么node->data在进程之间使用它而不是单个进程使用时会变成垃圾?

1 个答案:

答案 0 :(得分:0)

幸运的是,这与CK无关,只与你的记忆来源有关。

您在孩子身上的分配来自malloc,住在孩子的地址空间。子节点和父节点不共享相同的地址空间(是单独的进程),因此节点和数据的malloc(3)获取存储不在父节点地址空间内的地址。

您还需要从某个共享空间为nodenode->data分配内存。我对代码进行了一些最小的更改,以使其用于演示目的。希望显然,你希望你的API在魔术数字方面不那么脆弱。

#include <sys/mman.h>

#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

#include <ck_queue.h>

struct shared_map {
    CK_LIST_HEAD(list, list_node) list;
};

struct list_node {
    void *data;
    CK_LIST_ENTRY(list_node) list_entry;
};

static int
create_shared(void **pointer, int size)
{

    *pointer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
    if (*pointer == MAP_FAILED) {
        perror("mmap:");
        return -1;
    }

    return 0;
}

int
main(void)
{
    struct list_node *node, *node2;
    struct shared_map mapping = { .list = CK_LIST_HEAD_INITIALIZER(mapping->list) };
    struct shared_map *map = &mapping;

    int rtrn = create_shared((void **)&map, sizeof(struct shared_map) + sizeof(struct list_node) + 5);
    if (rtrn < 0) {
        printf("Can't create shared mapping\n");
        return -1;
    }

    CK_LIST_INIT(&map->list);

    pid_t pid;

    pid = fork();
    if (pid == 0) {
        /* Child. */
        node = (struct list_node *)(map + 1);
        node->data = node + 1;
        memcpy(node->data, "test", 5);
        CK_LIST_INSERT_HEAD(&map->list, node, list_entry);
    } else if (pid > 0) {
        /* Parent. */
        sleep(1); // Make sure child runs first.

        CK_LIST_FOREACH(node2, &map->list, list_entry) {
            printf("out: %s\n", (char *)node2->data);
        }
    } else {
        perror("fork");
        return -1;
    }

    return 0;
}

从根本上说,我只是改变了mmap的大小,以包含list_node的空间和附加到node-&gt;数据的C字符串,并使子设置为up。