memory efficient way of implementing a circular buffer using mmap

Viewed 364

I am implementing IPC using shared memory using mmap. The structure i am using for sharing is

struct shared{
    sem_t P;
    sem_t C;
    sem_t M;
    int prod_status;
    char** queue;
    int buffer_size;
    int queue_start;
    int queue_after_last;       //pointing to buffer index after the last element in the buffer
    int queue_count;
};

The size of the buffer is passed as command line argument.

int main(int agrc, char* argv[]){
    int N_buff=atoi(argv[1]);
    struct shared* shared_data=(struct shared*)mmap(NULL,sizeof(struct shared),PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);
    //printf("hello\n");
    shared_data->buffer_size=N_buff;
    shared_data->queue=(char**)mmap(NULL,sizeof(N_buff),PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);
    for(int i=0;i<N_buff;i++){
        shared_data->queue[i]=(char*)mmap(NULL,70*sizeof(char),PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);
    }
    shared_data->queue_start=0;
    shared_data->queue_after_last=0;
    shared_data->queue_count=0;
    shared_data->prod_status=1;
    sem_init(&shared_data->P,1,N_buff);
    sem_init(&shared_data->C,1,0);
    sem_init(&shared_data->M,1,1);};

The queue is accessed by other process. I am using the queue as pipe. The question is, everytime I allocated memory using

shared_data->queue[i]=(char*)mmap(NULL,70*sizeof(char),PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);

It will allocate whole page of memory. Is there any other memory efficient way of implementing this using mmap? I am using fork after this code to spawn a child process, so I guess I can use pointers for IPC between parent and child using this method.

1 Answers

Your code had some bugs.

The second mmap call used sizeof(N_buff) [which is always 4] instead of: sizeof(*shared_data->queue) * N_buff

It is possible to do a single mmap for all the data [see below].


Here's the annotated and corrected code:

#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <sys/sem.h>
#include <sys/mman.h>

struct shared {
    sem_t P;
    sem_t C;
    sem_t M;
    int prod_status;
    char **queue;
    int buffer_size;
    int queue_start;
    // pointing to buffer index after the last element in the buffer
    int queue_after_last;
    int queue_count;
};

int
main(int agrc, char *argv[])
{
    int N_buff = atoi(argv[1]);
    struct shared *shared_data;

    shared_data = mmap(NULL, sizeof(struct shared), PROT_READ | PROT_WRITE,
        MAP_SHARED | MAP_ANONYMOUS, -1, 0);

    // printf("hello\n");
    shared_data->buffer_size = N_buff;
// NOTE/BUG: sizeof(N_buff) is _always_ 4
#if 0
    shared_data->queue = mmap(NULL, sizeof(N_buff), PROT_READ | PROT_WRITE,
        MAP_SHARED | MAP_ANONYMOUS, -1, 0);
#else
    shared_data->queue = mmap(NULL, sizeof(*shared_data->queue) * N_buff,
        PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
#endif
// NOTE: sizeof(char) is _always_ 1
    for (int i = 0; i < N_buff; i++) {
        shared_data->queue[i] = mmap(NULL, 70,
            PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    }

    shared_data->queue_start = 0;
    shared_data->queue_after_last = 0;
    shared_data->queue_count = 0;
    shared_data->prod_status = 1;

    sem_init(&shared_data->P, 1, N_buff);
    sem_init(&shared_data->C, 1, 0);
    sem_init(&shared_data->M, 1, 1);
}

Here's some cleaned up code for a single mmap [I've compiled but not tested it]:

#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <sys/sem.h>
#include <sys/mman.h>

#define PERQUEUE 70

struct shared {
    sem_t P;
    sem_t C;
    sem_t M;
    int prod_status;
    char **queue;
    int buffer_size;
    int queue_start;
    // pointing to buffer index after the last element in the buffer
    int queue_after_last;
    int queue_count;
};

int
main(int agrc, char *argv[])
{
    int N_buff = atoi(argv[1]);
    struct shared *shared_data = NULL;
    size_t total_size = 0;

    total_size += sizeof(struct shared);
    total_size += sizeof(*shared_data->queue) * N_buff;
    total_size += sizeof(PERQUEUE * N_buff);

    void *mapbase = mmap(NULL, total_size, PROT_READ | PROT_WRITE,
        MAP_SHARED | MAP_ANONYMOUS, -1, 0);

    void *mapcur = mapbase;

    shared_data = mapcur;
    mapcur += sizeof(struct shared);

    // printf("hello\n");
    shared_data->buffer_size = N_buff;

    shared_data->queue = mapcur;
    mapcur += sizeof(*shared_data->queue) * N_buff;

    for (int i = 0; i < N_buff; i++) {
        shared_data->queue[i] = mapcur;
        mapcur += PERQUEUE;
    }

    shared_data->queue_start = 0;
    shared_data->queue_after_last = 0;
    shared_data->queue_count = 0;
    shared_data->prod_status = 1;

    sem_init(&shared_data->P, 1, N_buff);
    sem_init(&shared_data->C, 1, 0);
    sem_init(&shared_data->M, 1, 1);

    // stuff ...

    munmap(mapbase,total_size);
}
Related