12

I have a shared dynamic array within POSIX model between two different applications. I would like to have an ability to change its size without copying. Unfortunately I couldn't find right solution to increase and decrease POSIX shared memory in C language. In the web I have found many documents with poor explanations and wretched examples. I've managed to find some interesting topics, but all of them are unsuitable to me:

  1. "Linux System Programming" - "Mapping Files into Memory" Part: "Resizing a Mapping" - where is no working example to resize SHM.

  2. How do I implement dynamic shared memory resizing? - A description only. Have no example.

  3. mremap function failed to allocate new memory - Favorite answer works wrong.

  4. Fast resize of a mmap file

  5. Characteristics of mremap function in Linux

  6. mremap function failed to allocate new memory

  7. c/linux - ftruncate and POSIX Shared Memory Segments - rszshm doesn't use mremap() at all. It copy memory instead. The worst way.

I've developed an example as I understand the documentation. Unfortunately it is not working right. Please give me an advise, where I am wrong. And please be so kind as to give me a working example.

In the documentation I have found that I have to use ftruncate() before mremap(), but I couldn't find right syntax for using them. Besides, mremap() works with aligned memory pages. How to properly increase shared memory in this case?

/* main.c */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <errno.h>

int main(void)
{
    size_t size_of_mem = 1024;
    int fd = shm_open("/myregion", O_CREAT | O_RDWR,
                      S_IRWXO | S_IRUSR | S_IWUSR);
    if (fd == -1)
    {
        perror("Error in shm_open");
        return EXIT_FAILURE;
    }

    if (ftruncate(fd, size_of_mem) == -1)
    {
        perror("Error in ftruncate");
        return EXIT_FAILURE;
    }

    void *shm_address = mmap(0, size_of_mem,
                             PROT_READ | PROT_WRITE | PROT_EXEC,
                             MAP_SHARED, fd, 0);
    if (shm_address == MAP_FAILED)
    {
        perror("Error mmapping the file");
        return EXIT_FAILURE;
    }

    /* Increase shard memory */
    for (size_t i=0; i<1024; ++i){

        /* Does 8 align memory page? */
        size_t new_size_of_mem = 1024+(8*i);

        if (ftruncate(fd, new_size_of_mem) == -1)
        {
            perror("Error in ftruncate");
            return EXIT_FAILURE;
        }

        /*
           mremap() works with  aligned memory pages.
           How to properly increase shared memory in this case?
        */
        void *temp = mremap(shm_address, size_of_mem, new_size_of_mem, MREMAP_MAYMOVE);
        if(temp == (void*)-1)
        {
            perror("Error on mremap()");
            return EXIT_FAILURE;
        }

        size_of_mem = new_size_of_mem;

    }

    return 0;
}

Build:

$ gcc -g -O0 -ggdb -pipe -Wall -Wextra -Wpedantic -Wshadow -march=native -std=c11 -o ./main ./main.c -lrt

Run:

$ ./main
Error on mremap(): Bad address
Community
  • 1
  • 1
Dennis V
  • 574
  • 7
  • 19
  • 1
    At least you'll have to measure sizes in terms of pagesize. see `getpagesize()` or `sysconf()`. And, for sharing the memory between processes you'll have to find a way to communicate the new size to the other processes. – joop Nov 03 '16 at 10:53

1 Answers1

9

You are loosing the address of the new allocated/remapped memory assigned to temp.

That means that, since the second cycle of for loop, you are moving an already moved memory.

After the check of mremap returned value you can ressign the new address to the shm_address pointer.

void *temp = mremap(shm_address, size_of_mem, new_size_of_mem, MREMAP_MAYMOVE);
if(temp == (void*)-1)
{
    perror("Error on mremap()");
    return EXIT_FAILURE;
}

shm_address = temp;
LPs
  • 16,045
  • 8
  • 30
  • 61
  • Thank you very much. Now it really works without errors. How about remaining question. mremap() works with aligned memory pages. How to properly increase shared memory in this case? – Dennis V Nov 03 '16 at 10:50
  • 1
    It is a huge question. Post a different question to have a good explanation from some linux managemnt guru. What I can say here is that If the len parameter provided by the caller is not aligned on a page boundary the mapping is rounded up to the next full page. The bytes inside this added memory, between the last valid byte and the end of the mapping, are zero-filled. Any read from that region will return zeros. – LPs Nov 03 '16 at 11:07