2012-06-04 8 views
5

Mam DLL, który zajmuje 5 do 10 sekund, aby załadować, co oznacza, że ​​muszę czekać tak długo za każdym razem, gdy kompiluję i uruchamiam plik wykonywalny, który używa go. Czy istnieje sposób na załadowanie biblioteki DLL do pamięci, aby można było uzyskać do niej natychmiastowy dostęp za każdym razem, gdy kompilowałem odpowiedni plik wykonywalny? Kompiluję program na QT MinGW, jeśli to istotne.Czy jest możliwe zachowanie DLL w pamięci po wywołaniu procesu wyjścia?

EDYCJA: Na razie brak szczęścia. Ładowanie biblioteki DLL z innego programu wydaje się nie mieć żadnego efektu (oryginalny program nadal ładuje bibliotekę DLL i zajmuje tak długo). Sądzę, że muszę załadować bibliotekę DLL i jej funkcje w inny sposób, jeśli zostały załadowane do innego programu, ale nie wiem jak to zrobić. Teraz używam LoadLibrary i GetProcAddress.

+3

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. –

+0

To byłoby niefortunne, jeśli to prawda. Zakładam, że nic nie mogę zrobić bez dostępu do kodu źródłowego? – SharpHawk

+0

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. –

Odpowiedz

1

Najprostszym rozwiązaniem jest (zakładając MSVC++), aby DLL opóźnienie załadowane. Kompromisem oczywiście jest to, że inicjalizacja wciąż musi się odbyć, ale to nie będzie dłużej opóźniać innych części twojego programu. Na przykład. możesz to zrobić na wątku tła.

+0

Biorąc pod uwagę fakt, że nie mogłem uzyskać biblioteki DLL, aby pozostać w pamięci, i że rozwiązanie totowtwo oznaczałoby spowolnienie tanich laptopów jeszcze bardziej, ładowanie biblioteki DLL w równoległym wątku wydaje się być droga iść. To samo rozwiązanie zostało przedstawione w podobnym pytaniu, które zadawałem tutaj również w StackOverflow. – SharpHawk

1

Utwórz jawnie zainstalowaną usługę systemową, która będzie ładować bibliotekę DLL. W ten sposób inicjalizacja dzieje się przy starcie systemu i nigdy więcej. Polecam wbrew metodom opisanym w większości innych odpowiedzi. Chociaż wyglądają, jakby działały, czują się jak złe zachowanie ze strony twojego oprogramowania. Zarówno z punktu widzenia użytkownika, jak i opiekuna, wolałbym jawnie zainstalowaną usługę systemową zamiast zahaczyć coś w winlogin.exe. Im bardziej szczerze korzystasz z interfejsów API systemu Windows i środowiska, tym mniej zmieniających się zmian będziesz mieć w różnych wersjach i wersjach.

+0

To na pewno wygląda tak, więc zredagowałem moje "Nie". Czuję się jednak nieswojo z tymi rozwiązaniami. – totowtwo

+0

Inicjowanie występuje dla każdego procesu i wątku dołączanego do biblioteki DLL. Załadowanie biblioteki DLL tylko do usługi powoduje zapisanie we/wy pliku. –

+0

Czy możesz bardziej szczegółowo opisać, jak to zrobić? W tej formie twoja odpowiedź jest głównie krytyką innych przedstawionych sugestii. – SharpHawk

1

Jeśli się nie mylę, Windows przechowuje jedną instancję DLL w pamięci więc utrzymanie tego cało powinno działać:

#include <conio.h> 
#include <windows.h> 

int main() 
    { 
    HMODULE handle=LoadLibrary("yourdll.dll"); 
// Make shure Windows resolves the DLL 
    FARPROC dummy=GetProcAddress(handle,"functionInDll"); 
// The process will now just wait for keyboard input. 
    getch(); 
    CloseHandle(handle); 
    return 0; 
    } 
+0

Dlaczego robisz FARPROC manekin = GetProcAddress (uchwyt, "functionInDll"); jeśli chcesz tylko załadować bibliotekę DLL? – rkosegi

+0

Mhm, Próbowałem pozostawiając instancję mojego programu otwartą (z załadowaną biblioteką DLL), ale nowsze instancje programu nadal trwały tyle samo czasu, aby załadować bibliotekę DLL. Korzystanie z samych funkcji jest zawsze natychmiastowe. Zastanawiam się, na czym polega problem ... – SharpHawk

2

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:

enter image description here

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.

Powiązane problemy