0

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?

spanishgum
  • 1,030
  • 1
  • 14
  • 29
  • Could [this](http://stackoverflow.com/questions/13274786/how-to-share-memory-between-process-fork) help you ? – tforgione Nov 13 '15 at 21:33

1 Answers1

0

you are not sharing the node objects, each process has its own copy of the node. They are the same at fork time but changes in one process are not reflected in the other process. For that to happen you would have to actually instantiate the node object in the shared memory. You would need to do a 'placement new'

https://isocpp.org/wiki/faq/dtors#placement-new

pm100
  • 48,078
  • 23
  • 82
  • 145
  • Yes, that is what I tried to do with this line `thisNode = new (nodeLocation) Node();`. Does the placement new mechanism need to propagate through my entire class structure so that every nested object created at runtime follows suit? – spanishgum Nov 13 '15 at 21:49
  • I only read your first pass. But your change is not enough - everything you want shared (in the sense that changes in process1 will shown up in process 2) must be in shared memory – pm100 Nov 13 '15 at 21:51
  • So, I suppose my final question would be: How do I "overload" all allocation in my class, so that my shared memory block dynamically expands, and all dynamic objects allocate in that space? – spanishgum Nov 13 '15 at 21:54
  • no easy way - just slog through the code. I personally would not do this. I would work out the specific things that must be shared and structure them to be easily shared. No nested classes for example. – pm100 Nov 13 '15 at 22:04
  • I was afraid that was the case. Well this is a school project, and I'm probably getting carried away with trying to optimize things. Tbh I'm not even sure I need shared memory after fork time. I think I'll take your advice and leave it alone until I absolutely need some form of IPC. Thanks! – spanishgum Nov 13 '15 at 22:19