2011-01-06 16 views
9

Zastanawiam się, jak udostępnić trochę pamięci między różnymi modułami programu - powiedzmy, mam główną aplikację (exe), a następnie jakiś moduł (dll). Obaj łączą się z tą samą biblioteką statyczną. Ta biblioteka statyczna będzie miała jakiegoś menedżera, który zapewnia różne usługi. To, co chciałbym osiągnąć, to udostępnienie tego menedżera między wszystkimi modułami aplikacji i wykonanie tego w sposób przezroczysty podczas inicjowania biblioteki. Pomiędzy procesami mógłbym używać pamięci współdzielonej, ale chcę, aby była ona udostępniana tylko w bieżącym procesie. Czy możesz wymyślić jakiś sposób między platformami, aby to zrobić? Ewentualnie za pomocą bibliotek doładowania, jeśli zapewniają pewne możliwości, aby to zrobić.Udostępnianie pamięci między modułami

Jedyne rozwiązanie, o jakim teraz pomyślę, to korzystanie z biblioteki współdzielonej odpowiedniego systemu operacyjnego, z której wszystkie inne moduły będą łączyły się w czasie wykonywania, i tam zapisano menedżera.

EDIT: Aby wyjaśnić, co tak naprawdę potrzebne:

  1. muszę dowiedzieć się, czy wspólny menedżer został już utworzony (odpowiedzi poniżej już przewidzianych kilka sposobów, aby to zrobić)
  2. Pobierz wskaźnik do menedżera, jeśli istnieje, lub Ustaw wskaźnik gdzieś do nowo utworzonego obiektu zarządzania.
+0

Biorąc pod uwagę, że jest to pojedynczy proces, w jaki sposób różni się on od normalnego wzorca singleton? – sdg

+0

Jest to w rzeczywistości singleton, problem polega na tym, jak go wdrożyć, aby był on dzielony między różne biblioteki współdzielone. –

+0

W ramach tego samego procesu każdy moduł może uzyskać dostęp do zewnętrznych zmiennych globalnych bez żadnych ograniczeń. – 9dan

Odpowiedz

11

Myślę, że będziesz potrzebować pomocy z udostępnionej biblioteki, aby zrobić to w dowolny przenośny sposób. Nie musi koniecznie wiedzieć nic o obiektach współużytkowanych między modułami, po prostu musi dostarczyć trochę globalnie dostępnego mapowania z klucza (prawdopodobnie łańcucha) do wskaźnika.

Jeśli jednak chcesz wywoływać interfejsy API systemu operacyjnego, jest to możliwe i myślę, że możesz potrzebować tylko dwóch implementacji części specyficznej dla systemu operacyjnego (jedna dla bibliotek DLL systemu Windows i GetProcAddress, jedna dla systemów operacyjnych korzystających z dlopen) .

Podczas ładowania każdego modułu przechodzi listę wcześniej załadowanych modułów, szukając dowolnego, który eksportuje specjalnie nazwaną funkcję. Jeśli znajdzie jeden (dowolny, nie ma znaczenia który, ponieważ niezmiennikiem jest to, że wszystkie w pełni załadowane moduły są świadome wspólnego obiektu), pobiera adres wspólnego obiektu z wczytanego wcześniej modułu, a następnie zwiększa liczbę odwołań . Jeśli nie można go znaleźć, przydziela nowe dane i inicjuje licznik odwołań. Podczas rozładowywania modułu zmniejsza on liczbę odwołań i zwalnia wspólny obiekt, jeśli licznik odwołań wyniósł zero.

Oczywiście konieczne jest użycie podzielnika systemu operacyjnego dla wspólnego obiektu, ponieważ chociaż jest to mało prawdopodobne, możliwe jest, że został zwolniony z innej biblioteki niż ta, która została po raz pierwszy załadowana. Oznacza to również, że wspólny obiekt nie może zawierać żadnych funkcji wirtualnych ani żadnego innego rodzaju wskaźnika do segmentów różnych modułów. Wszystkie jego zasoby muszą być przydzielane dynamicznie przy użyciu przydziału dla całego systemu operacyjnego. Jest to prawdopodobnie mniej obciążające systemy, w których libC++ jest biblioteką współdzieloną, ale powiedziałeś, że łączysz statycznie CRT.

funkcje potrzebne w Win32 obejmowałyby EnumProcessModules, GetProcAddress, HeapAlloc i HeapFree, GetProcessHeap i GetCurrentProcess.

Wszystko, co uważam, myślę, że trzymałbym się umieszczania wspólnego obiektu we własnej bibliotece współdzielonej, która wykorzystuje struktury danych modułu ładującego, aby go znaleźć. W przeciwnym razie ponownie wynajdujesz program ładujący. To zadziała nawet wtedy, gdy CRT jest statycznie połączony z kilkoma modułami, ale myślę, że ustawiasz się na naruszenia ODR. Bądź naprawdę konkretny w utrzymywaniu wspólnych danych POD.

+0

Cóż, to naprawdę twórcze rozwiązanie :-) Zastanawiam się, czy aplikacja wykonywalna łączy się z niektórymi bibliotekami współdzielonymi (automatycznie przy starcie), a jednocześnie ma zdefiniowane pewne zmienne globalne, to kolejność ładowania bibliotek/inicjowania globalnych zmienne zdefiniowane przez standard, czy jest to implementacja zdefiniowana? Jeśli dane globalne zostaną najpierw zainicjowane, możemy przeprowadzić inicjalizację w głównym pliku wykonywalnym i nie martwić się o różne alokatory mem w przypadku statycznego łączenia CRT. –

+1

@John: Och, czy gwarantujesz, że te zmienne istnieją w głównym pliku wykonywalnym? Myślałem, że projektujesz dla ogólnego przypadku: próbujesz zbudować bibliotekę, która działa niezależnie od tego, czy aplikacja używa tego samego obiektu pomocnika, czy nie. W każdym razie, biblioteki nie są adresowane przez standard C++ AT ALL, więc kolejność ładowania nie jest zdefiniowana przez standard. Jednak na większości platform biblioteki są obsługiwane przez program ładujący OS przed uruchomieniem dowolnego konstruktora.Oczywiście z obciążeniem opóźnionym lub jawnym ładowaniem biblioteki nie ładują się podczas uruchamiania, ale gdy ten kod jest wykonywany. –

+0

Tak, biblioteka ma być ogólna, więc była to tylko myśl. –

0

Można użyć boost::interprocesshttp://www.boost.org/doc/libs/1_45_0/doc/html/interprocess.html i Windows można utworzyć wspólny segment w swojej biblioteki DLL, które będą wspólne dla wszystkich procesów wykorzystujących # pragma to: http://www.codeproject.com/KB/DLL/data_seg_share.aspx

+0

Tak, mam nadzieję, że można go rozwiązać z doładowaniem, ale czy można dokładniej określić, jak utworzyć nazwany segment pamięci tylko w bieżącym procesie? –

+0

Co masz na myśli przez "tylko obecny proces?" Dostępne tylko z jednego procesu? Zapisywalny tylko z jednego procesu? –

+0

Jeśli utworzę pamięć współdzieloną z pewnym identyfikatorem, będzie ona dostępna ze wszystkich procesów w systemie, prawda? Potrzebuję, aby ta pamięć była dostępna tylko przez proces, który ją tworzy, z pełnym dostępem do odczytu/zapisu –

1

do wykorzystania od bieżącego procesu tylko, don Trzeba opracować jakąś specjalną funkcję lub strukturę.

Można to zrobić nawet bez żadnej funkcji, ale jest bardziej bezpieczny i przyjazny dla platformy, aby zdefiniować zestaw funkcji zapewniających dostęp do udostępnionych danych. Te funkcje mogą być implementowane przez wspólną bibliotekę statyczną.

Wydaje mi się, że jedynym problemem związanym z tą konfiguracją jest: "Kto będzie właścicielem danych?". Musi istnieć jeden i tylko jeden właściciel udostępnianych danych.

Z tych podstawowych idei, moglibyśmy naszkicować API tak:

IsSharedDataExist  // check whether of not shared data exist 

CreateSharedData  // create (possibly dynamically) shared data 

DestroySharedData  // destroy shared data 

... various data access API ... 

lub C++ klasy z wzorca Singleton będzie właściwe.


UPDATE

byłem zdezorientowany. Prawdziwy problem można zdefiniować jako "Jak zaimplementować klasę Singleton w bibliotece statycznej, która będzie połączona z wieloma bibliotekami dynamicznego ładowania (będzie używana w tym samym procesie) w sposób niezależny od platformy".

Myślę, że podstawowy pomysł nie jest różny, ale upewnij się, że singleton jest naprawdę singielkiem, to dodatkowy problem w tej konfiguracji.

W tym celu można skorzystać z Boost.Interprocess.

#include <boost/config.hpp> 
#include <boost/interprocess/sync/named_mutex.hpp> 
... 
boost::interprocess::named_mutex* singleton_check = 0; 

// in the Create function of the singleton 
try { 
    singleton_check = new boost::interprocess::named_mutex(boost::interprocess::create_only, "name_of_the_mutex"); 
    // if no exception throw, this is the first time execution 
} 
catch (...) 
{ 
} 

Uwolnienie named_mutex jest tak proste jak delete singleton_check.


UPDATE # 2

Kolejna propozycja.

Myślę, że nie powinniśmy umieszczać wspólnych danych we wspólnej bibliotece statycznej. Jeśli nie możemy zapewnić globalnie unikalnych danych, to nie tylko trudne problemy z implementacją zależą od platformy, ale także marnowanie pamięci i zasobów globalnych.

Jeśli wolisz korzystać z biblioteki statycznej, powinieneś utworzyć dwie statyczne biblioteki. Jeden dla serwera/twórcy udostępnionych danych, jeden dla użytkowników udostępnionych danych. Biblioteka serwerów definiuje i zapewnia dostęp do Singleton. Biblioteka klientów udostępnia różne metody dostępu do danych.

Jest to faktycznie to samo, co implementacja Singleton bez bibliotek statycznych.

+0

Sposób, w jaki zamierzałem do tego podejść, polegał na sprawdzeniu podanej pamięci - jeśli już istniała, uzyskałabym do niej dostęp i wykonałbym każdą pracę, której potrzebuję, a jeśli to zrobi, nie istniał, a potem bym go stworzył. To samo dotyczy deinicjalizacji - każdy moduł zarejestruje się, aby śledzić liczbę użytków, a ostatnia zamknięta przeprowadzi porządkowanie. –

+0

Jeśli chodzi o coś między procesami, potrzebujesz specjalnej funkcjonalności dostępnej na platformie. Ale jeśli jest to "wewnątrz bieżącego procesu", globalnie zdefiniowane zmienne są nazwaną pamięcią. – 9dan

+0

Czy globalne zmienne nie byłyby udostępniane tylko w jednym module, z oddzielnymi kopiami w innych modułach? –

0

Zgodnie MSDN widzę istnieją tylko dwa sposoby udostępniania danych pomiędzy modułami

  1. Korzystanie data_seg pragmy
  2. korzystają z pamięci współdzielonej.

Jak wskazano, segment współdzielony działa tylko dla dwóch wystąpień tej samej biblioteki dll, więc pozostaje nam tylko jedna możliwość użycia techniki mapowania pamięci.

Powiązane problemy