Wie synchronisiert man eine DLL mit zwei Prozessen?
-
Hallo,
ich bin neu hier, hab per Suche keine passende Antwort gefunden und hoffe, dass ich bin im richtigen Bereich gelandet bin
Ich arbeite mich in DLLs ein. In Visual Studio 2005 habe ich mir dafür zwei Projekte (Dynamische Bibliothek (.dll) und Anwendung (.exe)) angelegt. Die EXE kann die Funktionen der DLL nutzen und eine globale Variable hoch- und runterzählen.
__declspec(dllexport) int dllInc();
__declspec(dllexport) int dllDec();int dllInc() {
g_dllVarS++;
return 0;
}int dllDec() {
g_dllVarS--;
return 0;
}- So weit so gut
Nun wollte ich das ganze mit zwei Prozessen der EXE, die auf die selbe DLL zugreifen (keine neue DLL-Instanz), programmieren. Dafür habe ich in der DLL die Varibale in einen shared Memory Bereich gelegt.
#pragma data_seg( "shared" )
int g_dllVarS = 0; // shared var
#pragma data_seg()
#pragma comment( linker, "/SECTION:shared,RWS" )Reicht es aus, dass die Variable im shared Memory Bereich liegt, damit ich keine RaceCondition bekomme? Oder muss ich z.B. mittels Critical Section den Zugriff atomar gestalten? Wenn ja, wo erstelle/initialisiere ich das handle hierfür (da es ja in dem Sinne keine Main-Function gibt)?
Oder gibt es hierfür ein besseres Verfahren?
Danke schon mal für's lesen
-
Reicht es aus, dass die Variable im shared Memory Bereich liegt, damit ich keine RaceCondition bekomme?
nein.
Oder muss ich z.B. mittels Critical Section den Zugriff atomar gestalten?
ja, das wäre eine von mehreren möglichkeiten.
obwohl der zugriff durch die critical-section nicht atomar wird, sondern es wird sichergestellt, dass immer nur ein thread gleichzeitig einen nicht-atomare zugriff durchführen kann. und dass der nicht-atomare zugriff erst nach dem "enter" der critical-section begonnen wird, und vor dem "leave" vollständig abgeschlossen ist.
einen atomaren zugriff könntest du mittels der InterlockedXxx funktionen machen. dann bräuchtest du keine critical section. (wobei nichtmal bei den InterlockedXxx funktionen garantiert ist dass der eigentliche zugriff auf hardware-ebene atomar ist. es ist bloss garantiert dass er in verbindung mit anderen InterlockedXxx funktionen atomar erscheint)
Wenn ja, wo erstelle/initialisiere ich das handle hierfür (da es ja in dem Sinne keine Main-Function gibt)?
entweder in der DllMain funktion, oder du verwendest eine globale instanz einer klasse, deren konstruktor das initialisieren der critical-section übernimmt.
beides ist nicht unüblich.
-
Danke erstmal für die schnelle Antwort.
entweder in der DllMain funktion, oder du verwendest eine globale instanz einer klasse, deren konstruktor das initialisieren der critical-section übernimmt.
Mit der DllMain Funktion werd ich morgen mal ausprobieren. Mit dem Konstruktor wird nicht klappen, denn ich programmiere in C. Oder gibt es in C etwas ähnliches wie einen Konstruktor aus C++?
-
ThomasBlue schrieb:
.... Die EXE kann die Funktionen der DLL nutzen und eine globale Variable hoch- und runterzählen.
.... zwei Prozessen der EXE, die auf die selbe DLL zugreifen (keine neue DLL-Instanz)....- Jeder neue Prozess, der die DLL nutzt, legt eine neue DLL-Instanz an.
- Willst Du nur eine Zählvariable mitführen, so kannst Du das auch mit SendMessage an die Hauptfenster der EXE realisieren. Alles was man braucht
sind die Fensterhandles (HWND), die man sich z.B. mit EnumWindows beschaffen kann.
- Siehe auch meinen gestrigen Beitrag zu einer DLL-Anfrage.
-
berniebutt schrieb:
ThomasBlue schrieb:
.... Die EXE kann die Funktionen der DLL nutzen und eine globale Variable hoch- und runterzählen.
.... zwei Prozessen der EXE, die auf die selbe DLL zugreifen (keine neue DLL-Instanz)....- Jeder neue Prozess, der die DLL nutzt, legt eine neue DLL-Instanz an.
Ja, wobei die "shared" Datensegmente eben wirklich "geshared" werden. Insofern verstehe ich nicht wieso du das schreibst, da dass was der OP schreibt jo wohl funktioniert, und sich dein Beitrag so liest als ob es eine Richtigstellung wäre.
EDIT: ah, hihi, überlesen. Er schreibt "keine neue DLL-Instanz" - was natürlich nicht richtig ist. Die "shared" Datensegmente werden nicht "verdoppelt", alles andere schon. Meine Frage ist somit hinfällig

-
Das ganze funktionert nicht mehr, wenn es zwar die gleiche DLL ist, aber in unterschiedlichen Verzeichnissen liegt.
Ich würde hier eher zu Semaphoren oder anderem greifen.
-
Wir sind hier auf dem nicht trivialen Gebiet "Inter-Process-Communication IPC". Da haben uns die Systemprogrammierer von MS, speziell der WiApi, leider ziemlich im Regen stehen gelassen. Es gibt mehrere Wege und manches ist sogar compiler-abhängig. Also: nicht ganz einfach, aber machbar!
-
Compiler-abhängig ist nur die Schreibweise, nicht aber die verschiedenen Möglichkeiten die einem zur Verfügung stehen.
-
berniebutt schrieb:
Da haben uns die Systemprogrammierer von MS, speziell der WiApi, leider ziemlich im Regen stehen gelassen.
Versteh ich nicht?
Atoms, Messages, Shared Memory, Memory Mapped Files, Named Pipes, DDE, COM?Im Regen stehen lassen finde ich das nicht!
-
Soo ich hab mal ein wenig weiter gemacht mit der Idee der DllMain (natürlich bevor ich die Beiträge hier gelesen hab
)
Wie ich mir das dachte, klappt es natürlich nicht.Lösung A:
Der shared Bereich:#pragma data_seg( "shared" )
static CRITICAL_SECTION CriticalSectionRead;
int g_dllVarS = 0; // shared var
#pragma data_seg()
#pragma comment( linker, "/SECTION:shared,RWS" )Die "Dllmain" (hoffentlich ist der Name nicht ausschlaggebend):
int dllinit() {
InitializeCriticalSection( &CriticalSectionRead );
}Die dllinit() wird nun bei jeder EXE aufgerufen. Ich hatte gehofft, dass falls "CriticalSectionRead" schon initialisiert ist, ein weiterer Aufruf von InitializeCriticalSection( &CriticalSectionForReadFct ); an der Variablen nichts weiter ändert (das z.B. wie bei CreateFileMapping() quasi ein already exists zurückkommt).
In diesem Falle haben die Anpassungen in dem Code natürlich nichts gebracht:int dllInc() {
EnterCriticalSection( &CriticalSectionRead );
g_dllVarS++;
return 0;
}int dllDec() {
g_dllVarS--;
LeaveCriticalSection( &CriticalSectionRead )
return 0;
}
;(Ich weiß, dass man normaler Weise nur den Zugriff auf die Variable umschließt. Aber um die Funktionalität nachzuweisen, wollte ich die beiden Funktionen quasi verschränken
)Lösung B:
Also hab ich in den shared Bereich noch eine Variable angelegt (der Name ist ja wohl mehr als klasse :p ).static int critical_virgin=1;
und die dllinit() damit ausgestattet:
int dllinit() {
if (critical_virgin) {
InitializeCriticalSection( &CriticalSectionRead );
critical_virgin=0;
}
}Nun stürzt mir die zweite EXE bei dem Aufruf von EnterCriticalSection( &CriticalSectionRead ); ab. Ich nehme mal an aus diesem Grunde:
hustbaer schrieb:
Die "shared" Datensegmente werden nicht "verdoppelt", alles andere schon. Meine Frage ist somit hinfällig

Und weil nun meine Critical Section Varible somit nicht mehr initialisiert ist. Also würde Critical Section als Lösung schon mal wegfallen, richtig?
Okay dann mal noch Antworten auf eure Fragen

berniebutt schrieb:
- Willst Du nur eine Zählvariable mitführen,
Nein, ich mag nur herausfinden, wie man quasi IPC bei DLLs nutzt
Martin Richter schrieb:
... funktionert nicht mehr, wenn es ... in unterschiedlichen Verzeichnissen liegt.
Liegt alles im gleichen Verzeichnis
Martin Richter schrieb:
Ich würde hier eher zu Semaphoren oder anderem greifen.
Okay, dann werd ich mal nach Semaphoren ausschau halten. Aber da hab ich doch auch das Problem, dass ich die Semaphoren irgendwo initialisieren muss, oder?
Und was ist denn "oder anderem"? Bitte näher erläutern
-
Critical Sections funktionieren nur im selben Prozess.
Einen Semaphore/Mutex kann man unter einem Namen anlegen und Prozessübergreifend darauf zugreifen.
-
ARRR!
Sorry, mein Fehler!
Natürlich funktionieren Critical-Sections nur innerhalb eines Prozesses.Also entweder auf Mutexen umsteigen (relativ langsam), oder selbst ne Spin-Lock basteln (mit InterlockedCompareExchange).
-
Einmal kurz zusammengefasst, Du hast zwei getrennte Aufgaben, die aufeinander abgestimmt sein müssen:
1. IPC - Die Kommunikation über Prozessgrenzen hinaus.
2. Die Synchronisation der Zugriffe auf die systemglobalen Daten.
Es eignen sich:
für 1. a)Shared memory / b)FileSharing / c)Selbstgestrickte Lösungen
Die Lösung a) ist leider compiler-abhängig, bei Borland über #pragmas nicht machtbar!
für 2. Mutex-Objekte, vielleicht auch Semaphoren.
-
Erstmal noch mal danke für eure Unterstützung Leute!
Hab das Problem nun mit Semaphoren gelöst, hier der Code dazu:
// Shared var section #pragma data_seg( "shared" ) int g_dllVarS = 0; #pragma data_seg() #pragma comment( linker, "/SECTION:shared,RWS" ) // Global (not shared) var section int g_dllVarN = 0; HANDLE ghSem; int dllinit() { ghSem = CreateSemaphore( NULL , 1, 1, "ReadSem"); return 0; } int dllclose() { CloseHandle(ghSem); return 0; } int dllInc() { printf("Enter Critical Sec -- "); WaitForSingleObject(ghSem, INFINITE); printf("Entered Critical Sec\n"); g_dllVarS++; g_dllVarN++; Sleep(3000); printf("Leave Critical Sec -- "); ReleaseSemaphore(ghSem, 1, NULL); printf("Leaved Critical Sec \n"); return 0; } int dllDec() { printf("Enter Critical Sec -- "); WaitForSingleObject(ghSem, INFINITE); printf("Entered Critical Sec\n"); g_dllVarS--; g_dllVarN--; Sleep(3000); printf("Leave Critical Sec -- "); ReleaseSemaphore(ghSem, 1, NULL); printf("Leaved Critical Sec \n"); return 0; } int dllGetVarS() { return g_dllVarS; } int dllGetVarN() { return g_dllVarN; }Nun kann immer nur ein Prozess auf die Variablen zugreifen. Bei mehrfachem Aufruf von CreateSemaphore(); mit dem gleichen Bezeichner (hier "ReadSem") wird kein neuer Semaphor erstellt, sondern nur das Zugriffsrecht erteilt.
Mittels dem Sleep(); sieht man sehr schön, dass ein Prozess gerade sperrt und welcher es ist
-
Wieso verwendest du eine Semaphore und keine Mutex???
-
@ ThomasBlue: Gratulation! Mutex erscheint mir aber passender als Semaphore.
@ Martin Richter: Du gehörst zu den wenigen Programmierern, die rundum mit den angebotenen Dingen von Microsoft zufrieden sind. Vielleicht bist Du später als mit DOS und Win16 eingestiegen? In den 1980er Jahren hiess es nicht zu unrecht: "Gates or Gates not?", jedenfalls unter deutschprachigen Programmierern.
-
Hey hier kein geflame bitte, schön beim Thema bleiben.
hustbaer schrieb:
Wieso verwendest du eine Semaphore und keine Mutex???
Hatte mich mit Mutex noch nicht befasst und oben hattest du doch geschrieben, dass das relativ langsam sei ... gut, ich weiß auch nicht, wie schnell/langsam Semaphore sind.
Wo besteht denn da der Unterschied?
-
Semaphore = genauso langsam wie die Mutex.
Unterschied: Mutex ist "spezialisierter", und damit IMO besser geeignet für das was du machen willst.
-
ThomasBlue schrieb:
Hey hier kein geflame bitte, schön beim Thema bleiben.
Jeder Beitrag hier ist beim Thema geblieben. Boszameg, das Wort "geflame" kenne ich leider nicht. Nehme ein Mutex-Objekt.