Nie jestem deweloperem MinGW, ale twoje pytanie jest bardzo powszechne i tak naprawdę nie zależy od sposobu utworzenia biblioteki DLL. Takie problemy będą być zazwyczaj rozwiązane z wykorzystaniem trzech technik:
- wyboru unikalnych adresów bazowych DLL
- wiązania DLL i EXE (podczas instalacji aplikacji)
- wykorzystania DLL opóźnienia załadunku technika
- trochę poprawić czas ładowania wezwanie DisableThreadLibraryCalls wewnątrz od
DLL_PROCESS_ATTACH
części DllMain
dokładna ów czarownice łącznika lub inne narzędzia, których możesz użyć, zależą od twojego środowiska programistycznego.
Aby zrozumieć problem, powinieneś wiedzieć, w jaki sposób zostanie załadowany plik wykonywalny lub biblioteka DLL. Przede wszystkim EXE lub DLL zostaną zmapowane w pamięci. Memory mapped file (sekcja) zostanie utworzony, co wskazuje na plik EXE/DLL. Będziesz miał w procesie niektóre adresy, których dostęp będzie odpowiadał plikowi EXE/DLL. Jeśli łączysz bibliotekę DLL, możesz wybrać adres podstawowy. Jeśli adres nie jest używany w przestrzeni adresowej procesu, nic nie zostanie zrobione. Jeśli zostanie użyty pierwszy wiersz kodu (wywołasz jakąś funkcję z biblioteki DLL), to strona z pamięcią 8K w pobliżu użytego adresu zostanie załadowana do pamięci z pliku. Jeśli dwa procesy używają tej samej biblioteki DLL, pamięć fizyczna kodu będzie udostępniona między procesami. Nawet jeśli przechowujesz zainicjowane zmienne, strona ze zmiennymi będzie udostępniana aż do pierwszych zmian zmiennej. Po modyfikacji zostanie wykonana kopia strony pamięci dla procesu, który dokonał modyfikacji.
Po wczytaniu biblioteki DLL niektóre części wywołującego (na przykład EXE) muszą zostać zmodyfikowane, tak aby zawierały rzeczywiste adresy funkcji używanych z biblioteki DLL. To samo zrobi z bibliotekami DLL, które korzystają z funkcji z innej biblioteki DLL.
Wszystko brzmi idealnie, ale jeśli nie ustawisz żadnych opcji linkera podczas kompilacji DLL (jeśli nie korzystasz z opcji linkera --image-base
lub --enable-auto-image-base
) będziesz mieć wszystkie swoje biblioteki DLL z tym samym adresem bazowym (domyślna wartość dla linker). Zatem pierwsza biblioteka DLL mogła zostać załadowana pod adresem. Podczas ładowania drugiej biblioteki DLL, która jest połączona z tym samym (lub jakimś nakładającym się adresem), nastąpi przeniesienie biblioteki DLL. Podczas relokacji zmienia się kod biblioteki DLL, a więc 1) ładowanie biblioteki DLL będzie powoli 2) zmodyfikowana kopia kodu zostanie wykonana w procesie (który zawiera pamięć używaną przez DLL) 3) zmodyfikowana kopia nie będzie współużytkowany przez wiele instancji biblioteki DLL (nawet jeśli wszystkie wystąpienia zostaną zmodyfikowane w ten sam sposób).
Polecam przede wszystkim użycie Process Explorer na przykład do sprawdzenia, które biblioteki DLL zostaną przeniesione do aplikacji. Powinieneś wybrać opcję "DLL" w menu "Widok"/"Lower Pain View" i zaznaczyć opcję "Relocation DLLs" w "Configure Highlighting" menu "Options". Możesz dodatkowo dostosować, które informacje o każdej bibliotece DLL będą wyświetlane. Im więcej informacji jak poniżej was zobaczyć bardziej powolny program zostanie załadowany i więcej przestrzeni adresowej nie będą dzielone między instancjami aplikacji lub między różnymi aplikacjami, które używają tego samego DLL:
W powyższy przykład, że drzewo Lenovo DLL TPOSDSVC.dll
, HKVOLKEY.dll
i TPLHMM.dll
są połączone z tym samym adresem bazowym 0x10000000
i tylko jeden DLL (TPOSDSVC.dll
tutaj) zostanie załadowany pod adresem. Dwie inne biblioteki DLL muszą zostać ponownie przeniesione.
Nie mogę napisać tutaj książki na ten temat. Polecam, abyś zbadał swój wniosek dotyczący problemu z relokacją. Możesz użyć opcji linkera (--image-base
lub --enable-auto-image-base
wydaje się być tym, czego potrzebujesz). Możesz użyć narzędzia dumpbin.exe (z Visual Studio również w wolnym wydaniu), aby sprawdzić obraz PE.
Po tym, jak wszystkie biblioteki DLL będą miały unikalny adres bazowy, można użyć innego narzędzia bind.exe
z opcją -u
, aby powiązać pliki EXE i biblioteki DLL z zależnymi bibliotekami DLL. Zmniejszy to dodatkowo rozmiar pamięci i poprawi czas uruchamiania aplikacji. Aktualizacja części IMAGE_DIRECTORY_ENTRY_IMPORT
i IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT
części DLL i EXE (patrz the answer). Bind.exe
używa wewnętrznie API BindImageEx
. Wiele konfiguracji Instalatora Windows używa tabeli BindImage i tabeli BindImage, aby utworzyć powiązanie po zakończeniu instalacji EXE i DLL.
Można rozważyć inne techniki (patrz here) w celu zmniejszenia rozmiaru DLL i EXE.
Nie wiem dokładnie, w jaki sposób można zastosować technikę opóźnionego obciążenia w MinGW, ale powinno to być definitywnie możliwe. Visual Studio należy wykonać w dwóch krokach: dodać Delayimp.lib
jako dodatkową bibliotekę i użyć opcji /DELAYLOAD
(patrz here), aby określić, które z bibliotek DLL powinny zostać wczytane przy pierwszym użyciu, a nie bezpośrednio. Korzystając z bardzo przydatnego narzędzia, można stwierdzić, że większość standardowych bibliotek DLL firmy Microsoft używa tej techniki. Możesz poprawić czas rozpoczęcia swojej aplikacji i zmniejszyć ilość używanej pamięci, jeśli używałbyś tej techniki.
To prawdopodobnie nie jest ładowanie do pamięci, ale inicjalizacja logiki wewnątrz biblioteki DLL, która trwa tak długo. Zachowanie biblioteki DLL w pamięci nie uniknie opóźnienia inicjalizacji. –
To byłoby niefortunne, jeśli to prawda. Zakładam, że nic nie mogę zrobić bez dostępu do kodu źródłowego? – SharpHawk
Można go zawinąć do pozaprocesowego serwera RPC, ale to z kolei powoduje szybkie ponowne połączenie z powodu nieefektywności z jego użyciem. –