共享内存段中的链表

Linked list in a shared memory segment

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

我以前从事过套接字编程,但是发送数据流并不能满足我的目的,因为我必须一次读取一个 node/element 来进行验证。所以,在所有的 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;
}

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

请有人能为我提供一些伪代码,因为我以前从未使用过此类 C 功能,并且完全不知道如何处理它。 欢迎提出任何建议,在此先表示感谢。

编辑 1

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

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

您将需要:

  • 在您的平台上设置共享内存 - 请参阅 here
  • 在您的程序中,调用 shm_open 打开共享内存并使用 mmap 通过指针访问它 - 参见 here
  • 从不同 processes/thread 访问共享内存必须使用一些 arbitration/mutual 排除机制 - 参见 here

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

您可以通过 mmap 调用一个指针来告诉系统您希望将段映射到您的虚拟地址 space 中的哪个位置,因此共享指针在两者中都指向相同的位置虚拟地址spaces。但是那个指针只是一个提示,操作系统并没有被强制跟随你的指针。

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

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

注意

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