这段代码是否正确?

时间:2014-02-04 15:27:39

标签: c++ c fork shared-memory

我在Linux上用g++ test.c -o test

编译它

我重写了原始示例。 现在让第一个进程等待2秒,(以便process2可以在共享内存上写入),然后我让process1从该内存中读取。这个测试是否正确?

第二个问题:我应该放在哪里:

shmdt(tests[0]); // or 1
shmctl(statesid, IPC_RMID, 0); 

//Global scope
char *state[2];
//...
//...

struct teststruct {
  int stateid;
  teststruct *next;
  //other things
};

void write(teststruct &t, char* what)
{
  strcpy(state[t.next->stateid], what);   
  printf("\n\nI am (%d), I wrote on: %d", t.stateid, t.next->stateid); 
}

void read(teststruct &t)
{
  printf("\n\nI am (%d), I read: **%s**", t.stateid, state[t.stateid]);        
}


int main() {

  key_t key;
  if ((key = ftok(".", 'a')) == -1) {
    perror("ftok");
    exit(1);
  }

   int statesid;
  if ((statesid = shmget(key, sizeof(char*)*50, 0600 | IPC_CREAT )) == -1) {
    perror("shmget error");
    exit(1);
  }

  state[0] = (char*)shmat(statesid, NULL, 0);
  state[1] = (char*)shmat(statesid, NULL, 0);

  teststruct tests[2];
  tests[0].stateid = 0;
  tests[0].next = &tests[1];
  tests[1].stateid = 1;
  tests[1].next = &tests[0];

  int t0, t1;
  switch (t0 = fork()) {
  case (0):
    sleep(2);
    read(tests[0]);

    exit(0);

  case (-1):
    printf("\nError!");
    exit(-1);

  default:
    wait();
  }

   switch (t1 = fork()) {
  case (0):
    write(tests[1], "1 write on 0 in theory.");

    exit(0);

  case (-1):
    printf("\nError!");
    exit(-1);

  default:
    wait();
  }

  return 0;
}

特别是我在问两个过程之间是否真的共享“状态”,如果我所做的是一个很好的方法。

我的目标是在fork之后使两个进程之间共享(读取/修改)char *state[2]

3 个答案:

答案 0 :(得分:2)

您无需再拨打shmat()两次。您只为两个指针分配了足够的空间,因此您无法在两个进程之间进行多次通信。而且你不能依赖于能够在第一个进程中将指向内存的指针复制到共享内存中,然后让第二个进程读取并使用它。地址可能在第一个过程中有效,而在第二个过程中无效;它可能指向第二个过程中完全不同的数据(特别是动态内存分配可能会搞砸了)。您只能依赖共享内存的内容在两个进程中都是相同的。您应该分配足够的共享内存来保存共享数据。

然而,话虽如此,这两个进程应该共享那一小块共享内存,在这两个进程中,state[0]state[1]将指向共享内存,你应该能够通过在共享存储器中写入来在两者之间进行通信。请注意,在分叉之后,如果任一进程更改了其state[0]state[1]中存储的值,则其他进程将不会看到该更改 - 另一个进程只能看到共享内存中那些指针指向哪些更改到。

当然,您没有设置任何同步机制,因此访问可能会很混乱。

  

如何修改我的代码只是为了让它按预期工作(不考虑同步问题)?

目前还不完全清楚它是如何工作的,这使得回答问题变得复杂。但是,如果您希望(为了示例)子进程将一个单词写入共享内存并且父进程从共享内存中读取该单词,那么您为您愿意处理的最大单词分配足够的共享内存然后安排孩子将一个单词从每个进程内存复制到共享内存中(并通知父进程已经这样做了),然后父进程可以从共享内存中复制或读取该单词并将其与数据进行比较来自每个进程的记忆。

因为您有一个父子进程,它们是同一进程的分支,您会发现这两个进程共享许多包含相同信息的相同内存地址。然而,这是巧合。您可以将不相关的进程连接到共享内存,并且它们不需要具有任何共同的地址。因此,从当前设置中获得虚假结果将是微不足道的。


工作代码

对于'working'的一些定义,以下C ++代码可以。代码巧妙地是C ++;代码假定struct teststruct声明类型teststruct,并使用引用作为参数。

请注意,问题中的(修订版)代码会无法放置wait()次调用。

shm2.cpp

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>

static char *state = 0;

struct teststruct
{
    int stateid;
    teststruct *next;
};

void sm_write(teststruct &t, char* /*what*/)
{
    //strcpy(state[t.next->stateid], what);   
    printf("[%5d] I am (%d), I wrote on: %d\n", (int)getpid(), t.stateid, t.next->stateid); 
}

void sm_read(teststruct &t)
{
    printf("[%5d] I am (%d), I read: **%s**\n", (int)getpid(), t.stateid, state);        
}

int main(void)
{
    key_t key;
    if ((key = ftok(".", 'a')) == -1) {
        perror("ftok");
        exit(1);
    }

    int statesid;
    if ((statesid = shmget(key, sizeof(char)*512, 0600 | IPC_CREAT )) == -1) {
        perror("shmget error");
        exit(1);
    }

    if ((state = (char*)shmat(statesid, NULL, 0)) == 0)
    {
        perror("shmat");
        exit(1);
    }

    sprintf(state, "This is a string in shared memory %d", 919);

    teststruct tests[2];
    tests[0].stateid = 0;
    tests[0].next = &tests[1];
    tests[1].stateid = 0;
    tests[1].next = &tests[0];

    int t0, t1;
    if ((t0 = fork()) < 0)
    {
        perror("fork-1");
        exit(1);
    }
    else if (t0 == 0)
    {
            sm_read(tests[0]);
            printf("[%5d] sleeping\n", (int)getpid());
            sleep(2);
            printf("[%5d] waking\n", (int)getpid());
            sm_read(tests[0]);
            exit(0);
    }
    else if ((t1 = fork()) < 0)
    {
        perror("fork-2");
        exit(-1);
    }
    else if (t1 == 0)
    {
        printf("[%5d] sleeping\n", (int)getpid());
        sleep(1);
        printf("[%5d] waking\n", (int)getpid());
        strcpy(state, "1 write on 0 in theory.");
        sm_write(tests[1], state);
        exit(0);
    }

    int corpse;
    int status;
    while ((corpse = wait(&status)) > 0)
        printf("PID %5d died with status 0x%.4X\n", corpse, status);

    return 0;
}

运行示例

[20440] sleeping
[20440] waking
[20440] I am (0), I wrote on: 0
[20439] I am (0), I read: **This is a string in shared memory 919**
[20439] sleeping
[20439] waking
[20439] I am (0), I read: **1 write on 0 in theory.**
PID 20440 died with status 0x0000
PID 20439 died with status 0x0000

答案 1 :(得分:1)

共享内存的大小有问题。在:

 (statesid = shmget(key, sizeof(char*)*2, 0600 | IPC_CREAT )

你只需要预留2个指向char的空间。您需要为所有数据分配足够的空间,基于结构是一种链接结构。代码可能类似于以下内容,虽然fork()和共享内存的目的对我来说不是很清楚:

struct teststruct {
    int stateid;
    teststruct *next;
    //other things
};

void dosomething(teststruct &t){
    //forget about global space, you don't need it
}

int main() {

    key_t key;
    if ((key = ftok(".", 'a')) == -1) {
        perror("ftok");
        exit(1);
    }

    int statesid;
    int size_struct = sizeof(teststruct)*2; //assuming you will have only 1 level of linking
    if ((statesid = shmget(key, size_struct, 0600 | IPC_CREAT )) == -1) {
        perror("shmget error");
        exit(1);
    }

    //if you need to hold just one teststruct object data, you can do
    teststruct* p_test_struct = (teststruct*)shmat(statesid, NULL, 0);

    for (int i=0; i<2; i++){
        *p_test_struct = tests[i]; //this actually writes tests[i] into shared mem
        int t0, t1;
        switch (t0 = fork()) {
        case (0):
            dosomething(*p_test_struct);
            exit(0);
        case (-1):
            printf("\nError!");
            exit(-1);
        default:
            wait();
        }
    }

    return 0;
}

答案 2 :(得分:-1)

不,它没有。因为您使用fork(多进程)而不是线程(多线程)。内存区域不会共享到父进程和子进程。您将在孩子身上获得相同的价值但在此之后它将独立于另一个。