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.

1

2 Answers 2

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;
}
Sign up to request clarification or add additional context in comments.

7 Comments

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 speculation
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.
misspelled, my bad. But since I can't have a fixed sized pool, I'll have to do it with another way
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.
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.
|
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

4 Comments

It looks like you are commenting on another answer. The answer area is not intended for that.
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 Review
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.
Don't use the answer section for such comments. This area is not intended for that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.