Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > Разделяемая память между двумя процессами


Автор: Relrin 30.10.2012, 20:21
Стоит задача в том, чтобы сделать разделяемую память для двух процессов, в котором один процесс записывает данный (пускай какие-то рандомные числа), а второй считывает. Вся эта процедура выводится в консоль с видом типа:
Первый записал: 2
Второй считал: 2
И так далее. Это сделано, но возникла проблемка следущего плана: почему после записи вывод происходит не один раз, а несколько? Быть может кто подскажет?
Исходный код основной программы:

Код

#include <iostream>
#include <windows.h>
#include <conio.h>
#include <stdio.h>
#include <tchar.h>
#include <time.h>
#include "MutexCritSection.h"
using namespace std;
 
HANDLE imutex;
HANDLE hSharedMemory;
MutexCritSection cs;
 
#define SHARED_MEMORY_NAME L"Shared"
#define SHARED_MEMORY_SIZE 100
#define CyclesRW 10
 
int main(int argc, char **argv) 
{
    cs.InitializeCriticalSection(L"SyncMutex");
 
    // сформируем общую память
    if((hSharedMemory=CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, SHARED_MEMORY_SIZE, SHARED_MEMORY_NAME)) == NULL)
    {
        cout << "Error to Mapping file" << endl;
        return -1;
    }
 
    // создадим второй процесс
    STARTUPINFO si = {NULL};
    PROCESS_INFORMATION pi={NULL}; 
    TCHAR commandArgs[]=TEXT("C:\\Code\\Lab_3_OSiSP\\Debug\\Lab_3_OSiSP.exe");
    BOOL secProcess = CreateProcess(NULL, commandArgs , NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
 
    imutex = CreateMutex(NULL, TRUE, L"Instance");
    // Первый процесс (записывает в разделяемую память)
    if(GetLastError() != ERROR_ALREADY_EXISTS)
    {
        TCHAR message[100];
        LPCTSTR shared_data_poiter;
        for(int i = 0; i < CyclesRW; i++)
        {
            cs.Enter();
            int numb=rand();
            wsprintf(message, L"%d", numb);
            wcout << "First process write: " << message << endl;
            shared_data_poiter = (LPCTSTR)MapViewOfFile(hSharedMemory, FILE_MAP_ALL_ACCESS, 0, 0, SHARED_MEMORY_SIZE);
            CopyMemory((PVOID)shared_data_poiter, message, 100 * sizeof(wchar_t));
            Sleep(500);
            cs.Leave();
        }
    }
    // Второй процесс (читает из разделяемой памяти)
    else
    {
        TCHAR message[100];
        LPCTSTR shared_data_poiter;
        for(int i = 0; i < CyclesRW; i++)
        {
            cs.Enter();
            int numb=0;
            wsprintf(message, L"Process %d", numb);
            shared_data_poiter = (LPCTSTR)MapViewOfFile(hSharedMemory, FILE_MAP_ALL_ACCESS, 0, 0, SHARED_MEMORY_SIZE);
            cout << "Second proress read data: ";
            wcout << shared_data_poiter << endl;
            cs.Leave();
        }
        return 0;
    }
 
    _getch();
    CloseHandle(hSharedMemory);
    CloseHandle(imutex);
    return 0;
}


Код модуля для работы с мьютексом:
Код

#include "MutexCritSection.h"
 
MutexCritSection::MutexCritSection(void)
{
}
 
void MutexCritSection::InitializeCriticalSection(LPCWSTR name)
{
    smutex = CreateMutex(NULL, FALSE, name);
}
 
void MutexCritSection::Enter()
{
    WaitForSingleObject(smutex, INFINITE);
}
 
void MutexCritSection::Leave()
{
    ReleaseMutex(smutex);
}
 
MutexCritSection::~MutexCritSection(void)
{
    CloseHandle(smutex);
}

Автор: feodorv 30.10.2012, 20:39
Если честно, то жуть пробирает(((
Зачем на каждом шаге цикла делать 
Цитата(Relrin @  30.10.2012,  21:21 Найти цитируемый пост)
            shared_data_poiter = (LPCTSTR)MapViewOfFile(hSharedMemory, FILE_MAP_ALL_ACCESS, 0, 0, SHARED_MEMORY_SIZE);

Между прочем, каждому MapViewOfFile должен соответствовать свой UnmapViewOfFile. Бог с ним. Но зачем Вам столько представлений? Достаточного одного...
Куча мьютексов, и ни один не защищает разделяемую память(((

Автор: Relrin 30.10.2012, 20:54
Цитата

Зачем на каждом шаге цикла делать 
   
Код

shared_data_poiter = (LPCTSTR)MapViewOfFile(hSharedMemory, FILE_MAP_ALL_ACCESS, 0, 0, SHARED_MEMORY_SIZE);

Между прочем, каждому MapViewOfFile должен соответствовать свой UnmapViewOfFile. Бог с ним. Но зачем Вам столько представлений? Достаточного одного...


Вот так лучше сделать получается? Хотя смысла, определенно, согласушь тут с вами, не было:
Код

    // первый процесс (записывает в разделяемую память)
    if(GetLastError() != ERROR_ALREADY_EXISTS)
    {
        TCHAR message[100];
        LPCTSTR shared_data_poiter = (LPCTSTR)MapViewOfFile(hSharedMemory, FILE_MAP_ALL_ACCESS, 0, 0, SHARED_MEMORY_SIZE);
        for(int i = 0; i < CyclesRW; i++)
        {
            cs.Enter();
            int numb=rand();
            wsprintf(message, L"%d", numb);
            wcout << "First process write: " << message << endl;
            CopyMemory((PVOID)shared_data_poiter, message, 100 * sizeof(wchar_t));
            Sleep(500);
            cs.Leave();
        }
        UnmapViewOfFile(shared_data_poiter);
    }
    // второй процесс (читает из разделяемой памяти)
    else
    {
        TCHAR message[100];
        LPCTSTR shared_data_poiter = (LPCTSTR)MapViewOfFile(hSharedMemory, FILE_MAP_ALL_ACCESS, 0, 0, SHARED_MEMORY_SIZE);
        for(int i = 0; i < CyclesRW; i++)
        {
            cs.Enter();
            int numb=0;
            wsprintf(message, L"Process %d", numb);
            cout << "Second proress read: ";
            wcout << shared_data_poiter << endl;    
            cs.Leave();
        }
        UnmapViewOfFile(shared_data_poiter);
        return 0;
    }

Но меня до сих пор мучает вопрос: Почему читает из памяти N раз данные, хотя в цикле ограничивает чтение мьютекс?

Автор: Dem_max 31.10.2012, 05:14
Мьютекс должен быть до MapViewOfFile и освобождение мьютекса после UnmapViewOfFile

Автор: Relrin 31.10.2012, 07:03
Цитата(Dem_max @ 31.10.2012,  05:14)
Мьютекс должен быть до MapViewOfFile и освобождение мьютекса после UnmapViewOfFile

Сделал вот-так, но эффект получается как и ранее(запись один раз, считывание N раз):

Код

    if(GetLastError() != ERROR_ALREADY_EXISTS)
    {
        TCHAR message[100];
        for(int i = 0; i < CyclesRW; i++)
        {
            cs.Enter();
            int numb=rand();
            LPCTSTR shared_data_poiter = (LPCTSTR)MapViewOfFile(hSharedMemory, FILE_MAP_ALL_ACCESS, 0, 0, SHARED_MEMORY_SIZE);
            wsprintf(message, L"%d", numb);
            wcout << "First process write: " << message << endl;
            CopyMemory((PVOID)shared_data_poiter, message, 100 * sizeof(wchar_t));
            Sleep(500);
            UnmapViewOfFile(shared_data_poiter);
            cs.Leave();
        }
    }
    // второй процесс (читает из разделяемой памяти)
    else
    {
        TCHAR message[100];
        for(int i = 0; i < CyclesRW; i++)
        {
            cs.Enter();
            int numb=0;
            LPCTSTR shared_data_poiter = (LPCTSTR)MapViewOfFile(hSharedMemory, FILE_MAP_ALL_ACCESS, 0, 0, SHARED_MEMORY_SIZE);
            wsprintf(message, L"Process %d", numb);    
            cout << "Second proress read: ";
            wcout << shared_data_poiter << endl;
            UnmapViewOfFile(shared_data_poiter);
            cs.Leave();
        }
        return 0;
    }

Автор: feodorv 31.10.2012, 07:35
Цитата(Relrin @  30.10.2012,  21:54 Найти цитируемый пост)
Почему читает из памяти N раз данные, хотя в цикле ограничивает чтение мьютекс?

Мьютекс только защищает данные, но никак не ограничивает число шагов цикла:

Цитата(Relrin @  30.10.2012,  21:54 Найти цитируемый пост)
    // второй процесс (читает из разделяемой памяти)
...
        for(int i = 0; i < CyclesRW; i++) ...
...



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

Первый процесс создаёт такое событие в сброшенном виде CreateEvent( NULL, TRUE, FALSE, EVENT_NAME), второй - пытается его открыть OpenEvent(). Эти блоки кода стоит защитить разделяемым мьютексом (первоначально, при создании, занятым первым процессом и освобождаемым лишь после создания события), чтобы первый процесс точно создал событие до того, как второй будет пытаться его открыть.

Тогда первый процесс пишет данные и по окончании записи сигнализирует второму SetEvent, что данные есть. Второй процесс сидит на этом событии WaitForSingleObject и ждёт, пока оно освободится, тут же переводя его в сброшенное состояние ResetEvent. И, конечно, операции чтения-записи в разделяемую память должны быть защищены мьютексом, как Вы и сделали)))

Добавлено через 7 минут и 13 секунд
А что это за процесс создаётся путём CreateProcess?

Автор: Relrin 31.10.2012, 13:04
Сделал вот так, за исключением формирования мьютекса, необходимого для создания процесса:
Код

    if(GetLastError() != ERROR_ALREADY_EXISTS)
    {
        TCHAR message[100];
        HANDLE hEvent=CreateEvent(NULL, TRUE, FALSE, L"MyEvent");
        LPCTSTR shared_data_poiter = (LPCTSTR)MapViewOfFile(hSharedMemory, FILE_MAP_ALL_ACCESS, 0, 0, SHARED_MEMORY_SIZE);
        for(int i = 0; i < CyclesRW; i++)
        {
            cs.Enter();
            wsprintf(message, L"%d", i);
            wcout << "First process write: " << message << endl;
            CopyMemory((PVOID)shared_data_poiter, message, 100 * sizeof(wchar_t));
            Sleep(500);
            SetEvent(hEvent);
            cs.Leave();
        }
        UnmapViewOfFile(shared_data_poiter);
    }
    // второй процесс (читает из разделяемой памяти)
    else
    {
        TCHAR message[100];
        HANDLE hEvent=OpenEvent(NULL,FALSE,L"MyEvent");
        LPCTSTR shared_data_poiter = (LPCTSTR)MapViewOfFile(hSharedMemory, FILE_MAP_ALL_ACCESS, 0, 0, SHARED_MEMORY_SIZE);
        for(int i = 0; i < CyclesRW; i++)
        {
            WaitForSingleObject(hEvent,INFINITE);
            cs.Enter();
            ResetEvent(hEvent);
            int numb=0;
            wsprintf(message, L"%d", numb);    
            cout << "Second proress read: ";
            wcout << shared_data_poiter << endl;
            cs.Leave();
        }
        UnmapViewOfFile(shared_data_poiter);
        return 0;
    }


Цитата

А что это за процесс создаётся путём CreateProcess?

Там создается процесс, аналогичной этой программе. С того же места потом процесс работает парно с родительским, где один записывает, второй считывает информацию. 

Цитата

Мьютекс только защищает данные, но никак не ограничивает число шагов цикла:

Это само собой понятно. Я действительно просто не могу понять одной простой вещи: почему запись и чтение работает корректно(вроде как), а вывод считанных данных происходит неоднократно? Хотя вывод дан один раз. В цикле все это находится, т.к. я хочу сделать 10 циклов записи-чтения в память. После того, как второй процесс считал данные из памяти, он выходит из мьютекса, первый на его место заходит, чтобы записать данные. То есть, по мере того, пока первый пишет, второй не должен что-либо делать, выводить, т.е. просто тупо ждать как события, так и мьютекса. То есть, в идеале я хочу видеть, чтобы в консоль выводило примерно так:
Цитата

First process write: 15
Second proress read: 15
First process write: 34
Second proress read: 34
First process write: 134566
Second proress read: 134566
First process write: 4657
Second proress read: 4657
и так далее


А получается:
Цитата

First process write: 15
Second proress read: 15
Second proress read: 15
Second proress read: 15
First process write: 34
Second proress read: 34
Second proress read: 34
Second proress read: 34
Second proress read: 34
Second proress read: 34

Автор: feodorv 31.10.2012, 13:50
Цитата(Relrin @  31.10.2012,  14:04 Найти цитируемый пост)
просто тупо ждать как события, так и мьютекса.

Вы уверены, что второй процесс ждёт события? А что если вызов OpenEvent вернул NULL? Где обработка ошибок?


Цитата(Relrin @  31.10.2012,  14:04 Найти цитируемый пост)
            Sleep(500);
            SetEvent(hEvent);
            cs.Leave();

Здесь неправильная последовательность вызовов. Нужно так:
Код

            SetEvent(hEvent);
            cs.Leave();
            Sleep(500);



Поставьте временные метки, когда печатаете прочтено/записано через GetTickCount. Проанализируйте вывод.


К сожалению, пока нет времени вникать в тонкости происходящего...

Автор: Relrin 31.10.2012, 14:13
Сделал вот так теперь:

Код

imutex = CreateMutex(NULL, TRUE, L"Instance");
    // первый процесс (записывает в разделяемую память)
    if(GetLastError() != ERROR_ALREADY_EXISTS)
    {
        TCHAR message[100];
        HANDLE hEvent=CreateEvent(NULL, TRUE, FALSE, L"MyEvent");
        LPCTSTR shared_data_poiter = (LPCTSTR)MapViewOfFile(hSharedMemory, FILE_MAP_ALL_ACCESS, 0, 0, SHARED_MEMORY_SIZE);
        for(int i = 0; i < CyclesRW; i++)
        {
            cs.Enter();
            int numb=rand();
            wsprintf(message, L"%d", numb);
            wcout << "First process write: " << message << endl;
            CopyMemory((PVOID)shared_data_poiter, message, 100 * sizeof(wchar_t));
            SetEvent(hEvent);
            cs.Leave();
            Sleep(500);
        }
        UnmapViewOfFile(shared_data_poiter);
    }
    // второй процесс (читает из разделяемой памяти)
    else
    {
        TCHAR message[100];
        HANDLE hEvent=OpenEvent(EVENT_MODIFY_STATE,FALSE,L"MyEvent");
        if(hEvent==NULL)
        {
            cout<<"Event doesn't opened!"<<endl;
            return 0;
        }
        LPCTSTR shared_data_poiter = (LPCTSTR)MapViewOfFile(hSharedMemory, FILE_MAP_ALL_ACCESS, 0, 0, SHARED_MEMORY_SIZE);
        for(int i = 0; i < CyclesRW; i++)
        {
            WaitForSingleObject(hEvent,INFINITE);
            cs.Enter();
            ResetEvent(hEvent);
            int numb=0;
            wsprintf(message, L"%d", numb);    
            cout << "Second proress read: ";
            wcout << shared_data_poiter << endl;
            cs.Leave();
        }
        UnmapViewOfFile(shared_data_poiter);
        return 0;
    }


Почитав в интернете, и пройдя проверку с GetTickCount(), оказалось: когда создавался процесс, до всех операций которые будут происходить,  новосозданному процессу подается заново функция main(), т.е. получалось, что плодились дочерние процессы, а следовательно и множественный вывод. Тогда возникает вопрос: а как ограничить создание процессов только одной копией исходной программы?

Автор: Dem_max 31.10.2012, 14:46
Создаешь мьютекс, и проверяешь возвращаемое значение если ошибка то программа уже запущена.

Автор: feodorv 31.10.2012, 14:51
Цитата(Relrin @  31.10.2012,  15:13 Найти цитируемый пост)
плодились дочерние процессы

Это из-за того самого CreateProcess??? Что-то он мне не понравился с самого начала)))


Цитата(Relrin @  31.10.2012,  15:13 Найти цитируемый пост)
Тогда возникает вопрос: а как ограничить создание процессов только одной копией исходной программы?

Вам же нужно два процесса, из них - один дочерний, я правильно понимаю? Тогда создавайте дочерний процесс внутри if'а, проверяющего условие на предсуществование мьютекса:
Код

imutex = CreateMutex(NULL, TRUE, L"Instance");
    // первый процесс (записывает в разделяемую память)
    if(GetLastError() != ERROR_ALREADY_EXISTS)
    {
       CreateProcess(...);
       ....
    }
    // второй процесс (читает из разделяемой памяти)
    else
    {
       ....
    }

Автор: Relrin 31.10.2012, 15:07
Уже как-то получше смотрится, но все равно пока что-то не так идет: После первой записи данных идет чтение,но чтение идет не один раз, а несколько. Затем процесс завершается  smile. А запись еще идет и происходит вывод того, что записано. Другими словами вывод в консоль вот так:

Цитата

First process write: 15
Second process read: 15
Second process read: 15
Second process read: 15
Second process read: 15
Second process read: 15
Second process read: 15
Second process read: 15
First process write: 13456
First process write: 6789
First process write: 234557
First process write: 1677
...

Хотя, в диспетчере задач, я удостоверился, что создается лишь одна копия программы, а не несколько, как было ранее.


Код таков сейчас:
Код

int main(int argc, char **argv) 
{
    cs.InitializeCriticalSection(L"SyncMutex");
    
    // сформируем общую память
    if((hSharedMemory=CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, SHARED_MEMORY_SIZE, SHARED_MEMORY_NAME)) == NULL)
    {
        cout << "Error to Mapping file!!" << endl;
        return -1;
    }

    imutex = CreateMutex(NULL, TRUE, L"Instance");
    // первый процесс (записывает в разделяемую память)
    if(GetLastError() != ERROR_ALREADY_EXISTS)
    {
        // создадим второй процесс
        STARTUPINFO si = {NULL};
        PROCESS_INFORMATION pi={NULL}; 
        TCHAR commandArgs[]=TEXT("Lab_3_OSiSP.exe");
        if(!CreateProcess(NULL, commandArgs , NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
        {
            cout << "Cannot create second process!" << endl;
            return -1;
        } 

        TCHAR message[100];
        HANDLE hEvent=CreateEvent(NULL, TRUE, FALSE, L"MyEvent");
        LPCTSTR shared_data_poiter = (LPCTSTR)MapViewOfFile(hSharedMemory, FILE_MAP_ALL_ACCESS, 0, 0, SHARED_MEMORY_SIZE);
        for(int i = 0; i < CyclesRW; i++)
        {
            cs.Enter();
            int numb=rand();
            wsprintf(message, L"%d", numb);
            wcout << "First process write: " << message << endl;
            CopyMemory((PVOID)shared_data_poiter, message, 100 * sizeof(wchar_t));
            SetEvent(hEvent);
            cs.Leave();
            Sleep(500);
        }
        UnmapViewOfFile(shared_data_poiter);
    }
    // второй процесс (читает из разделяемой памяти)
    else
    {
        TCHAR message[100];
        HANDLE hEvent=OpenEvent(EVENT_MODIFY_STATE,FALSE,L"MyEvent");
        if(hEvent==NULL)
        {
            cout << "Event doesn't opened!" << endl;
            return 0;
        }
        LPCTSTR shared_data_poiter = (LPCTSTR)MapViewOfFile(hSharedMemory, FILE_MAP_ALL_ACCESS, 0, 0, SHARED_MEMORY_SIZE);
        for(int i = 0; i < CyclesRW; i++)
        {
            WaitForSingleObject(hEvent,INFINITE);
            cs.Enter();
            ResetEvent(hEvent);
            int numb=0;
            wsprintf(message, L"%d", numb);    
            cout << "Second proress read: ";
            wcout << shared_data_poiter << endl;
            cs.Leave();
        }
        UnmapViewOfFile(shared_data_poiter);
    }

    _getch();
    CloseHandle(hSharedMemory);
    CloseHandle(imutex);
    return 0;
}


Автор: feodorv 31.10.2012, 15:15
Цитата(Relrin @  31.10.2012,  16:07 Найти цитируемый пост)
       HANDLE hEvent=CreateEvent(NULL, TRUE, FALSE, L"MyEvent");

Вот это нужно делать до CreateProcess smile 

Автор: Relrin 31.10.2012, 15:22
Цитата(feodorv @ 31.10.2012,  15:15)
Цитата(Relrin @  31.10.2012,  16:07 Найти цитируемый пост)
       HANDLE hEvent=CreateEvent(NULL, TRUE, FALSE, L"MyEvent");

Вот это нужно делать до CreateProcess smile

Только что так и сделал. Результат такой же, как и в предыдущем посте.

Добавлено через 2 минуты и 56 секунд
Проблему решил. Переместив Sleep(500), в первом процессе до изменений события.  smile 

Автор: feodorv 31.10.2012, 16:42
Цитата(Relrin @  31.10.2012,  16:22 Найти цитируемый пост)
Проблему решил. Переместив Sleep(500), в первом процессе до изменений события.  

Ну не знаю. Это похоже на гадание, не на программирование. Нужно бы разобраться, почему происходит выход из WaitForSingleObject, сделать контроль ошибок и т.д. Но если и так удовлетворяет, то ладно. Мои поздравления  smile 

Автор: Dem_max 1.11.2012, 06:42
Цитата

 сделать контроль ошибок и т.д

Контроль ошибок для дураков, а не для настоящих программистов.  smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)