6

I wanted to write some function void* share(void*, int) that should set up shared memory to share the data at the pointer.

My first attempt looked like (without checks etc.):

void* share(void *toBeShared, int size) {
    int fd = shm_open(SHM_NAME, O_CREAT | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR);
    ftruncate(fd, size);
    return mmap(toBeShared, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
}

but this does not seem to work as I would like it. The second attempt was something like:

void* share(void *toBeShared, int size) {
    void *mem = NULL;
    int fd = shm_open(SHM_NAME, O_CREAT | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR);
    ftruncate(fd, size);
    mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)
    memcpy(mem, toBeShared, size);
    return mem;
}

and this does work, but I need to copy the entire data, which I would like to avoid.

Therefore my question: is there a way to share memory that has already been allocated (if possible without having to copy too much around) and if yes, how could it be done?

Thanks in advance.

PS: I've seen more of these questions (e.g. here and here), but there are no answers given in there.


edit:

how I would like to use it:

typedef struct {
    char *name;
    int status;
} MyTask;

int main(int argc, char** argv) {
    MyTask* taskList = NULL, sharedTaskList = NULL;
    int length = 0;
    ...
    readFile(&taskList, &length, ...);
    sharedTaskList = share(taskList, length * sizeof(MyTask));
    // or maybe even better: without needing to assign it to new variable
    for(i = 0; i < NR_WORKERS; i++) {
        switch(pid = fork()) {
            //etc...
        }
    }
    ...
    return 0;
}
Community
  • 1
  • 1
  • Possible duplicate of [How to use shared memory with Linux in C](http://stackoverflow.com/questions/5656530/how-to-use-shared-memory-with-linux-in-c) – CodeWarrior101 Jan 31 '16 at 10:25
  • 2
    The answer depends on what the existing mapping is. Is it a file mapping? Is it anonymous? Trying to answer for every possible case will be very tedious. It would help to understand your actual application. It will also be OS-specific because not everything that should work is implemented on every OS. (For example, Linux does not allow you to mmap `/proc/*/mem` for some reason.) – David Schwartz Jan 31 '16 at 10:32
  • @DavidSchwartz it is actually just a simple array with structs. I tried to write my function as general as possible... – Mr Tsjolder from codidact Jan 31 '16 at 10:34
  • @MrTsjolder How was this array allocated? And why wasn't it allocated in shared memory in the first place? Does it contain any pointers? – David Schwartz Jan 31 '16 at 10:35
  • You are not going to be able to do that as there are too many boundary conditions you can't satisfy. – fuz Jan 31 '16 at 10:35
  • Could you please explain why you want to share some *existing* memory zone (usually, you *get* some *shared memory zone*) – Basile Starynkevitch Jan 31 '16 at 10:44
  • @DavidSchwartz I added an example of how I would like to use it. The reason why I do not allocate it in shared memory in the first place, is the fact that between the reading of the file (which also contains other things), I need to do some other things before the workers should start and I like to keep the resources that need to be handled in case of errors as low as possible... Also I don't know how much shared memory I will need before having read the file... – Mr Tsjolder from codidact Jan 31 '16 at 10:47
  • @MrTsjolder I'm kind of baffled by your response. I asked you why you didn't allocate it in shared memory in the first place and you gave a response that has nothing to do with the choice of *where* you allocate it. I'm not saying you should change the point in the flow of execution, just the type of allocation. – David Schwartz Jan 31 '16 at 10:51
  • @DavidSchwartz I'm sorry, I must admit I am not really at home in C and system close programming languages. How is allocation in shared memory exactly done? I only learned about this sequence thus far to set up shared memory... – Mr Tsjolder from codidact Jan 31 '16 at 10:56
  • @MrTsjolder Exactly the way you did it in the code above. – David Schwartz Jan 31 '16 at 11:03
  • @DavidSchwartz How can I use the code above, without knowing how much memory I'll need? – Mr Tsjolder from codidact Jan 31 '16 at 11:06
  • @MrTsjolder: please explain **what exactly your applications are doing**. They obviously cannot share any arbitrary large amount of memory. Memory is a limited resource, and shared memory is much more limited and scarce. – Basile Starynkevitch Jan 31 '16 at 11:10
  • I finally am voting to close your question as unclear (which I also downvoted), because **you did not motivate your question** and did not explain what is your application. Sounds like an [XY problem](http://xyproblem.info/)... – Basile Starynkevitch Jan 31 '16 at 11:19
  • @BasileStarynkevitch My application has a list of tasks to be handled by concurrent processes. The tasks (URLs) are read from a file and I need as much memory as there are URLs in the file, I guess. Or do I only need the memory of the pointer to be shared? – Mr Tsjolder from codidact Jan 31 '16 at 11:22
  • Just have every process read the common file containing the URLs. Perhaps you could put the set of URLs in some database. Your scenario does not look like a good use case for shared memory – Basile Starynkevitch Jan 31 '16 at 11:24
  • If they only need to read the list of tasks, there's nothing special you need to do. Memory is shared after a `fork`, though changes made by one process won't be visible to the other. – David Schwartz Jan 31 '16 at 11:26
  • You are wrong, `fork` does not share memory, it is cloning the virtual address space. Also, your late explaining comments should go into the question (which you should edit to improve it). My feeling is that your entire approach is flawed and your application is probably not a good use case for shared memory (and smells like a good case for using DBMS to manage the set of crawled URLs, or just have a monitoring process communicating with pipes or sockets with web-crawling processes) – Basile Starynkevitch Jan 31 '16 at 11:27
  • @BasileStarynkevitch Unfortunately I was assigned to implement it with shared memory... I eventually solved it by copying the memory (I also changed signature and stepped away from the idea to create my functions as general as possible). Should I close this question as unclear? – Mr Tsjolder from codidact Feb 03 '16 at 08:54

1 Answers1

-4

How to share existing memory?

Don't share existing memory. Get some (small amount of) "fresh" shared memory and use (i.e. fill or read) it later.

Assuming you are on Linux, read shm_overview(7).

I guess that some of your functions might fail. You should test against failure each call, e.g.

int fd = shm_open(SHM_NAME, O_CREAT | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR);
if (fd<0) {perror("shm_open"); exit(EXIT_FAILURE);};

and so on. Perhaps use also strace(1)

is there a way to share memory that has already been allocated

Short answer, no! (or not easily, and not in a portable way). You conventionally do the opposite: obtain some shared segment of known size, and use some pointers into it. (the same shared segment might have different virtual addresses in different processes, e.g. because of ASLR).

You could use mmap(2) with MAP_FIXED on some already used virtual address space subsegment (that would overwrite & replace the mapping with a new one, not share an existing mapping!), but I would suggest to avoid that. Notice that the virtual address space is managed in multiples of pages, so there is no way to share some data which is not page aligned. So your share function is impossible unless both toBeShared and size are page-aligned. You could consider the Linux specific mremap(2)

In other words, your applications should first allocate some shared memory and then put/use some data inside the obtained shared segment, not try to share some existing unshared virtual memory range. So you probably want to code some void* get_my_shared_memory(); (assuming the size is a compile time constant, and you call that function once per process, and its resulting virtual address would often vary from one process to another)

In practice, memory is a finite resource, and shared memory is a scarce and very limited resource. On most systems, you'll be able to share a few dozens of megabytes only... So sharing an arbitrary large amount of memory is unreasonable.

Perhaps your entire application might just use some server, e.g. some database server à la PostGreSQL, to share information, by making requests to that server (and using ACID properties of DBMS). Or you could organize it as a monitoring process exchanging messages (e.g. URL to be processed) -on pipes or sockets or fifos- with slave processes. But we don't know what kind of application are you coding.

BTW, sharing memory is not enough. You need to synchronize your processes.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • 2
    You can't use MAP_FIXED to share an existing mapping. If you use MAP_FIXED, the existing mapping will be discarded. I don't see that you addressed his question at all. (And why should he read shm_overview? What there do you think is relevant to his question?) – David Schwartz Jan 31 '16 at 10:28