How to allocate linked list inside struct in shared memory c
Asked Answered
C

2

8

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.

Candicandia answered 23/10, 2015 at 0:44 Comment(1)
P
11

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;
}
Phares answered 23/10, 2015 at 7:14 Comment(7)
Yes i use shared memory for several process to access it. No I can't use a pool of threads. I think I have to do shmid and shmat everytime I want to add a node, but that's just speculationBarbwire
It's not a pool of threads, it's a pool of nodes. If you really wanted to create a shared-memory segment for each node, you'd have to make the next pointer shmids or something, but that's really pointless, in my opinion. by the way, I have tested the above code across different processes (parent/child and sequential) and it works.Phares
misspelled, my bad. But since I can't have a fixed sized pool, I'll have to do it with another wayBarbwire
Why can't you have a fixed-size pool? Your requirements are not clear from the question.Phares
I did say "linked list inside a struct". What you have is a array inside a struct. But I did improve my question, so there are less misunderstandingsBarbwire
You misunderstand the code: It is a linked list. You can easily insert and remove nodes and change the links between them. The array is just the allocation pool. It has a fixed size, works with indices instead of pointers and uses char buffers instead of pointers to memory out of the main struct, so that all information is available across processes. That wouldn't be possible in your design. The fixed-size is really a fixed maximum size.Phares
The DList struct is just a wrapper around some global variables, so that it is easy to map everything to a single shared-memory segment.Phares
I
0

This was very helpful, thanks for posting. A few tweaks & fiddles:

  1. dnode_alloc needs to increment npool when dlist->pfree != DNULL (add the line list->npool++; just before the return statement).
  2. Similarly dnode_free needs to decrement npool prior to returning (add line list->npool--; just before returning).
  3. In dnode_alloc the section dlist->npool < MAX_DNODE should be dlist->npool < MAX_DLEN
Icing answered 24/12, 2022 at 19:20 Comment(4)
It looks like you are commenting on another answer. The answer area is not intended for that.Knurly
This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From ReviewKnurly
Yes, I'm commenting on M Oehm's answer, who provided a very helpful code snippet which I copied, enhanced, and used in a system I'm currently building. I found a few items that needed correcting so my comment was to provide context for anyone else that wanted to use it.Icing
Don't use the answer section for such comments. This area is not intended for that.Knurly

© 2022 - 2024 — McMap. All rights reserved.