I have a Node class which contains two nested classes, Server and Client, as well as pointers to an instance of each.
I perform some init methods to my Node in the parent process but want the nested Server and Client instances to act independently.
My current approach uses shmget, and shmat. I made a non-successful attempt with mmap previously.
#include "../hdr/g04.h"
#include <sys/shm.h>
#include <sys/wait.h>
void fatal(string s) {
cerr << "Error: " << s << " . . .\n";
exit(-1);
}
int main() {
int shmkey = 12345, shmid;
Node **nodeLocation, *thisNode;
pid_t client_pid, server_pid;
// get a shared mem segment
if ((shmid = shmget(shmkey, sizeof(Node *), IPC_CREAT | 0666)) < 0)
fatal("shmget");
if ((nodeLocation = (Node **) shmat(shmid, NULL, 0)) == (Node **) - 1)
fatal("shmat");
// instantiate the Node
// assign shared memory address
thisNode = new Node();
*nodeLocation = thisNode;
// Node configuration / initialization
if (configure(*thisNode) < 0) fatal("configuration");
if (thisNode->read_seeds() < 0) fatal("seed file");
if (thisNode->read_catalogue() < 0) fatal("catalogue");
thisNode->init();
// test
thisNode->server->test = 10;
cout << "parent: " << thisNode
<< "\n\t" << thisNode->server->test << endl;
// create a client operations process
if ((client_pid = fork()) < 0) fatal("fork");
if (client_pid == 0) {
// test
thisNode->server->test = 20;
cout << "client: " << thisNode
<< "\n\t" << thisNode->server->test << endl;
// do client stuff
exit(0);
}
// test
wait(NULL);
cout << "parent: " << thisNode
<< "\n\t" << thisNode->server->test << endl;
// create a server operations process
if ((server_pid = fork()) < 0) fatal("fork");
if (server_pid == 0) {
// test
thisNode->server->test = 30;
cout << "server: " << thisNode
<< "\n\t" << thisNode->server->test << endl;
// do server stuff
exit(0);
}
// test
wait(NULL);
cout << "parent: " << thisNode
<< "\n\t" << thisNode->server->test << endl;
delete thisNode;
return 0;
}
Output:
parent: 0x7f2532683390
10
client: 0x7f2532683390
20
parent: 0x7f2532683390
10
server: 0x7f2532683390
30
parent: 0x7f2532683390
10
Desired output:
parent: 0x7f2532683390
10
client: 0x7f2532683390
20
parent: 0x7f2532683390
20
server: 0x7f2532683390
30
parent: 0x7f2532683390
30
I see that the address of the Node is the same. That is good. But why is the change to the value of 'test' in each child not reflected in the parent process?
My guess is a context switch is occurring, so each process has its own copy that is being loaded into the same address.
How can I change this so that each process accesses the exact same copy of thisNode?
UPDATE:
I tried avoiding a double pointer, and I changed a few minor things. I came across the placement new operator, and thought this would allow me to allocate my object at the address provided by shmat.
#include <sys/shm.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <errno.h>
void fatal(string s) {
if (errno)
perror(s.c_str());
else
cerr << "Error: " << s << " . . .\n";
exit(-1);
}
void test(string proc, Node *thisNode) {
cout << proc << thisNode
<< "\n\t" << thisNode->server->test << endl;
}
int main() {
Node *tmp = new Node();
key_t shmkey;
int shmid;
Node *thisNode, *nodeLocation;
pid_t client_pid, server_pid;
if ((shmkey = ftok("shared_obj.dat", 'R')) == (key_t)-1)
fatal("ftok: ");
if ((shmid = shmget(shmkey, sizeof(*tmp), 0644 | IPC_CREAT)) < 0)
fatal("shmget: ");
if ((nodeLocation = (Node *) shmat(shmid, (void *)0, 0)) < 0)
fatal("shmat: ");
thisNode = new (nodeLocation) Node();
// Node configuration / initialization
if (configure(*thisNode) < 0) fatal("configuration");
if (thisNode->read_seeds() < 0) fatal("seed file");
if (thisNode->read_catalogue() < 0) fatal("catalogue");
thisNode->init();
// test
thisNode->server->test = 10;
test("parent: ", thisNode);
// create a client operations process
if ((client_pid = fork()) < 0) fatal("fork");
if (client_pid == 0) {
thisNode->server->test = 20;
test("client: ", thisNode);
// do client stuff
exit(0);
}
// test
wait(NULL);
test("parent: ", thisNode);
// create a server operations process
if ((server_pid = fork()) < 0) fatal("fork");
if (server_pid == 0) {
thisNode->server->test = 30;
test("server: ", thisNode);
// do server stuff
exit(0);
}
// test
wait(NULL);
test("parent: ", thisNode);
// delete thisNode;
return 0;
}
The program runs smoothly, but I still get the same outputs as before.
I also tried the ftok, shmget, shmat sequence in each child process but that has no effect. Is there a mechanism I can use to enforce my class to allocate all data within a shared memory segment?
I feel like this can be done, but I have no idea how and assume it is probably difficult. Any thoughts?