Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to allocate linked list inside struct in shared memory c

I have a linked list inside a struct in C, or so I think. The structs are:

//Structure of the domain list
typedef struct domains *domain_list;
    struct domains{
    char *domain;
    domain_list next;
}domains_node;

//Structure of the configuration of the server
typedef struct{
    int n_threads;
    domain_list domain_list;
    char* local_domain;
    char* named_pipe_statistics;
}server_config;

I tried to enter them in shared memory, I'm sure that the struct is fine, but I don't know if the linked list is correct (Global variables used):

//Initialize shared memory
if((config_shmid = shmget(IPC_PRIVATE, sizeof(server_config), IPC_CREAT|0777)) < 0){
    perror("Error in config shmid\n");
    exit(1);
}
if((config = (server_config*)shmat(config_shmid, NULL, 0)) == (server_config *)-1){
    perror("Error in config shmat\n");
    exit(1);
}

if((config_domain_shmid = shmget(IPC_PRIVATE, sizeof(struct domains), IPC_CREAT|0777)) < 0){
    perror("Error in domain_list config shmid\n");
    exit(1);
}
if((config->domain_list = (domain_list)shmat(config_domain_shmid, NULL, 0)) == (domain_list)-1){
    perror("Error in domain_list config shmat\n");
    exit(1);
}

This is for process comunication. I need a dynamic (not fixed) linked list, inside a struct, in shared memory. So, what I need is a way to allocate space in memory for the new nodes I create, and how to link them after. I now it's not with malloc, but answers on this matter just go as "adequate allocation" and I don't know what it is.

like image 588
André Almeida Avatar asked Oct 23 '15 00:10

André Almeida


1 Answers

You don't say this, but I guess that you use shared memory so that several processes can access the same linked list, either simultaneously or sequentially.

In that case, you can create a shared-memory segment that will hold a pool of nodes plus some control data.

The whole information must be contained in that segment, though, for the other processes to see it. Therefore, your domain member should be a char buffer, not a pointer to a string that lies somewhere else in memory.

All non-null node pointer values will be addresses in the pool, but the shared memory segments will probably be mapped to different addresses on different processes. Therefore, the nodes can't have absolute next pointers. They could keep a relative index into the shared node pool, though. The same applies to the headsm of the lists.

In your linked-list code you should replace the malloc and free with custom functions that fetch a node in the pool or put it back there. Because the pool has a fixed size, the custom malloc can return NULL, for which you should check.

The code below implements a simple linked list with a fixed-sized pool that is contained in a shared memory segment. It keeps all visible data as relative sizes, but operates on node pointers, which you can get with dnode, for local iteration. Because 0 is a valid pool index, there is a special value, DNULL, which describes the null pointer by means of a size_t.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/shm.h>



typedef struct DNode DNode;
typedef struct DList DList;

#define MAX_DNODE 32            // Max. domain string length
#define MAX_DLEN 64             // Max. number of list nodes

#define DNULL (MAX_DLEN + 1)    // NULL value

struct DNode {
    char domain[64];
    size_t next;
};

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->domain, str, sizeof(node->domain));
        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 main(int argc, char* argv[])
{
    int shmid;

    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;

    dnode_push(&dlist->head, "Alpha");
    dnode_push(&dlist->head, "Bravo");
    dnode_push(&dlist->head, "Charlie");
    dnode_push(&dlist->head, "Delta");
    dnode_push(&dlist->head, "Echo");

    while (dlist->head != DNULL) {
        puts(dnode(dlist->head)->domain);
        dnode_pop(&dlist->head);
    }

    shmdt(dlist);

    return 0;
}
like image 173
M Oehm Avatar answered Nov 14 '22 23:11

M Oehm