Reputation: 813
I am currently working on a part where a linked list, one linked list node has multiple variable data, is to be saved in a shared memory segment so that another program can read that list and do operations accordingly.
I have previously worked on socket programming, but sending a stream of data does not fulfill my purpose, as I have to do validation based on reading one node/element at a time. So, of all the IPC's, I think shared memory would be the best as it also has good performance than others(in this case, not generally).
The following is the struct that I have made:
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;
}
As you can see, I have also included an XML parser part in the main function so as to give you an idea of what I am taking as an input. But the part where I am stuck is how to save/use this struct inside a shared memory, and making it easy for the other program to access it.
Please can somebody provide me with some pseudo-code for the same as I have never used such C functionalities before and am absolutely clueless on how to approach this. Any and all suggestions are welcome and am thankful in advance.
Edit 1
Using Centos7 on a virtual machine since somebody pointed out that mentioning the platform would be fruitful.
Edit 2 Just added some code to implement shared memory segments, and it does not give me any such errors. What my concern is:
Upvotes: 1
Views: 1198
Reputation: 12668
In general you cannot warrant that a shared memory segment will occupy the same virtual address range in one process than in other. So you'll have a problem when trying to interpret the values in the pointer fields, as they represent the address of the pointed object in the virtual address space of the process that wrote there the pointer value and this can be different if both processes map the shared memory segment at different places.
You can pass the mmap
call a pointer to tell the system where in your virtual address space you want the segment to be mapped, so the shared pointers point to the same place in both virtual address spaces. But that pointer is only a hint, and the operating system is not forced to follow your pointer.
There are two solutions for this. The first is to offset the pointer values, so you construct your virtual address space pointer from the one you see in the shared segment. The second is to ensure your memory segments both are mapped to the same address. This has to be coordinated between both processes (but it has to be done only once, at memory mapping) as the place that is good for one can be forbidden for the other (because it has mapped some other thing there)
In 64bit architectures this is easy, as you have a zillion virtual addresses to map the segment to, and probably you can select an address without clashing with other subsystem. Think that in 32bit systems, normally shared libraries consume a bunch of addresses for the data segments of the modules herein, and the stacks makes provision for large amounts of memory, and the heap also... so you have to plan the thing before an attempt of putting both segments in a shared, identical address.
In your particular case, that almost all the fields of the structure are pointers, this applies to all the fields, and not only the list linking ones. Not only all the list nodes must lie in the shared segment... also all the strings, and everything you access that is shared.
Upvotes: 1
Reputation: 758
You will need to:
shm_open
to open the shared memory and use mmap
to access it using a pointer - see here.Upvotes: 0