我目前正在研究一个链表的部分,一个链表节点有多个变量数据,将被保存在共享内存段中,以便另一个程序可以读取该列表并进行相应的操作。
我以前从事过套接字编程,但发送数据流并不能满足我的目的,因为我必须基于一次读取一个节点/元素来进行验证。因此,在所有 IPC 中,我认为共享内存是最好的,因为它也比其他 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 解析器部分,以便让您了解我将什么作为输入。但我遇到的问题是如何在共享内存中保存/使用这个结构,并使其他程序可以轻松访问它。
请有人为我提供一些伪代码,因为我以前从未使用过此类 C 功能,并且对如何解决这个问题完全一无所知。 欢迎提出任何建议,并提前致谢。
编辑 1
在虚拟机上使用 Centos7,因为有人指出提及该平台会很有成效。
编辑2 只是添加了一些代码来实现共享内存段,并且它没有给我任何此类错误。我关心的是:
- 它达到了我的预期吗?
- 这种方法正确吗?
- 我知道我目前只是推送一个元素,但这肯定是正确的,对吧?
- 我是否在浪费时间和精力尝试使用共享内存来解决这个问题?
最佳答案
一般来说,您不能保证共享内存段在一个进程中占用的虚拟地址范围与在其他进程中占用的虚拟地址范围相同。因此,当尝试解释指针字段中的值时,您会遇到问题,因为它们表示指向对象的地址在写入指针值的进程的虚拟地址空间中如果两个进程将共享内存段映射到不同的位置,则可能会有所不同。
您可以向 mmap
调用传递一个指针,告诉系统您希望将段映射到虚拟地址空间中的哪个位置,因此共享指针指向两个虚拟地址空间中的同一位置。但该指针只是一个提示,操作系统不会强制遵循您的指针。
对此有两种解决方案。第一个是偏移指针值,因此您可以从共享段中看到的指针构造虚拟地址空间指针。第二个是确保您的内存段都映射到相同的地址。这必须在两个进程之间进行协调(但只能在内存映射时完成一次),因为对一个进程有利的地方可以被另一个进程禁止(因为它在那里映射了其他一些东西)
在 64 位架构中,这很容易,因为您有无数的虚拟地址可以将段映射到,并且您可能可以选择一个地址而不会与其他子系统发生冲突。认为在 32 位系统中,通常共享库会消耗大量模块数据段的地址,并且堆栈提供大量内存,堆也......所以你必须在执行之前计划好这些事情。尝试将两个段放入一个共享的相同地址中。
注意
在您的特定情况下,结构的几乎所有字段都是指针,这适用于所有字段,而不仅仅是列表链接字段。不仅所有列表节点必须位于共享段中...所有字符串以及您访问的所有内容也必须位于共享段中。
关于c - 共享内存段中的链表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47766016/