Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Передача info через общ. обл памяти между процессами

10K
08 января 2010 года
Shalfey
47 / / 10.03.2007
Среда выполнения: Linux

Задание:

Родить 2 процесса-потомка. От родителя передать каждому уникальную строку сообщения вида, предположим, "Hello, my 1 son!" через ОБЩУЮ ОБЛАСТЬ ПАМЯТИ. Для синхронизации этого дела использовать семафоры.


Итак,реализовываем это дело без семафоров. Не очень правильно, но главное - идея:

 
Код:
for(int i = 0; i < NumChildren; i++){
    pid_t pid = fork();
    if(pid > 0){    // мы в родителе
        Записать строку сообщ "Hello, " + toString(i) в общ. обл. памяти.
    }else if(pid == 0){ // мы в потомке
        читать общую обл. памяти.
    }else{  // Ошибка создания процесса
        cout << "Вы все дураки!" << endl;
    }
}


Безусловно, такой подход неверен. Есть куча проблем, например:

1) Первый потомок начал читать общую обл. памяти до того, как родитель туда что-то написал
2) Второй потомок прочитал из общей памяти сообщение для предыдущего потомка, так как родитель не успел записать туда новое сообщение
3) Потомок читает сообщение во время его записи в общую обл памяти и получает кусок норм. сообщ + мусор
4) ...

Для избежания этих проблем я вижу 1 хороший вариант, который и хочу реализовать:

Код:
for(int i = 0; i < NumChildren; i++){
    pid_t pid = fork();
    if(pid > 0){    // мы в родителе
        Заблокировать выполнение только что порожденного потомка
        Записать строку сообщ "Hello, " + toString(i) в общ. обл. памяти.
        Разблокировать выполнение потомка
    }else if(pid == 0){ // мы в потомке
        Ожидать, пока родитель разблокирует выполнение
        Блокировать родителя, чтоб не писал новых сообщений в общ. обл. памяти
        читать общую обл. памяти.
        разблокировать родителя
    }else{  // Ошибка создания процесса
        cout << "Вы все дураки!" << endl;
    }
}


Теперь вопрос: как это дело реализовать на семафорах? Не надо ничего расписывать, нужна сама идея, приведите в контексте этого псевдокода базовые операции с семафором.

Возможные трудности: Смущает использование 1 семафора для родителя и потомка. Не знаю, как они наследуются, т.п.

Предложите решение, пожалуйста. У меня как-то не укладывается в голове эта абстракция, дальнейшего продвижения нет.

Застрял. Подсобите маленько, пожалуйста.
602
08 января 2010 года
KPI Student
265 / / 16.12.2006
Думаю, что достаточно сделать так:

Родитель:
- инициализирует семафор dataReady количества готовых данных нулем.
- инициализирует двоичный семафор dataWait, сигнализирующий о том, что данные нужно записать в общую область, единицей
- порождает потомков
- выполняет цикл передачи:
 
Код:
while (!isTermainateRequested)
{
   dataWait.decrement(); // как только что-то сигнаизирует о том, что нужно записать новую порцию данных, этот семафор станет больше единицы, система сделает ему декремент и пропустит родителя дальше

   // тут пишем

   dataReady.increment(); // сигнализируем о том, что готова новая порция данных
}


В свою очередь, дочерние процессы:
- ждет на семафоре dataReady (декремент), система не пропустит потомка дальше, пока что-то не увеличит значение этого семафора (изначально равен нулю)
- как только его пропустило дальше, процесс читает готовые данные
- как только данные прочитаны - можно писать туда новую порцию. О чем и просигнализируем инкрементом семафора dataWait. Как только это будет сделано - родитель снова проснется и запишет новую порцию данных
 
Код:
while (!isTermainateRequested)
{
   dataReady.decrement();

   // тут читаем

   dataWait.increment(); // сигнализируем о том, что пора писать новую порцию данных
}
602
08 января 2010 года
KPI Student
265 / / 16.12.2006
У меня была похожая задача - 2 дочерних процесса пишут в родитель

____
приписка потом: если будешь запускать пример - корректное завершение у меня только по сигналу SIGTERM для процесса main. Если грохнуть его по CTRL-C, система шлет SIGINT, все моментально завершается, и никакие ресурсы IPC освобождены не будут.

Я для корректного завершения открывал вторую консоль, ну а там:
ps -a
kill <main_pid>
10K
13 января 2010 года
Shalfey
47 / / 10.03.2007
Задача решена. Была реализована следующая схема:
Код:
for(int i = 0; i < NumChildren; i++){
    pid_t pid = fork();
    if(pid > 0){    // мы в родителе
        Записать строку сообщ "Hello, " + toString(i) в общ. обл. памяти.
        post в sem1 - семафорим потомку готовность данных
        wait sem2 - ждём маяка потомка о прочтении данных
    }else if(pid == 0){    // мы в потомке
        wait sem1 - ждём пока родитель отсемафорит готовность данных
        читать общую обл. памяти.
        post sem2 - семафорим родителю о факте прочтения данных
    }else{    // Ошибка создания процесса
        cout << "fork failed!" << endl;
    }
}


Привожу непосредственно реализацию:
main.cpp
Код:
#include "head.h"
int main(){
    cout<<flush<<"Starting of programm! Parent's process is running\n"<<flush;
    /* get program filename */
    char EXE_NAME_LINK[] = "/proc/self/exe";
    char EXE_NAME[256];
    memset(EXE_NAME, 0, 256);
    readlink(EXE_NAME_LINK, EXE_NAME, 256);
    printf("[inf] exe_name = %s\n", EXE_NAME);

    /* generate IPC key*/
    key_t IPC_KEY = ftok(EXE_NAME, MAGIC_NUMBER);
    printf("[inf] IPC_KEY = %d\n", IPC_KEY);
    // Получаем идентификатор общей области памяти
    int IDmem = shmget(IPC_KEY, MAX_STR_LENGTH, IPC_CREAT|0x1ff);
    // Получаем указатель на общую область памяти
    char* mem = (char*) shmat(IDmem, 0, 0);
    /* create semaphore */
    int IDsemParentReady = binary_semaphore_allocation(IPC_KEY, IPC_CREAT|0x1ff);
    int IDsemChildReady = binary_semaphore_allocation(IPC_KEY+1, IPC_CREAT|0x1ff);
    pid_t child[NumChildren];
    for(unsigned int i = 0; i < NumChildren; i++){
        string message = "";
        child = fork();
        if(child > 0){  //находимся в процессе родителя
            message = "Hello, son " +toString(i)+"! My parent's PID is " + toString(getppid()) +
                    ", my own PID is " + toString(getpid()) +
                    ", your PID is " + toString(child) + "\n";
            // Тут надо записать сообщение в общую область памяти
            strcpy(mem, message.c_str());
            binary_semaphore_post(IDsemParentReady);
            binary_semaphore_wait(IDsemChildReady);
            // Записали в общую область памяти сообщение для процесса
        }else if(child == 0){ // находимся в процессе дитя
            binary_semaphore_wait(IDsemParentReady);
            // Read shared memory
            cout<<flush<<"Child's process # "<< i << " is running."
                <<" Information from parent:\n" << flush;
            char msg[MAX_STR_LENGTH];
            strcpy(msg, mem);
            write(1, msg, strlen(msg));
            binary_semaphore_post(IDsemChildReady);
            exit(0);
        }
        else
            cout << flush<<"Error during creating process\n"<<flush;
    }
    cout<<flush<<"Waiting for finishing all children's processes.\n" << flush;
    for(unsigned int j = 0; j < NumChildren; j++){
        waitpid(child[j], NULL, 0);
    }
    binary_semaphore_deallocate(IDsemParentReady);
    binary_semaphore_deallocate(IDsemChildReady);
    shmdt(mem);
    // Удалили область отображения в локальном адресном пространстве
    shmctl(IDmem, IPC_RMID, 0);
    // Удалили блок разделяемой памяти
    cout<<flush<<"All children's processes are finished. Parent's process  is terminating.\n"<<flush;
    return EXIT_SUCCESS;
}

BinSemaphore.cpp
Код:
#include "BinSemaphore.h"
/* Получить идентификатор двоичного семафора, выделяя его в случае  необходимости. */
int binary_semaphore_allocation(key_t key, int sem_flags){
    return semget (key, 1, sem_flags);
}
/* Освобождаем двоичной семафор. Все пользователи должны закончить использование семафора
        к этому моменту.  Возвращает -1 при ошибке. */
int binary_semaphore_deallocate(int semid){
    union semun ignored_argument;
    return semctl (semid, 1, IPC_RMID, ignored_argument);
}
/* обслужить двойной  семафор. блокировка, пока   значение  семафора  положительное, заетм  декремент на 1. */
int binary_semaphore_wait(int semid){
    struct sembuf operations[1];
    /* использовать первый  (и только) семафор. */
    operations[0].sem_num = 0;
    /*  декремент 1. */
    operations[0].sem_op = -1;
    /*  разрешается отмена операции*/
    operations[0].sem_flg = SEM_UNDO;
    return semop (semid, operations, 1);
}
/* пост к двоичному  семафору: увеличиваем его значение на 1. это выполнится немедленно. */
int binary_semaphore_post(int semid){
    struct sembuf operations[1];
    /* используем  первый семафор. */
    operations[0].sem_num = 0;
    /* increment by 1. */
    /* приращение на 1. */
    operations[0].sem_op = 1;
    /*разрешаем отмену операции. */
    operations[0].sem_flg = SEM_UNDO;
    return semop (semid, operations, 1);
}

BinSemaphore.h
Код:
#ifndef _BINSEMAPHORE_H
#define _BINSEMAPHORE_H
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
/* Мы должны определить  union semun  самостоятельно.  */
union semun {
    int val;
    struct semid_ds *buf;
    unsigned short int *array;
    struct seminfo *__buf;
};
int binary_semaphore_allocation (key_t key, int sem_flags);
int binary_semaphore_deallocate (int semid);
int binary_semaphore_wait (int semid);
int binary_semaphore_post (int semid);
#endif  /* _BINSEMAPHORE_H */

head.h
Код:
#ifndef _HEAD_H
#define _HEAD_H
#include <sys/types.h>
#include <sys/wait.h>
#include "BinSemaphore.h"
#include <sys/ipc.h>
#include <cstdlib>
#include <sys/sem.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <cstdio>
#include <sstream>
#include <iostream>
using namespace std;
extern int errno;
const int MAGIC_NUMBER = 777;
const int MAX_STR_LENGTH = 255;
const int NumChildren = 2;
template <typename T>
std::string toString(T val)
{
    std::ostringstream oss;
    oss<< val;
    return oss.str();
}
template<typename T>
T fromString(const std::string& s)
{
  std::istringstream iss(s);
  T res;
  iss >> res;
  return res;
}
/* Пример использования
std::string str;
int iVal;
float fVal;
str = toString(iVal);
str = tiString(fVal);
*/
#endif  /* _HEAD_H */
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог