【发布时间】: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