【问题标题】:Linked list in a shared memory segment共享内存段中的链表
【发布时间】:2017-12-12 05:42:55
【问题描述】:

我目前正在研究一个链表,一个链表节点有多个变量数据,将被保存在一个共享内存段中,以便另一个程序可以读取该列表并进行相应的操作。

我之前从事过套接字编程,但是发送数据流并不能满足我的目的,因为我必须基于一次读取一个节点/元素来进行验证。所以,在所有的 IPC 中,我认为共享内存是最好的,因为它也比其他的具有更好的性能(在这种情况下,不是一般情况下)。

以下是我制作的结构:

    struct DNode {
    char *polname;
    char *devname;
    char *status;
    char *srczone;
    char *dstzone;
    char *srcaddr;
    char *dstaddr;
    char *srcuser;
    char *app;
    char *service;
    char *urlcategory;
    char *action;
    char *vulnerability;

    char *value;
    struct DNode *next;
        };
    struct DNode *head = NULL;

struct DList {
    DNode pool[MAX_DNODE];      // fixed-size space for nodes
    size_t npool;               // used space in pool
    size_t pfree;               // pointer to re-use freed nodes
    size_t head;                // global list head
};

DList *dlist;
    DNode *dnode_alloc(void)
{
    if (dlist->pfree != DNULL) {
        DNode *node = dlist->pool + dlist->pfree;

        dlist->pfree = dlist->pool[dlist->pfree].next;
        return node;
    } else {
        if (dlist->npool < MAX_DNODE) return &dlist->pool[dlist->npool++];
    }

    return NULL;
}
void dnode_free(DNode *node)
{
    if (node) {
        node->next = dlist->pfree;
        dlist->pfree = node - dlist->pool;
    }
}

DNode *dnode(size_t index)
{
    return (index == DNULL) ? NULL : dlist->pool + index;
}

DNode *dnode_next(const DNode *node)
{
    return dnode(node->next);
}

DNode *dnode_push(size_t *head, const char *str)
{
    DNode *node = dnode_alloc();

    if (node) {
        strncpy(node->polname, str, sizeof(node->polname));
        node->next = *head;
        *head = node - dlist->pool;
    }

    return node;
}

    void dnode_pop(size_t *head)
{
    if (*head != DNULL) {
        size_t next = dlist->pool[*head].next;

        dnode_free(&dlist->pool[*head]);
        *head = next;
    }
}
int list_insert_front(struct node* new_node) {
        struct node *temp;
        temp = malloc(sizeof *temp);
        if (temp && new_node) {
            memcpy(temp, new_node, sizeof(struct node));
            temp->next = head;
            head = temp;
            return 1;
        }
        return 0;
    }

       int main(int argc, char **argv)
{
   struct Dnode *iter = head;
    int shmid;
    xmlDocPtr doc;
    xmlNode *root_element = NULL;


    if (argc != 2)
    {
        printf("\nInvalid argument\n");
        return(1);
    }

    doc = xmlReadFile(argv[1], NULL, XML_PARSE_NOBLANKS | XML_PARSE_NOERROR | XML_PARSE_NOWARNING | XML_PARSE_NONET);
    if (doc == NULL)
    {
        fprintf(stderr, "Document not parsed successfully.\n");
        return 0;
    }

    root_element = xmlDocGetRootElement(doc);

    if (root_element == NULL)
 {
        fprintf(stderr, "empty document\n");
        xmlFreeDoc(doc);
        return 0;
    }

    printf("Root Node is %s\n", root_element->name);
    traverse_dom_trees(root_element);

    shmid = shmget(IPC_PRIVATE, sizeof(DList), IPC_CREAT | 0660);
    if (shmid < 0) exit (1);

    dlist = shmat(shmid, NULL, 0);
    if (dlist == (void *) (-1)) exit(1);

    dlist->head = DNULL;
    dlist->pfree = DNULL;
    dlist->npool = 0;

    while(iter != NULL){
dnode_push(&dlist->head, head->polname);
    dnode_pop(&dlist->head);
    iter = head->next;
    }


    shmdt(dlist);
    xmlFreeDoc(doc);       // free document
    xmlCleanupParser();    // Free globals
    return 0;
}

如您所见,我还在 main 函数中包含了一个 XML 解析器部分,以便让您了解我将什么作为输入。但是我被卡住的部分是如何在共享内存中保存/使用这个结构,并使其他程序更容易访问它。

请有人为我提供一些伪代码,因为我以前从未使用过这样的 C 功能,并且完全不知道如何处理这个问题。 欢迎任何和所有建议,并提前表示感谢。

编辑 1

在虚拟机上使用 Centos7,因为有人指出提到该平台会很有成效。

编辑 2 只是添加了一些代码来实现共享内存段,它并没有给我任何这样的错误。我关心的是:

  • 它是否符合我的预期?
  • 方法是否正确?
  • 我知道我目前只推送一个元素,但这肯定是对的,对吧?
  • 我是否在浪费时间和精力尝试使用共享内存来解决问题?

【问题讨论】:

  • 也许你应该澄清你的平台。虽然有跨平台共享的 mem / IPC 库,但通常你会考虑一些平台。
  • 恐怕共享内存并不是这里最好的工具。要使用它,您必须首先构建一个自定义内存分配器,该分配器能够在共享内存段中分配和释放内存,并在该共享动态内存中持续复制节点本身以及从节点指向的所有 char 数组。长话短说,您只是在编写队列的自定义实现......
  • 共享内存不适合链表。这对阵列有好处。对于您的情况,(您使用链表),共享内存或多或少类似于内存池,但共享。大部分工作不会是创建共享内存或锁定/解锁共享内存。最繁重的工作是如何管理(alloc、free 等)内存池。相比如何管理内存池,如何让内存池共享要容易得多。所以如果你的数据可以重新组织成一个数组,并且添加多于删除,那么你基本上可以忘记内存池,共享内存很容易,适合你。
  • 是的,你做到了,而且看起来不错。这个想法——你使用字段next作为本地进程空间中的指针,并作为共享内存空间中的偏移——非常棒。但事实上,我想你知道,你只是在共享内存空间中实现了一个数组,虽然它看起来像一个链表,对吧:)。现在让我们回到共享内存。首先IPC_PRIVATE不好,会导致私有内存,不共享。使用其他密钥(请参阅ftok)。接下来你需要开始考虑保护(锁定/解锁)共享内存(参见semget)。
  • 我看到您使用的是 System V 样式 (shmget/shmat),但我建议您使用 POSIX 样式 (shm_open/mmap),相应地锁定/解锁机制是sem_open。一个原因是许多文档更喜欢 POSIX 样式(如果我没记错的话),另一个原因是 POSIX 共享内存可以在创建后通过ftruncate 更改大小。 (System V风格不错,够用了,不想引入更多未知问题就用吧)。

标签: c struct linked-list shared-memory


【解决方案1】:

一般来说,您不能保证共享内存段在一个进程中与在另一个进程中占用相同的虚拟地址范围。因此,在尝试解释指针字段中的值时会遇到问题,因为它们代表了指向对象的地址在写入指针值的进程的虚拟地址空间中,而这个如果两个进程在不同位置映射共享内存段,则可能会有所不同。

您可以传递mmap 调用一个指针来告诉系统您希望将段映射到虚拟地址空间中的哪个位置,因此共享指针指向两个虚拟地址空间中的相同位置。但是那个指针只是一个提示,操作系统不会强制跟随你的指针。

对此有两种解决方案。第一个是偏移指针值,因此您从您在共享段中看到的那个构造您的虚拟地址空间指针。第二个是确保您的内存段都映射到相同的地址。这必须在两个进程之间进行协调(但它只能在内存映射中完成一次),因为对一个有利的地方可能会被另一个禁止(因为它在那里映射了其他东西)

在 64 位架构中,这很容易,因为您有无数个虚拟地址可以将段映射到,而且您可能可以选择一个地址而不会与其他子系统发生冲突。想想在 32 位系统中,通常共享库为这里的模块的数据段消耗一堆地址,并且堆栈提供了大量内存,堆也......所以你必须在尝试将两个段放在一个共享的相同地址中。

注意

在您的特定情况下,结构的几乎所有字段都是指针,这适用于所有字段,而不仅仅是链接列表的字段。不仅所有列表节点都必须位于共享段中......所有字符串以及您访问的所有内容都是共享的。

【讨论】:

    【解决方案2】:

    您需要:

    • 在您的平台上设置共享内存 - 请参阅 here
    • 在您的程序中,调用shm_open 打开共享内存并使用mmap 通过指针访问它 - 请参阅here
    • 从不同的进程/线程访问共享内存必须使用一些仲裁/互斥机制 - 请参阅here

    【讨论】:

    • 先生,如果你能检查一下我刚刚做的版本。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-20
    • 2012-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多