How to use shared memory to share an array between processes?

I am currently working on a program that is supposed to simulate a hotel reservation system. I have used fork to create the processes (parent is hotel, and there is one child per customer), and I currently have the input parsing more or less complete. That's the background of my issue.

What I need help with is sharing a couple of arrays between the processes. I currently have 2 boolean arrays, one to keep track of reserved rooms and one to keep track of rooms that have been paid for.
So far I have my program able to correctly change the values within the child process, but the changes to the array do not show outside the child process.
I know that to allow the information to be shared between processes I need to use shared memory, but I've been googling "shared memory with arrays" and things like that for a few days now and not found anything really helpful with my current problem. Everything I find is either not what I need, or they don't explain what they're doing so I don't know how to tweak it to fit my needs.
No one seems to teach/explain shared memory from the beginning.

I just need to know how to have arrays in shared memory and how to access/change them from the different processes.

I am coding on Ubuntu.

I don't know how much it effects this question, but I will also be using mutex semaphores. I'm hoping I'll be able to figure that one out on my own though.
Here's basically what I'm finding so far:
INSIDE WRITER PROGRAM:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void* memory = NULL;
char *p;
int shmid, retval;

shmid = shmget((key_t)123456789,6,IPC_CREAT|0666);

memory = smhat(shmid,NULL,0);

p=(char*)memory;

memset(p,'\0',6);

memcopy(p,"hello",6);

retval = shmdt(p);


INSIDE READER PEOGRAM:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void* memory = NULL;
char *p;
int shmid, retval;

shmid = shmget((key_t)123456789,6,IPC_CREAT|0666);

memory = smhat(shmid,NULL,0);

p= (char*)memory;

printf("\nMESSAGE IS %s \n",p);

retval = shmdt(p);

retval = shmctl(shmid, IPC_RMID, NULL);


These are just snippets from a tutorial I just watched.
From what I've found so far, there's 3 steps:
1. Initialize shared memory
2. attach to shared memory
3. detach from shared memory

The problem I have with this tutorial is that it's between two different cpp files and I need to do this between two processes not two programs.

What I'm understanding from this is that shared memory is just like a little block of memory that different processes can access. shmget() is allocating that block, shmat() is attaching the shared memory to the program/process, and smhdt() is detaching it once the program/process is done.

This is pretty much how all the tutorials I am finding are, but they don't show me how to do this with fork() processes and they don't show me how to put arrays into it.
I mean I see that p is a char and that he puts "hello" into it, so he's making a char array using pointer notation instead of bracket notation, but I just don't see how to tweak that for my needs since I need to be able to check each element of my bool array's and change certain elements.

Shared memory is quite a pain to use. Are you absolutely sure you need to use it? Pipes and sockets are way easier and you don't need to use synchronization primitives with them; the system takes care of synchronization for you. The only downside is that they're a little bit slower than shared memory.
I am not 100% sure that I need to use shared memory, it just seemed like everyone I talked to about this thought it was what should be used. I have used pipes before, but I wasn't sure how to use them for this. And I have heard of sockets before, but I don't know anything about them. I should probably look them up. When you say "synchronization primitives" do you mean the semaphores? Because I am sure that I need to use them for this.

I also found this, but I'm not sure how to tweak it for my needs:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>

void* create_shared_memory(size_t size) {
  // Our memory buffer will be readable and writable:
  int protection = PROT_READ | PROT_WRITE;

  // The buffer will be shared (meaning other processes can access it), but
  // anonymous (meaning third-party processes cannot obtain an address for it),
  // so only this process and its children will be able to use it:
  int visibility = MAP_ANONYMOUS | MAP_SHARED;

  // The remaining parameters to `mmap()` are not important for this use case,
  // but the manpage for `mmap` explains their purpose.
  return mmap(NULL, size, protection, visibility, 0, 0);
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <string.h>
#include <unistd.h>

int main() {
  char* parent_message = "hello";  // parent process will write this message
  char* child_message = "goodbye"; // child process will then write this one

  void* shmem = create_shared_memory(128);

  memcpy(shmem, parent_message, sizeof(parent_message));

  int pid = fork();

  if (pid == 0) {
    printf("Child read: %s\n", shmem);
    memcpy(shmem, child_message, sizeof(child_message));
    printf("Child wrote: %s\n", shmem);

  } else {
    printf("Parent read: %s\n", shmem);
    sleep(1);
    printf("After 1s, parent read: %s\n", shmem);
  }
}


I wonder if I could use an array with it by changing "memcpy(shmem, child_message, sizeof(child_message));" to "memcpy(shmem, array, sizeof(bool)*length of array);"
I am not 100% sure that I need to use shared memory, it just seemed like everyone I talked to about this thought it was what should be used.
If you ask people "how do I give two processes access to the same array?" they'll tell you to use shared memory. But if you ask them "how do I send messages back and forth between a parent and child process", then they're more likely to propose a different solution.
I could be wrong, but it sounds to me like what you really need is a client-server design, where communication might look somewhat like:

Client: I want to reserve a room for one from the 26th to the 28th.
Server: Okay, I have available rooms 203 and 714.
Client: Okay, I'll take 203.
Server: Okay, I've reserved it for you. Bye.
Client: Bye.

A design based around shared memory would be a bit weird, don't you think? When you reserve a room on a hotel you don't grab the reservation book yourself, write whatever you want, then hand it back.

you say "synchronization primitives" do you mean the semaphores?
Semaphores are one kind of synchronization primitive, yes.

Because I am sure that I need to use them for this.
Do you mean that it's a requirement of the solution, or that you believe you need them? In the latter case, no. If you use pipes or sockets you don't need to use semaphores or any other synchronization primitive. The system will take care to synchronize the two processes without you having to do anything special.
The problem I have with this tutorial is that it's between two different cpp files and I need to do this between two processes not two programs.


What do you mean by this? How are a process and a program different?
Do you mean that it's a requirement of the solution, or that you believe you need them?

It's a requirement of the solution.

What do you mean by this? How are a process and a program different?

I guess they aren't, but It's hard for me to think of them as the same thing since I'm not used to trying to run two programs at the same time.

I would suggest to use boost

I've looked into it a bit, but I would prefer to use something that I don't need to install on the computer to run the program, and that's what it looked like I needed to do to use boost.
Last edited on
Here's some code I was just playing with:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int main()
{
    key_t key = 5678;
    int shm_id;
    //int arr[10];
    
    shm_id = shmget(key, 10*sizeof(int), 0666 | IPC_CREAT);
    
    int* arr = (int*)shmat(shm_id, NULL, 0);
    
    arr[0] = 20;
    
    cout << "This is arr[0] before fork: " << arr[0] << endl;
    
    int pid = fork();
    
    if(pid > 0)
    {
        sleep(1);    
    }
    
    if(pid == 0)
    {
        cout << "This is arr[0] in child before change: " << arr[0] << endl;
       arr[0] = 27; 
       cout << "This is arr[0] in child after change: " << arr[0] << endl;
       exit(0);
    }
    
    cout << "This is arr[0] after fork: " << arr[0] << endl;
}


This is the output:
1
2
3
4
This is arr[0] before fork: 20
This is arr[0] in child before change: 20
This is arr[0] in child after change: 27
This is arr[0] after fork: 27


This looks promising to me. Does it look like I could use this for my purposes or will the semaphores cause it to not work? I'm also not sure how to do this with two arrays. Would I just do like "shm_id2 = shmget(key2, 10*sizeof(int), 0666 | IPC_CREAT);"?

I used sleep to make it run in the order I wanted in this test program, but in my actual project I will be using sleep to simulate the delay of each hotel function.

In other words when I get a reserve command, I might have to put sleep(15) in the code.

if (command == "reserve")
{
sleep(15)
do reservation
}

or something like that.

sleep(15) is the example I was given, but it's supposed to be 15 miliseconds not 15 seconds.
Last edited on
So I think I got my original question answered with the above code and some new code I messed with that seems to work even better. It seems to work as long as the child runs before the parent, but that's what the mutex semaphores are for, right?

I'm not sure if this will work for my program as I realized that helios was right about the customer not just grabbing the book and writing whatever they want. I now realize that what I need to do is get the child to pass its command like "reserve (1-4) 8/21/2018 10 Bob deadline 100" to the parent and then the parent processes the command. I was told I should have a pair of mutexes per customer, so I guess I'll do an array of mutex semaphores?

So my original question for this post is answered, but I still have questions about different parts of this program. Should I keep posting to this post, or make a new post since it's a different question? Last time I did that some random guy got on to me for it.

Anyway, here is they new code I was playing with that seems to be better to me and seems to do the job.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>

using namespace std;

int main()
{
  struct Data
  {
      int a;
      int arr[3];
  };
  
  int shm_id;
  key_t key = 1234;
  struct Data *p;
  
  shm_id = shmget(key, sizeof(struct Data), IPC_CREAT | 0666);
  
  p = (struct Data *) shmat(shm_id, NULL, 0);
  
  p->a = 27;
  p->arr[0] = 1;
  p->arr[1] = 2;
  p->arr[2] = 3;
  
  cout << p->a << endl;
  cout << p->arr[0] << endl;
  cout << p->arr[1] << endl;
  cout << p->arr[2] << endl;
  
  int pid = fork();
  
  if(pid > 0)
  {
      sleep(1);
  }
  if(pid == 0)
  {
      p->a = 32;
      p->arr[0] = 4;
      p->arr[1] = 5;
      p->arr[2] = 6;
      exit(0);
  }
  
  cout << p->a << endl;
  cout << p->arr[0] << endl;
  cout << p->arr[1] << endl;
  cout << p->arr[2] << endl;
}

return 0;


output is:
1
2
3
4
5
6
7
8
27
1
2
3
32
4
5
6
Last edited on
Topic archived. No new replies allowed.