WinXP - pamięć współdzielona - czy potrzebne są semafory?

0

Witam,

Sam nie jestem jakims wielkim znawca tematu, wiec spytam. Potrzebuje zaimplememtowac komunikacje miedzyprocesowa w Windows XP. Czyli mamy kilka procesow (lub uslug systemowych), ktore przesylaja informacje innym procesom - adresat musi byc okreslony.

Wymyslilem ze zrobie to za pomoca pamieci wspoldzielonej. Jednak z pamiecia wspoldzielona jest taki problem, zeby dwa procesy nie mogly jednoczesnie pisac do tego samego obszaru pamieci. W Linuksie sa od tego semafory. W windows tez sa semafory, muteksy, sekcje krytyczne.

Wiec mam pytanie - czy uzywajac w Windows funkcji takich jak CreateFileMapping, OpenFileMapping, MapViewOfFile nie trzeba uzywac semaforow/sekcji krytycznych? (w przykladzie z MSDN nie sa uzywane, ale to tylko prosta komunikacja miedzy dwoma procesami).

Przyklad z MSDN: http://msdn2.microsoft.com/en-us/library/aa366551(VS.85).aspx

Czy moge bez obawy tego uzywac do komunikacji miedzy wieloma procesami?

0

ogolnie, jesli zalezy Ci na tym, zeby dane sie nie posiekaly - tak, trzeba uzywac miedzyprocesowej synchronizacji. tak samo jak przy watkach

0

czy uzywajac w Windows funkcji takich jak CreateFileMapping, OpenFileMapping, MapViewOfFile nie trzeba uzywac semaforow/sekcji krytycznych?

Do tego co pisał quetzalcoatl dodam jeszcze, że sekcji używasz tylko do synchronizacji wewnątrz jednego procesu. W przypadku międzyprocesowej komunikacji masz eventy/mutexy/semafory.

0

W tej chwili moj kod wyglada mniej wiecej tak:

MMF::MMF(const string& szMMName):m_szMMName(szMMName),m_hMapFile(NULL)
{
 m_pLock = new CIPCReadWriteLock(
        sReadWriteLockName,
        dwMaxWait);

}

MMF::~MMF(void)
{
 CloseMapFile();
 if(m_pLock)
    delete m_pLock;
}
bool MMF::CreateMapFile()
{
	CShareRestrictedSD ShareRestrictedSD;
	if(NULL == m_hMapFile)
	{
		m_hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, 
			ShareRestrictedSD.GetSA(),
			PAGE_READWRITE,
			0,
			1000,
			m_szMMName.c_str());
	}
	

	
	return NULL != m_hMapFile;
}
bool MMF::OpenMapFile()
{
	if(NULL == m_hMapFile)
	{
		m_hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS,
			TRUE,
			m_szMMName.c_str());
	}
	return NULL != m_hMapFile;
}
void MMF::CloseMapFile()
{
	if(m_hMapFile)
	{
		::CloseHandle(m_hMapFile);
		m_hMapFile = NULL;
	}
}
bool MMF::WriteData(const char* szData,int nLen)
{
	LPVOID lpRet = MapViewOfFile(m_hMapFile,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);


	if(lpRet && m_pLock->WaitToWrite())
	{
		memcpy((char*)lpRet,szData,nLen);
		::UnmapViewOfFile(lpRet);
                m_pLock->Done();
		return true;
	}
	else
	{
		return false;
	}
}
bool MMF::ReadData(string& szData)
{
	LPVOID lpRet = MapViewOfFile(m_hMapFile,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);
	if(lpRet && m_pLock->WaitToRead())
	{
		int nCopyLen = strlen((char*)lpRet);
		char *pBuffer = new char[nCopyLen];
		memset(pBuffer,0,nCopyLen);
		memcpy(pBuffer,lpRet,nCopyLen);
		szData = string(pBuffer,nCopyLen);
		::UnmapViewOfFile(lpRet);
		return true;
	}
	else
	{
		return false;
	}
}

Uzyta klasa CIPCReadWriteLock, ktora ma niby zapewniac synchronizacje pochodzi stad: http://69.10.233.10/KB/threads/ipc_tute.aspx

Jak testuje: writer w petli tworzy dwa obiekty wspoldzielone i je usuwa po 500 ms. Reader co 500 ms probuje odczytac i raportuje czy mu sie udalo.

Rezultaty? Bez synchronizacji: Jezeli writer umiesci jakies dane w pamieci, to reader moze je caly czas odczytywac. Kilka readerow - takze. Nawet jezeli writer sprobuje usunac dane z pamieci, to i tak sa one dostepne dla readera. Dopiero jak zamkne readera i uruchomie jeszcze raz, dostepnych danych juz nie ma.

Uzywajac tej klasy do synchronizacji: reader moze odczytac kazda wartosc tylko raz, niezaleznie od wszystkiego.
Natomiast writer moze zapisywac i usuwac zasoby w nieskonczonosc, kilka writerow takze. Ale jezeli rownoczesnie uruchomie readera, to po jednym odczytaniu zasobu przez niego juz ani writer ani reader nie moga nic zrobic. Nawet po zakonczeniu readera uruchomiony writer nie moze nic zapisywac.

Wedlug mnie to pierwszy wariant jest prawidlowy. Reader nie usuwa zasobu, tylko czyta raz za razem to samo.

W praktyce bedzie to wygladalo tak, ze komunikacja bedzie tylko dwustronna. Czyli proces "nasluchuje", czy nie ma dla niego zadnych wiadomosci (obojetnie od kogo), a rozpoznawalby to po nazwie zasobu. Raczej nie przewiduje sytuacji, w ktorej kilka procesow zapisywaloby ten sam zasob roznymi wartosciami. Jezeli nawet cos takiego dzialoby sie rownoczesnie, to zapisywane by byly (lub tworzone) zasoby o roznych nazwach.

0

Synchronizację można rozwiązać analizując zawartość wspólnego
bufora przez odbiorcę i nadawcę , aplikacja czytająca może np. poprzez wyzerowanie
bufora dać sygnał że dane odebrała i czeka na następne .
Bez konieczności tworzenia i niszczenia nowych obiektów w celu przesłania komunikatu .
Taką klasę można wydziabac sobie własnoręcznie .
Co pasuje do :

W praktyce bedzie to wygladalo tak, ze komunikacja bedzie tylko dwustronna. Czyli proces "nasluchuje", czy nie ma dla niego zadnych wiadomosci (obojetnie od kogo), a rozpoznawalby to po nazwie zasobu

...

0
dzejo napisał(a)

Synchronizację można rozwiązać analizując zawartość wspólnego
bufora przez odbiorcę i nadawcę , aplikacja czytająca może np. poprzez wyzerowanie
bufora dać sygnał że dane odebrała i czeka na następne .
Bez konieczności tworzenia i niszczenia nowych obiektów w celu przesłania komunikatu .
Taką klasę można wydziabac sobie własnoręcznie .
Co pasuje do :

W praktyce bedzie to wygladalo tak, ze komunikacja bedzie tylko dwustronna. Czyli proces "nasluchuje", czy nie ma dla niego zadnych wiadomosci (obojetnie od kogo), a rozpoznawalby to po nazwie zasobu

...
No ja raczej zrobiłbym to zwykłym event'em. Zamiast sprawdzać stan bufora (0 lub nie) lepiej poczekać na jeden z eventów funkcją WaitForMultiplyObjects. Cała synchronizacja powinna zmieścić się w tej jednej operacji.

0

adf88: mozesz dokladniej napisac jak to zrobic? Akurat nie mam z tym doswiadczenia - nie chce bladzic po omacku, a chcialbym miec juz z glowy ten problem.

Pytanie jest - patrzac na kod, ktory dalem wyzej - gdzie umiescic tworzenie eventa (CreateEvent?) i gdzie WaitForMultiplyObjects i jak tego uzyc? W tej chwili cale to zagadnienie wydaje mi sie nieco zagmatwane, przyklady w necie jakos niewiele mi mowia jak tego uzyc w moim przypadku.

Cale to CIPCReadWriteLock sobie odpuszczam, bo nie mam sil ani czasu w to wnikac

0

Eventa musi stworzyć jedna ze stron (CreateEvent), druga strona go otworzyć (też CreateEvent).
Eventy identyfikuje się po nazwie. Otwierając eventa możesz zrobić coś takiego:

while(!CreateEvent(...)) Sleep(10);

wtedy proces otworzy go jak już będzie dostępny.

Obiekty do synchronizacji (też eventy) mogą być w stanie signaled lub unsignaled. Funkcja WaitForSingleObject zakończy działanie dopiero, gdy obiekt na który czeka wejdzie w stan signaled. Dla eventów stany te ustawiamy odpowiednio funkcjami SetEvent, ResetEvent.

Tworząc event (CreateEvent) parametr bInitialState decyduje o początkowym stanie eventa. Parametr bManualReset określa, czy event ma być automatycznie resetowany. Automatyczny reset czyli automatyczne ustawienie eventa w stan unsignaled następuje po odczekaniu na event jedną z funkcji WaitFor...

Tyle chyba ci wystarczy, eventy są najprostsze jeśli chodzi o synchronizację.
Jeśli chcesz czekać na kilka eventów na raz to podaj je w tablicy dla funkcji WaitForMultiplyObjects. Funkcja zwróci indeks pierwszego obiektu jaki wejdzie w stan signaled, lub (zależy to od podanych parametrów) będzie czekać na wszystkie obiekty. Szerszy opis oczywiście na MSDN.

Fajna jest funkcja MsgWaitForMultiplyObjects, która czeka nie tylko na obiekty, ale też na komunikaty jak GetMessage.
MsgWaitForMultiplyObjects można wkomponować w pętle komunikatów tak, że nie zamrozi aplikacji na czas czekania na event czy inny obiekt.
A obiektów na które można czekać jest mnóstwo, od evenów, sekcji krytycznych, muteksów i semaforów, przez wątki i procesy po strumienie i inne duperele, wystarczy podać funkcji WaitFor uchwyt do owego obiektu. Np. proces lub wątek wejdzie w stan signaled jak się zakończy. Strumień wejdzie w stan signaled jak pojawią się w nim znaki itp. A właśnie, zamiast pamięci współdzielonej może strumień (CreatePipe) ? Jedne dane czyta jeden proces czy kilka ?

0

Ok ale skad mam wiedziec, czy tworze, czy otwieram event? Tzn, jak stworzyc nowy, a jak otworzyc istniejacy event, skoro robi sie to tak samo?

Patrzylem, na ten przyklad: http://msdn2.microsoft.com/en-us/library/ms686915(VS.85).aspx

Tyle ze to dotyczy watkow w obrebie jednego procesu i jest globalna tablica eventow podawana do funkcji WaitForMultipleObjects, wiec nie ma problemu.

Jezeli mam kilka roznych procesow, to skad mam wiedziec ile tych procesow chce uzyskac dostep i jak utworzyc taka tablice eventow?

0

Ok ale skad mam wiedziec, czy tworze, czy otwieram event?

A po co Ci ta informacja? Jeżeli nie ma eventu o danej nazwie, CreateEvent tworzy go, a jeśli jest - otwiera.

0

Chyba jednak prostsze bedzie zerowanie bufora, nie bede sobie zycia utrudniac.

MMF::MMF(const string& szMMName):m_szMMName(szMMName),m_hMapFile(NULL)
{
 

}

MMF::~MMF(void)
{
 CloseMapFile();
 
}
bool MMF::CreateMapFile()
{
	CShareRestrictedSD ShareRestrictedSD;
	if(NULL == m_hMapFile)
	{
		m_hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, 
			ShareRestrictedSD.GetSA(),
			PAGE_READWRITE,
			0,
			1000,
			m_szMMName.c_str());
	}


 //Utworz eventy
   ghGlobalWriteEvent = CreateEvent(
        ShareRestrictedSD.GetSA(),
        TRUE,
        FALSE,                    // domyslnie jako nonsignaled
        TEXT("WriteEvent")       // nazwa
        );
   if(ghGlobalWriteEvent!= NULL)
       printf("\nUtworzono obiekt WriteEvent\n");
   else
       printf("\nNie mozna utworzyc obiektu WriteEvent\n");


   ghGlobalReadEvent = CreateEvent(
        ShareRestrictedSD.GetSA(),  // SA
        TRUE,
        FALSE,                    // domyslnie jako nonsignaled
        TEXT("ReadEvent")       // nazwa
        );

   if(ghGlobalReadEvent!= NULL)
       printf("\nUtworzono obiekt ReadEvent\n");
   else
       printf("\nNie mozna utworzyc obiektu ReadEvent\n");
	
  
	return NULL != m_hMapFile;
}
bool MMF::OpenMapFile()
{
	if(NULL == m_hMapFile)
	{
		m_hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS,
			TRUE,
			m_szMMName.c_str());
	}
	return NULL != m_hMapFile;
}
void MMF::CloseMapFile()
{
	if(m_hMapFile)
	{
		::CloseHandle(m_hMapFile);
		m_hMapFile = NULL;
	}
}
bool MMF::WriteData(const char* szData,int nLen)
{
	LPVOID lpRet = MapViewOfFile(m_hMapFile,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);

	if(lpRet)
        {
                if(WaitForSingleObject(ghGlobalReadEvent,INFINITE)==WAIT_OBJECT_0);
                   printf("***ReadEvent ma stan signaled, moge zapisywac\n");


		memcpy((char*)lpRet,szData,nLen);
		::UnmapViewOfFile(lpRet);

                
		return true;
	}
	else
	{
		return false;
	}
}
bool MMF::ReadData(string& szData)
{
	LPVOID lpRet = MapViewOfFile(m_hMapFile,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);
	if(lpRet)
	{       if(WaitForSingleObject(ghGlobalWriteEvent,INFINITE)==WAIT_OBJECT_0);
                   printf("***WriteEvent ma stan signaled, moge odczytywac\n");


		int nCopyLen = strlen((char*)lpRet);
		char *pBuffer = new char[nCopyLen];
		memset(pBuffer,0,nCopyLen);
		memcpy(pBuffer,lpRet,nCopyLen);
		szData = string(pBuffer,nCopyLen);
		::UnmapViewOfFile(lpRet);

                printf("Ustawiam read na signaled\n");
                SetEvent(ghGlobalReadEvent);  //read na signaled

		return true;
	}
	else
	{
		return false;
	}
}

To nie dziala tak jak powinno. Uruchamiam writera - czeka i to jest akurat ok. Gdy writer jest uruchomiony, odpalam readera - dochodzi do punktu "Ustawiam read na signaled" i w tym momencie writer powinien przestac czekac, a jednak sie tak nie dzieje - czeka dalej, mimo ze event read jest ustawiony juz na signaled. Co jest grane?

[edit]
Jednak padlo na sockety jako lepsze rozwiazanie. Tym nie mniej chetnie sie dowiem, dlaczego ten kod nie dzialal jak trzeba.

1 użytkowników online, w tym zalogowanych: 0, gości: 1