Является ли этот кусок кода правильным?

0

Я скомпилировал его на Linux с помощью g++ test.c -o test

Я переписал оригинальный пример. Теперь первый процесс подождал 2 секунды (так что процесс2 мог писать в общей памяти), затем я сделал 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;
}

В частности, я спрашиваю, действительно ли "государство" разделено между двумя процессами, и если то, что я сделал, является хорошим способом сделать это.

Моя цель состоит в том, чтобы сделать char *state[2] общим (чтение/изменение) между двумя процессами после fork.

  • 2
    Я советую вам сначала написать очень простой кусок кода и поиграть с ним, чтобы лучше понять, что такое общая память. В настоящее время эта программа полна ловушек и будет работать только случайно (поскольку разветвленные процессы обычно наследуют одно и то же виртуальное отображение, но нет гарантии, что все равно будет работать в другой операционной системе).
  • 0
    Хорошо спасибо. Мне просто нужно, чтобы он работал на Linux как «упражнение».
Теги:
fork
shared-memory

3 ответа

2
Лучший ответ

Вам не нужно дважды называть shmat(). Вы выделили достаточно места для двух указателей, поэтому вы не можете много общаться между двумя процессами. И вы не можете полагаться на возможность скопировать указатель на память в первом процессе в общую память, а затем прочитать второй процесс и использовать его. Адрес может быть действителен в первом процессе, а не во втором; он может указывать на совершенно разные данные во втором процессе (в частности, распределение динамической памяти могло бы испортить это). Вы можете полагаться только на то, что содержимое разделяемой памяти одинаково в обоих процессах. Вы должны выделить достаточно разделяемой памяти для хранения общих данных.

Однако, с учетом сказанного, два процесса должны делиться этой небольшой частью общей памяти, и в обоих процессах state[0] и state[1] будут указывать на общую память, и вы должны иметь возможность связываться между ними по запись в общей памяти. Обратите внимание, что после форкирования, если какой-либо из процессов изменит значение, сохраненное в его state[0] или state[1], другой процесс не увидит это изменение - другой процесс сможет видеть только, какие изменения в общей памяти указывают эти указатели.

Конечно, вы не настроили какой-либо механизм синхронизации, поэтому доступ, скорее всего, будет хаотичным.

Как я могу изменить свой код, чтобы он работал по назначению (без учета проблем синхронизации)?

Не совсем ясно, как оно должно работать, что затрудняет ответ на этот вопрос. Однако, если вы хотите (для примера) дочерний процесс записать слово в разделяемую память и родительский процесс для чтения слова из разделяемой памяти, тогда вы выделяете достаточно разделяемую память для самого большого слова, которое вы хотите обработать, затем организуйте для ребенка копировать слово из своей памяти в процессе в общую память (и уведомить родителя, что он это сделал), а затем родитель может скопировать или прочитать слово из разделяемой памяти и сравнить его с данными из его памяти для каждого процесса.

Поскольку у вас есть процесс родительский-дочерний процесс, который является вилкой одного и того же процесса, вы обнаружите, что два процесса разделяют много одинаковых адресов памяти, содержащих одну и ту же информацию. Это, однако, случайно. Вы можете подключать несвязанные процессы к общей памяти, и у них нет общих адресов. Таким образом, было бы тривиально получить ложные результаты от вашей текущей настройки.


Рабочий код

Для некоторых определений "рабочий" используется следующий код 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
  • 0
    Как я могу изменить свой код только для того, чтобы он работал как задумано? (Без учета вопросов синхронизации)
  • 1
    Кстати, вызывать программный test как правило, плохая идея. В оболочках есть встроенная команда test , которая может привести к путанице всех видов.
Показать ещё 4 комментария
1

У вас проблема с размером разделяемой памяти. В:

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

вы просто резервируете место для двух указателей на символ. Вам нужно выделить достаточно места для всех ваших данных, что на основе структуры является вид связанной структуры. Код может быть чем-то вроде следующего, хотя цель 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;
}
  • 0
    Цель fork - просто «поиграть» с «char * state [2]», который должен быть правильно разделен (чтение / изменение) между двумя процессами после fork.
-2

Нет. Потому что вы используете fork (многопроцессор) вместо потоков (многопоточность). Зоны памяти не разделяются на родительский и дочерний процессы. У вас будет такое же значение на нем, но после этого он будет независим от другого.

  • 0
    Спасибо, как я могу исправить это с помощью форка?
  • 0
    Вы должны найти способ установить связь между обоими процессами. У вас есть много опций, сокетов, файлов, БД. На мой взгляд, Redis - лучший выбор. Но если вы хотите разрешить это только с C, вы можете попробовать с файлами. Я не знаю, если это лучший вариант в любом случае.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню