【问题标题】:share pointer between processes via shared-memory IPC通过共享内存 IPC 在进程之间共享指针
【发布时间】:2012-12-22 01:45:45
【问题描述】:

我正在尝试通过共享内存在父级和分叉子级之间共享已定义类的指针。

所以在父母的主要我创建了指针

mydata *p;
Reader::GetInstance()->Read(p, i+1);
        pid = fork();
        if (pid == -1){
            cout << "error on fork"<<endl;
        }else if (pid == 0){
            cout << "i will fork now" <<endl;
            const char * path = "./mydatamanager";
            execl (path, "-", (char *)0);
            break;
        }else {
            writer(shmid, p);
        }

作者包含这个

void writer(int shmid , mydata * p)
{
    void *shmaddr;   
    shmaddr = shmat(shmid, (void *)0, 0);
    if((int)shmaddr == -1)
    {   
        perror("Error in attach in writer");
        exit(-1);
    }
    else
    {
        memcpy( shmaddr, p, sizeof(*p) );
    }   
}

我的数据是

    class mydara {
    public:
        int var1;
        int var2;
        int var3;
        int var4;
        int var5;
        int var6;
        char *var7;
mydata (int v2, int v3,char *v7, int v6){
        var2 = v2;
        var3 = v3;
        var7 =new char[128];
        strcpy(var7, v7);
        var6 = v6;
        var4 = 0;
        var5 = 0;
    }
    };

在 mydatamanager 中我以这种方式得到这个指针

void reader(int shmid, mydata *& p)
{
    cout << "in reader" << endl;
    void *shmaddr;

      //sleep(3);

    shmaddr = shmat(shmid, (void *)0, SHM_RDONLY|0644);
    if((int)shmaddr == -1)
    {   
        perror("Error in reader");
        exit(-1);
    }
    else
    {
        cout << "in else "<< endl;
        p = (mydata*) shmaddr;
        cout <<"shared memory address is " <<shmaddr <<endl;
        cout <<"var5 "<< p->var5<< endl;
        cout <<"var2 "<< p->var2<< " match with "<<getpid() << "?" << endl;
        cout <<"var3 "<< p->var3<< endl;
        cout <<"var4 "<< p->var4<< endl;
        cout <<"var7 "<< p->var7<< endl; // the 
        //shmdt(shmaddr);
    }
}

和 mydatamanager main :

int main()
{
    cout << "in main" <<endl;
    int shmid;
    shmid = shmget(IPC_PRIVATE, 4096, IPC_CREAT|0644);
    cout << "in advanced point" <<endl;
    sleep(1);
    mydata * p;
    reader (shmid, p);
    cout << p->var7 <<endl;
    return 0;
}

结果始终为 0。 我如何通过父母和孩子分享这个指针,我的代码哪里出了问题?

【问题讨论】:

  • reader 的转换中使用的类型mydata 的定义是什么?它是 C 结构吗?
  • 危险会出现在罗宾逊身上!!!!!如果您要使用共享内存,我会鼓励您使用 POD(“普通旧数据”)结构,而不是类。而且您不得在一个进程中“new[]”一个数组,并期望它在另一个进程中有效!!!
  • 我没有看到你调用 shmget() 来创建共享内存。您的作者应该执行 memcpy( shmaddr, p, sizeof(*p) ) 之类的操作来将数据复制到共享内存。您的读者可以执行“p = (mydata *) shmaddr;”。共享内存 (var7) 中的指针是个问题。
  • @didierc 它与 writer 中的类相同
  • @brian beuning var7 应该是什么问题?

标签: c++ linux pointers ipc shared-memory


【解决方案1】:
【解决方案2】:

首先,您没有同步任何内容。那么你怎么知道哪个先运行,读者或作者。在新分配的块中,内存必然为零,因此结果为零。

任何共享内存都必须确保读取器至少在写入器完成(至少部分)写入过程之前不会读取。

当心共享类 - 你不能使用虚函数,因为这几乎肯定会做一些超出你预期的事情(崩溃,很可能,但其他选项可用,没有一个特别令人愉快)

【讨论】:

  • 在父主程序中我在分叉进程后编写,然后在进程中我让它休眠 1 秒以确保数据在共享内存中更新。我不在这个类中使用虚函数。
  • 我在您发布的代码中没有看到任何睡眠?你的意思是睡觉可以解决问题吗?如果没有,请发布您的更新代码。
  • 我添加了 mydatamanager 类,我从父级的 execl 中去
【解决方案3】:

处理您的问题的最简单方法是在父进程forkfork 之前创建一个信号量,让子进程尝试在读取之前获取它(而不是执行sleep)并且父进程在写入之后释放它。

首先,创建、销毁和检索信号量 ID 的函数如下:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>


int create_semaphore(const char *path, char id, int count){
    key_t k = ftok(path, id);
    semid = semget(k, 1, IPC_CREAT | IPC_EXCL | 0600);
    semctl(semid, 0, SET_VAL, count);
    return semid;
}

int destroy_semaphore(int semid){
    semctl(semid, 0, IPC_RMID, 0);
}

int get_semaphore(const char *path, char id){
    key_t k = ftok(path, id);
    semid = semget(k, 1, 0600);
    return semid;
}

现在我们需要一个函数来获取它,另一个函数来释放它:

void acquire_semaphore(int semid){
    sembuf op;
    op.sem_num = O;
    op.sem_op = -1;
    op.sem_flg = 0;
    semop(semid,&op,1);
}

void release_semaphore(int semid){
    sembuf op;
    op.sem_num = 0;
    op.sem_op = 1;
    op.sem_flg = 0;
    semop(semid,&op,1);
}

有了这些样板函数,您应该能够同步您的流程。

因此,您需要提供路径和唯一 ID(以简单字符的形式)来创建和标识您的信号量。如果您已经使用ftok 创建您的共享内存 id (shmid),您应该理解这个想法。否则,只需确保两个进程中的两个值相同即可。

在您的编写器代码中,输入以下行:

 semid = create_semaphore(argv[0], 'S', 0);

就在pid = fork(); 行之前,同时创建和获取信号量。

添加行:

 release_semaphore(semid);

writer(shmid, mydata); 指令之后释放信号量。您还需要在范围内的某处声明semid。我使用 writer 程序路径来创建信号量,这是确保没有其他进程已经使用我们的路径的好习惯。唯一的问题是您需要确保读者将使用相同的路径。您可以将该值硬编码在阅读器代码中的某处,或者更好的是,在 execl 参数中从编写器传递它(留作练习)。

假设读者知道path,剩下要做的就是像这样获取信号量:

semid = get_semaphore(path, 'S');
acquire_semaphore(semid);
destroy_semaphore(semid);

阅读器main函数中reader(shmid, mydata);行之前。

正如其他帖子所说,通过共享内存段共享类实例通常是一个非常糟糕的主意。它更安全 传递简单的struct 数据,并在阅读器端重建您的对象(在网上查找序列化和编组以获取更多信息)。

询问您是否对此(未经测试的)代码有问题。

圣诞快乐!

【讨论】: