在分叉进程之间共享运行时创建的对象

Sharing an object created at runtime between forked processes

我有一个节点 class,它包含两个嵌套的 classes,服务器和客户端,以及指向每个实例的指针。

我在父进程中对我的节点执行一些初始化方法,但希望嵌套的服务器和客户端实例独立运行。

我目前的方法是使用 shmget 和 shmat。 我之前用 mmap 做了一个不成功的尝试。

#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;
}

输出:

parent: 0x7f2532683390
        10
client: 0x7f2532683390
        20
parent: 0x7f2532683390
        10
server: 0x7f2532683390
        30
parent: 0x7f2532683390
        10

期望的输出:

parent: 0x7f2532683390
        10
client: 0x7f2532683390
        20
parent: 0x7f2532683390
        20
server: 0x7f2532683390
        30
parent: 0x7f2532683390
        30

我看到Node的地址是一样的。那很好。但是为什么每个子进程中'test'的值的变化没有反映到父进程中呢?

我的猜测是发生了上下文切换,因此每个进程都有自己的副本被加载到同一地址。

我如何更改它以便每个进程访问 thisNode 的完全相同的副本?


更新:

我尝试避免使用双指针,并更改了一些小的东西。我遇到了 placement new 运算符,并认为这将允许我在 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;
}

程序运行顺利,但我仍然得到与以前相同的输出。

我还在每个子进程中尝试了 ftok、shmget、shmat 序列,但没有效果。有没有一种机制可以用来强制我的 class 分配共享内存段中的所有数据?

我觉得这可以做到,但我不知道如何做,我想这可能很困难。有什么想法吗?

您没有共享节点对象,每个进程都有自己的节点副本。它们在分叉时是相同的,但一个进程中的更改不会反映在另一个进程中。为此,您必须实际实例化共享内存中的节点对象。你需要做一个 'placement new'

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