Proces zarządzanego wykonania

Proces wykonywania zarządzanego obejmuje następujące kroki, które zostały szczegółowo omówione w dalszej części tego tematu:

  1. Wybieranie kompilatora. Aby uzyskać korzyści zapewniane przez środowisko uruchomieniowe języka wspólnego, należy użyć co najmniej jednego kompilatora języka przeznaczonego dla środowiska uruchomieniowego.
  2. Kompilowanie kodu do języka pośredniego. Kompilowanie tłumaczy kod źródłowy na wspólny język pośredni (CIL) i generuje wymagane metadane.
  3. Kompilowanie kodu CIL do kodu natywnego. W czasie wykonywania kompilator just in time (JIT) tłumaczy element CIL na kod natywny. Podczas tej kompilacji kod musi przejść proces weryfikacji, który analizuje CIL i metadane, aby dowiedzieć się, czy kod można określić jako bezpieczny.
  4. Uruchamianie kodu. Środowisko uruchomieniowe języka wspólnego udostępnia infrastrukturę, która umożliwia wykonywanie i usługi, które mogą być używane podczas wykonywania.

Wybieranie kompilatora

Aby uzyskać korzyści zapewniane przez środowisko uruchomieniowe języka wspólnego (CLR), należy użyć co najmniej jednego kompilatora języka przeznaczonego dla środowiska uruchomieniowego, takiego jak Visual Basic, C#, Visual C++, F# lub jeden z wielu kompilatorów innych firm, takich jak kompilator Eiffla, Perl lub COBOL.

Ponieważ jest to środowisko wykonawcze z wieloma językami, środowisko uruchomieniowe obsługuje szeroką gamę typów danych i funkcji językowych. Używany kompilator języka określa, które funkcje środowiska uruchomieniowego są dostępne, i projektujesz kod przy użyciu tych funkcji. Kompilator, a nie środowisko uruchomieniowe, ustanawia składnię, która musi być używana przez kod. Jeśli składnik musi być całkowicie używany przez składniki napisane w innych językach, wyeksportowane typy składnika muszą uwidaczniać tylko funkcje języka zawarte w specyfikacji języka wspólnego (CLS). Możesz użyć atrybutu CLSCompliantAttribute , aby upewnić się, że kod jest zgodny ze specyfikacją CLS. Aby uzyskać więcej informacji, zobacz Niezależność języka i składniki niezależne od języka.

Kompilowanie do CIL

Podczas kompilowania do kodu zarządzanego kompilator tłumaczy kod źródłowy na wspólny język pośredni (CIL), który jest niezależnym od procesora CPU zestawem instrukcji, które mogą być efektywnie konwertowane na kod natywny. Funkcja CIL zawiera instrukcje dotyczące ładowania, przechowywania, inicjowania i wywoływania metod na obiektach, a także instrukcje dotyczące operacji arytmetycznych i logicznych, przepływu sterowania, bezpośredniego dostępu do pamięci, obsługi wyjątków i innych operacji. Przed uruchomieniem kodu funkcja CIL musi zostać przekonwertowana na kod specyficzny dla procesora CPU, zwykle przez kompilator just in time (JIT). Ponieważ środowisko uruchomieniowe języka wspólnego dostarcza co najmniej jeden kompilator JIT dla każdej obsługiwanej architektury komputera, ten sam zestaw CIL może być kompilowany i uruchamiany w dowolnej obsługiwanej architekturze.

Gdy kompilator tworzy kod CIL, generuje również metadane. Metadane opisują typy w kodzie, w tym definicję każdego typu, podpisy elementów członkowskich każdego typu, składowych, do których odwołuje się kod, oraz inne dane używane przez środowisko uruchomieniowe w czasie wykonywania. CIL i metadane są zawarte w przenośnym pliku wykonywalnym (PE), który jest oparty na i rozszerza opublikowany microsoft PE i wspólny format pliku obiektów (COFF) używany historycznie do zawartości wykonywalnej. Ten format pliku, który uwzględnia kod CIL lub kod natywny, a także metadane, umożliwia systemowi operacyjnemu rozpoznawanie obrazów środowiska uruchomieniowego języka wspólnego. Obecność metadanych w pliku wraz z funkcją CIL umożliwia samodzielne opisanie kodu, co oznacza, że nie ma potrzeby bibliotek typów ani języka definicji interfejsu (IDL). Środowisko uruchomieniowe lokalizuje i wyodrębnia metadane z pliku zgodnie z potrzebami podczas wykonywania.

Kompilowanie kodu CIL do kodu natywnego

Przed uruchomieniem wspólnego języka pośredniego (CIL) należy go skompilować względem środowiska uruchomieniowego języka wspólnego, aby kod natywny dla architektury maszyny docelowej. Platforma .NET udostępnia dwa sposoby wykonania tej konwersji:

Kompilacja kompilatora JIT

Kompilacja JIT konwertuje kod CIL na natywny na żądanie w czasie wykonywania aplikacji, gdy zawartość zestawu jest ładowana i wykonywana. Ponieważ środowisko uruchomieniowe języka wspólnego dostarcza kompilator JIT dla każdej obsługiwanej architektury procesora CPU, deweloperzy mogą tworzyć zestawy CIL, które mogą być kompilowane i uruchamiane na różnych komputerach z różnymi architekturami maszyn. Jeśli jednak kod zarządzany wywołuje natywne interfejsy API specyficzne dla platformy lub bibliotekę klas specyficznych dla platformy, zostanie on uruchomiony tylko w tym systemie operacyjnym.

Kompilacja JIT uwzględnia możliwość wywołania kodu podczas wykonywania. Zamiast używać czasu i pamięci, aby przekonwertować wszystkie CIL w pliku PE na kod natywny, konwertuje CIL zgodnie z potrzebami podczas wykonywania i przechowuje wynikowy kod natywny w pamięci, aby był dostępny dla kolejnych wywołań w kontekście tego procesu. Moduł ładujący tworzy i dołącza wycinkę do każdej metody w typie, gdy typ jest ładowany i inicjowany. Gdy metoda jest wywoływana po raz pierwszy, element wycinkowy przekazuje kontrolę do kompilatora JIT, który konwertuje element CIL dla tej metody na kod natywny i modyfikuje wycinkę, aby wskazywał bezpośrednio na wygenerowany kod macierzysty. W związku z tym kolejne wywołania metody skompilowanej JIT przechodzą bezpośrednio do kodu natywnego.

Generowanie kodu w czasie instalacji przy użyciu NGen.exe

Ponieważ kompilator JIT konwertuje element CIL zestawu na kod macierzysty, gdy są wywoływane poszczególne metody zdefiniowane w tym zestawie, ma negatywny wpływ na wydajność w czasie wykonywania. W większości przypadków mniejsza wydajność jest akceptowalna. Co ważniejsze, kod wygenerowany przez kompilator JIT jest powiązany z procesem, który wyzwolił kompilację. Nie można go udostępniać w wielu procesach. Aby umożliwić współużytkowanie wygenerowanego kodu w wielu wywołaniach aplikacji lub w wielu procesach współużytkujących zestaw zestawów, środowisko uruchomieniowe języka wspólnego obsługuje tryb kompilacji z wyprzedzeniem. Ten tryb kompilacji z wyprzedzeniem używa Ngen.exe (generatora obrazów natywnych) do konwertowania zestawów CIL na kod macierzysty, podobnie jak kompilator JIT. Jednak działanie Ngen.exe różni się od działania kompilatora JIT na trzy sposoby:

  • Wykonuje konwersję z CIL na kod natywny przed uruchomieniem aplikacji zamiast podczas działania aplikacji.
  • Kompiluje cały zestaw jednocześnie, a nie jedną metodę naraz.
  • Utrwala wygenerowany kod w natywnej pamięci podręcznej obrazów jako plik na dysku.

Weryfikacja kodu

W ramach kompilacji kodu natywnego kod CIL musi przejść proces weryfikacji, chyba że administrator ustanowił zasady zabezpieczeń, które umożliwiają kodowi obejście weryfikacji. Weryfikacja sprawdza CIL i metadane, aby dowiedzieć się, czy kod jest bezpieczny, co oznacza, że uzyskuje dostęp tylko do lokalizacji pamięci, do których ma dostęp. Bezpieczeństwo typu pomaga odizolować obiekty od siebie i chronić je przed przypadkowym lub złośliwym uszkodzeniem. Zapewnia również pewność, że ograniczenia zabezpieczeń kodu mogą być niezawodnie wymuszane.

Środowisko uruchomieniowe opiera się na tym, że następujące instrukcje są prawdziwe dla kodu, który jest weryfikowalny typ bezpieczny:

  • Odwołanie do typu jest ściśle zgodne z przywoływanym typem.
  • Tylko odpowiednio zdefiniowane operacje są wywoływane na obiekcie.
  • Tożsamości są tym, co twierdzą.

Podczas procesu weryfikacji kod CIL jest badany w celu potwierdzenia, że kod może uzyskiwać dostęp do lokalizacji pamięci i wywoływać metody tylko za pomocą poprawnie zdefiniowanych typów. Na przykład kod nie może zezwalać na dostęp do pól obiektu w sposób umożliwiający zastępowanie lokalizacji pamięci. Ponadto weryfikacja sprawdza kod w celu określenia, czy kod CIL został poprawnie wygenerowany, ponieważ niepoprawny kod CIL może prowadzić do naruszenia reguł bezpieczeństwa typów. Proces weryfikacji przechodzi dobrze zdefiniowany zestaw kodu bezpiecznego typu i przekazuje tylko kod, który jest bezpieczny. Jednak niektórych kodów bezpiecznych typów może nie przejść weryfikacji z powodu pewnych ograniczeń procesu weryfikacji, a niektóre języki zgodnie z projektem nie generują weryfikowanie bezpiecznego kodu typu. Jeśli zasady zabezpieczeń wymagają bezpiecznego kodu, ale kod nie przechodzi weryfikacji, podczas uruchamiania kodu jest zgłaszany wyjątek.

Uruchamianie kodu

Środowisko uruchomieniowe języka wspólnego udostępnia infrastrukturę, która umożliwia wykonywanie zarządzane i usługi, które mogą być używane podczas wykonywania. Aby można było uruchomić metodę, należy ją skompilować do kodu specyficznego dla procesora. Każda metoda, dla której wygenerowano element CIL, jest kompilowana w trybie JIT, gdy jest wywoływana po raz pierwszy, a następnie uruchamiana. Następnym razem, gdy metoda zostanie uruchomiona, zostanie uruchomiony istniejący kod natywny skompilowany w trybie JIT. Proces kompilowania JIT,a następnie uruchamiania kodu jest powtarzany do momentu ukończenia wykonywania.

Podczas wykonywania kod zarządzany odbiera usługi, takie jak odzyskiwanie pamięci, zabezpieczenia, współdziałanie z niezarządzanym kodem, obsługa debugowania między językami oraz ulepszona obsługa wdrażania i obsługi wersji.

W systemie Microsoft Windows Vista moduł ładujący systemu operacyjnego sprawdza moduły zarządzane, sprawdzając nieco w nagłówku COFF. Ustawiany bit oznacza zarządzany moduł. Jeśli moduł ładujący wykryje moduły zarządzane, ładuje mscoree.dll i _CorValidateImage_CorImageUnloading powiadamia moduł ładujący o załadowaniu i usunięciu obrazów modułu zarządzanego. _CorValidateImage wykonuje następujące akcje:

  1. Gwarantuje, że kod jest prawidłowym kodem zarządzanym.
  2. Zmienia punkt wejścia na obrazie na punkt wejścia w środowisku uruchomieniowym.

W 64-bitowym systemie Windows modyfikuje obraz, który jest w pamięci, _CorValidateImage przekształcając go z PE32 do FORMATU PE32+ .

Zobacz też