Udostępnij za pośrednictwem


Architektura integracji środowiska CLR — środowisko hostowane w środowisku CLR

Dotyczy:programu SQL ServerAzure SQL Managed Instance

Integracja programu SQL Server ze środowiskiem uruchomieniowym języka wspólnego programu .NET Framework (CLR) umożliwia programistom baz danych używanie języków, takich jak C#, Visual Basic .NET i Visual C++. Funkcje, procedury składowane, wyzwalacze, typy danych i agregacje należą do rodzaju logiki biznesowej, którą programiści mogą pisać przy użyciu tych języków.

ClR zawiera pamięć zbieraną przez pamięć, wątek wyprzedzający, usługi metadanych (odbicie typu), weryfikowalność kodu i zabezpieczenia dostępu do kodu. ClR używa metadanych do lokalizowania i ładowania klas, określania wystąpień w pamięci, rozpoznawania wywołań metod, generowania kodu natywnego, wymuszania zabezpieczeń i ustawiania granic kontekstu czasu wykonywania.

Środowiska CLR i SQL Server różnią się w zależności od środowiska uruchomieniowego w sposób, w jaki obsługują pamięć, wątki i synchronizację. W tym artykule opisano sposób, w jaki te dwa czasy uruchamiania są zintegrowane, tak aby wszystkie zasoby systemowe były zarządzane równomiernie. W tym artykule opisano również sposób, w jaki zabezpieczenia dostępu kodu CLR (CAS) i SQL Server są zintegrowane w celu zapewnienia niezawodnego i bezpiecznego środowiska wykonawczego dla kodu użytkownika.

Podstawowe pojęcia dotyczące architektury ŚRODOWISKA CLR

W programie .NET Framework programista pisze w języku wysokiego poziomu, który implementuje klasę definiującą jej strukturę (na przykład pola lub właściwości klasy) i metody. Niektóre z tych metod mogą być funkcjami statycznym. Kompilacja programu tworzy plik nazywany zestawem zawierającym skompilowany kod w wspólnym języku pośrednim (CIL) i manifest zawierający wszystkie odwołania do zestawów zależnych.

Nuta

Zestawy są istotnym elementem architektury środowiska CLR. Są to jednostki tworzenia pakietów, wdrażania i przechowywania wersji kodu aplikacji w programie .NET Framework. Za pomocą zestawów można wdrożyć kod aplikacji wewnątrz bazy danych i zapewnić jednolity sposób administrowania, tworzenia kopii zapasowych i przywracania kompletnych aplikacji bazy danych.

Manifest zestawu zawiera metadane dotyczące zestawu, opisujące wszystkie struktury, pola, właściwości, klasy, relacje dziedziczenia, funkcje i metody zdefiniowane w programie. Manifest ustanawia tożsamość zestawu, określa pliki tworzące implementację zestawu, określa typy i zasoby tworzące zestaw, elementuje zależności czasu kompilacji dla innych zestawów i określa zestaw uprawnień wymaganych do prawidłowego działania zestawu. Te informacje są używane w czasie wykonywania do rozwiązywania odwołań, wymuszania zasad powiązania wersji i weryfikowania integralności załadowanych zestawów.

Program .NET Framework obsługuje atrybuty niestandardowe do dodawania adnotacji do klas, właściwości, funkcji i metod z dodatkowymi informacjami, które aplikacja może przechwytywać w metadanych. Wszystkie kompilatory programu .NET Framework używają tych adnotacji bez interpretacji i przechowują je jako metadane zestawu. Te adnotacje można zbadać w taki sam sposób, jak w przypadku innych metadanych.

Kod zarządzany jest wykonywany w środowisku CLR, a nie bezpośrednio przez system operacyjny. Aplikacje kodu zarządzanego uzyskują usługi CLR, takie jak automatyczne odzyskiwanie pamięci, sprawdzanie typów czasu wykonywania i obsługa zabezpieczeń. Te usługi pomagają zapewnić jednolite zachowanie niezależnych od platformy i języka zarządzanych aplikacji kodu.

Cele projektowania integracji środowiska CLR

Gdy kod użytkownika działa wewnątrz środowiska hostowanego przez clR w programie SQL Server (nazywanym integracją CLR), mają zastosowanie następujące cele projektowe:

Niezawodność (bezpieczeństwo)

Kod użytkownika nie powinien być dozwolony do wykonywania operacji, które naruszyły integralność procesu aparatu bazy danych, takie jak wyskakujące okno komunikatu z żądaniem odpowiedzi użytkownika lub zakończenie procesu. Kod użytkownika nie powinien być w stanie zastąpić pamięci aparatu bazy danych ani wewnętrznych struktur danych.

Skalowalność

Programy SQL Server i CLR mają różne modele wewnętrzne do planowania i zarządzania pamięcią. Program SQL Server obsługuje model współpracy, niewłaszczający wątków, w którym wątki dobrowolnie dają wykonywanie okresowo lub gdy oczekują na blokady lub we/wy. ClR obsługuje model wątków wstępnych. Jeśli kod użytkownika uruchomiony wewnątrz programu SQL Server może bezpośrednio wywołać wątkowe wątki systemu operacyjnego, nie integruje się dobrze z harmonogramem zadań programu SQL Server i może obniżyć skalowalność systemu. ClR nie rozróżnia pamięci wirtualnej i fizycznej, ale program SQL Server bezpośrednio zarządza pamięcią fizyczną i jest wymagany do korzystania z pamięci fizycznej w ramach konfigurowalnego limitu.

Różne modele zarządzania wątkami, planowania i pamięci stanowią wyzwanie związane z integracją systemu zarządzania relacyjnymi bazami danych (RDBMS), który jest skalowany w celu obsługi tysięcy współbieżnych sesji użytkowników. Architektura powinna zapewnić, że skalowalność systemu nie zostanie naruszona przez kod użytkownika wywołujący interfejsy programowania aplikacji (API) dla wątków, pamięci i synchronizacji elementów pierwotnych bezpośrednio.

Bezpieczeństwo

Kod użytkownika uruchomiony w bazie danych musi przestrzegać reguł uwierzytelniania i autoryzacji programu SQL Server podczas uzyskiwania dostępu do obiektów bazy danych, takich jak tabele i kolumny. Ponadto administratorzy bazy danych powinni mieć możliwość kontrolowania dostępu do zasobów systemu operacyjnego, takich jak pliki i dostęp sieciowy, z poziomu kodu użytkownika uruchomionego w bazie danych. Ta praktyka staje się ważna jako języki programowania zarządzanego (w przeciwieństwie do języków niezarządzanych, takich jak Transact-SQL) zapewniają interfejsy API dostępu do takich zasobów. System musi zapewnić bezpieczny sposób uzyskiwania dostępu do zasobów maszyny poza procesem aparatu bazy danych. Aby uzyskać więcej informacji, zobacz zabezpieczenia integracji środowiska CLR.

Wydajność

Zarządzany kod użytkownika uruchomiony w a aparatze bazy danych powinien mieć wydajność obliczeniową porównywalną z tym samym kodem uruchomionym poza serwerem. Dostęp do bazy danych z kodu zarządzanego użytkownika nie jest tak szybki, jak natywny język Transact-SQL. Aby uzyskać więcej informacji, zobacz Wydajność architektury integracji środowiska CLR.

Usługi CLR

ClR udostępnia kilka usług, które ułatwiają osiągnięcie celów projektowych integracji środowiska CLR z programem SQL Server.

Weryfikacja bezpieczeństwa typu

Kod bezpieczny pod kątem typu to kod, który uzyskuje dostęp do struktur pamięci tylko w dobrze zdefiniowany sposób. Na przykład, biorąc pod uwagę prawidłowe odwołanie do obiektu, bezpieczny kod typu może uzyskać dostęp do pamięci przy stałych przesunięciach odpowiadających rzeczywistym członkom pola. Jeśli jednak kod uzyskuje dostęp do pamięci przy dowolnym przesunięciu wewnątrz lub poza zakresem pamięci, który należy do obiektu, nie jest bezpieczny. Gdy zestawy są ładowane do środowiska CLR, przed skompilacją CIL przy użyciu kompilacji just in time (JIT), środowisko uruchomieniowe wykonuje fazę weryfikacji, która sprawdza kod w celu określenia bezpieczeństwa typu. Kod, który pomyślnie przejdzie tę weryfikację, jest nazywany weryfikowalnym kodem bezpiecznym dla typów.

Domeny aplikacji

ClR obsługuje pojęcie domen aplikacji jako stref wykonywania w procesie hosta, w którym można załadować i wykonać zestawy kodu zarządzanego. Granica domeny aplikacji zapewnia izolację między zestawami. Zestawy są izolowane pod względem widoczności zmiennych statycznych i elementów członkowskich danych oraz możliwości dynamicznego wywoływania kodu. Domeny aplikacji są również mechanizmem ładowania i zwalniania kodu. Kod można zwolnić tylko z pamięci, zwalniając domenę aplikacji. Aby uzyskać więcej informacji, zobacz Application Domains and CLR Integration Security.

Zabezpieczenia dostępu kodu (CAS)

System zabezpieczeń CLR umożliwia kontrolowanie, jakie rodzaje operacji może wykonywać kod, przypisując uprawnienia do kodu. Uprawnienia dostępu do kodu są przypisywane na podstawie tożsamości kodu (na przykład podpisu zestawu lub źródła kodu).

ClR zapewnia zasady dla całego komputera, które można ustawić przez administratora komputera. Te zasady definiują uprawnienia dla dowolnego kodu zarządzanego uruchomionego na maszynie. Ponadto istnieją zasady zabezpieczeń na poziomie hosta, które mogą być używane przez hosty, takie jak PROGRAM SQL Server, w celu określenia dodatkowych ograniczeń dotyczących kodu zarządzanego.

Jeśli zarządzany interfejs API w programie .NET Framework uwidacznia operacje na zasobach chronionych przez uprawnienie dostępu do kodu, interfejs API wymaga tego uprawnienia przed uzyskaniem dostępu do zasobu. To zapotrzebowanie powoduje, że system zabezpieczeń CLR wyzwoli kompleksowe sprawdzanie każdej jednostki kodu (zestawu) w stosie wywołań. Dostęp do zasobu jest udzielany tylko wtedy, gdy cały łańcuch wywołań ma uprawnienia.

Możliwość dynamicznego generowania kodu zarządzanego przy użyciu interfejsu API Reflection.Emit nie jest obsługiwana w środowisku hostowanym przez środowisko CLR w programie SQL Server. Taki kod nie miałby uprawnień cas do uruchomienia i w związku z tym kończyłby się niepowodzeniem w czasie wykonywania. Aby uzyskać więcej informacji, zobacz zabezpieczenia dostępu kodu integracji środowiska CLR.

Atrybuty ochrony hosta (HPA)

ClR udostępnia mechanizm dodawania adnotacji zarządzanych interfejsów API, które są częścią programu .NET Framework z pewnymi atrybutami, które mogą być interesujące dla hosta środowiska CLR. Przykłady takich atrybutów to:

  • SharedState, który wskazuje, czy interfejs API uwidacznia możliwość tworzenia stanu udostępnionego lub zarządzania nim (na przykład pól klas statycznych).

  • Synchronization, który wskazuje, czy interfejs API uwidacznia możliwość przeprowadzania synchronizacji między wątkami.

  • ExternalProcessMgmt, który wskazuje, czy interfejs API uwidacznia sposób kontrolowania procesu hosta.

Biorąc pod uwagę te atrybuty, host może określić listę HPAs, takich jak atrybut SharedState, który powinien być niedozwolony w środowisku hostowanym. W takim przypadku clR odrzuca próby wywołania interfejsów API, które są oznaczone przez HPA na liście zabronionej. Aby uzyskać więcej informacji, zobacz Atrybuty ochrony hosta i programowanie integracji CLR.

Jak działa program SQL Server i clR

W tej sekcji omówiono sposób, w jaki program SQL Server integruje modele zarządzania wątkami, planowania, synchronizacji i pamięci programu SQL Server oraz środowiska CLR. W szczególności ta sekcja analizuje integrację w świetle celów dotyczących skalowalności, niezawodności i zabezpieczeń. Program SQL Server zasadniczo działa jako system operacyjny środowiska CLR, gdy jest hostowany w programie SQL Server. ClR wywołuje procedury niskiego poziomu zaimplementowane przez program SQL Server do zarządzania wątkami, planowaniem, synchronizacją i pamięcią. Te procedury są tymi samymi typami pierwotnymi, których używa reszta aparatu programu SQL Server. Takie podejście zapewnia kilka korzyści ze skalowalności, niezawodności i zabezpieczeń.

Skalowalność: typowe wątki, planowanie i synchronizacja

ClR wywołuje interfejsy API programu SQL Server do tworzenia wątków, zarówno do uruchamiania kodu użytkownika, jak i do własnego użytku wewnętrznego. Aby przeprowadzić synchronizację między wieloma wątkami, clR wywołuje obiekty synchronizacji programu SQL Server. Dzięki temu harmonogram programu SQL Server może planować inne zadania, gdy wątek oczekuje na obiekt synchronizacji. Na przykład gdy clR inicjuje odzyskiwanie pamięci, wszystkie jego wątki czekają na zakończenie odzyskiwania pamięci. Ponieważ wątki CLR i obiekty synchronizacji, na które oczekują, są znane harmonogramowi programu SQL Server, program SQL Server może zaplanować wątki, które uruchamiają inne zadania bazy danych, które nie obejmują środowiska CLR. Umożliwia to również programowi SQL Server wykrywanie zakleszczeń obejmujących blokady podjęte przez obiekty synchronizacji CLR i stosowanie tradycyjnych technik usuwania zakleszczenia.

Kod zarządzany jest uruchamiany z wyprzedzeniem w programie SQL Server. Harmonogram programu SQL Server ma możliwość wykrywania i zatrzymywania wątków, które nie przyniosły przez znaczną ilość czasu. Możliwość przypinania wątków CLR do wątków programu SQL Server oznacza, że harmonogram programu SQL Server może identyfikować wątki "uciekające" w clR i zarządzać ich priorytetem. Takie wątki ucieczki są zawieszone i umieszczane w kolejce. Wątki, które są wielokrotnie identyfikowane jako wątki uciekające, nie mogą być uruchamiane przez dany okres czasu, aby inni wykonujący procesy robocze mogły działać.

Istnieją sytuacje, w których długotrwały kod zarządzany generuje automatycznie, a niektóre sytuacje, w których nie. W następujących sytuacjach długotrwały kod zarządzany daje automatycznie:

  • Jeśli kod wywołuje system operacyjny SQL (na przykład w celu wykonywania zapytań dotyczących danych)
  • Jeśli do wyzwalania odzyskiwania pamięci jest przydzielona wystarczająca ilość pamięci
  • Jeśli kod wchodzi w tryb preemptive, wywołując funkcje systemu operacyjnego

Kod, który nie wykonuje żadnej z tych akcji, takich jak ciasne pętle zawierające tylko obliczenia, nie dają automatycznie harmonogramu, co może prowadzić do długich oczekiwania na inne obciążenia w systemie. W takich sytuacjach deweloper musi jawnie uzyskać, wywołując funkcję System.Thread.Sleep() programu .NET Framework lub jawnie wprowadzając tryb preempcyjny z System.Thread.BeginThreadAffinity(), w dowolnych sekcjach kodu, które mają być długotrwałe. W poniższych przykładach kodu pokazano, jak ręcznie uzyskać wydajność przy użyciu każdej z tych metod.

Przykłady

Ręczne zwracanie do harmonogramu SOS

for (int i = 0; i < Int32.MaxValue; i++)
{
  // *Code that does compute-heavy operation, and does not call into
  // any OS functions.*

  // Manually yield to the scheduler regularly after every few cycles.
  if (i % 1000 == 0)
  {
    Thread.Sleep(0);
  }
}

Używanie wątkuAffinity do uruchamiania z preemptively

W tym przykładzie kod CLR jest uruchamiany w trybie preemptive w BeginThreadAffinity i EndThreadAffinity.

Thread.BeginThreadAffinity();
for (int i = 0; i < Int32.MaxValue; i++)
{
  // *Code that does compute-heavy operation, and does not call into
  // any OS functions.*
}
Thread.EndThreadAffinity();

Skalowalność: typowe zarządzanie pamięcią

ClR wywołuje elementy pierwotne programu SQL Server do przydzielania i cofania przydziału pamięci. Ponieważ pamięć używana przez clR jest uwzględniana w całkowitym użyciu pamięci systemu, program SQL Server może pozostać w skonfigurowanych limitach pamięci i upewnić się, że clR i SQL Server nie konkurują ze sobą dla pamięci. Program SQL Server może również odrzucać żądania pamięci CLR, gdy pamięć systemowa jest ograniczona, i poprosić CLR o zmniejszenie użycia pamięci, gdy inne zadania wymagają pamięci.

Niezawodność: domeny aplikacji i nieodwracalne wyjątki

Gdy kod zarządzany w interfejsach API programu .NET Framework napotyka krytyczne wyjątki, takie jak nadmiar pamięci lub przepełnienie stosu, nie zawsze jest możliwe odzyskanie sprawności po takich awariach i zapewnienie spójnej i poprawnej semantyki dla ich implementacji. Te interfejsy API zgłaszają wyjątek przerwania wątku w odpowiedzi na te błędy.

W przypadku hostowania w programie SQL Server takie przerwania wątków są obsługiwane w następujący sposób: CLR wykrywa stan współużytkowany w domenie aplikacji, w której występuje przerwanie wątku. ClR wykrywa to, sprawdzając obecność obiektów synchronizacji. Jeśli w domenie aplikacji występuje stan współużytkowany, sama domena aplikacji zostanie zwolniona. Zwalnianie domeny aplikacji zatrzymuje transakcje bazy danych, które są obecnie uruchomione w tej domenie aplikacji. Ponieważ obecność stanu współużytkowanego może poszerzyć wpływ takich krytycznych wyjątków na sesje użytkowników innych niż ten, który wyzwolił wyjątek, program SQL Server i clR podjął kroki w celu zmniejszenia prawdopodobieństwa wystąpienia stanu udostępnionego. Aby uzyskać więcej informacji, zobacz .NET Framework.

Zabezpieczenia: zestawy uprawnień

Program SQL Server umożliwia użytkownikom określenie wymagań dotyczących niezawodności i zabezpieczeń kodu wdrożonego w bazie danych. Gdy zestawy są przekazywane do bazy danych, autor zestawu może określić jeden z trzech zestawów uprawnień dla tego zestawu: SAFE, EXTERNAL_ACCESSi UNSAFE.

Funkcjonalność SAFE EXTERNAL_ACCESS UNSAFE
Code Access Security Wykonaj tylko Wykonywanie i dostęp do zasobów zewnętrznych Nieograniczony
Programming model restrictions Tak Tak Brak ograniczeń
Verifiability requirement Tak Tak Nie
Ability to call native code Nie Nie Tak

SAFE jest najbardziej niezawodnym i bezpiecznym trybem ze skojarzonymi ograniczeniami w zakresie dozwolonego modelu programowania. SAFE zestawy mają wystarczające uprawnienia do uruchamiania, wykonywania obliczeń i uzyskiwania dostępu do lokalnej bazy danych. SAFE zestawy muszą być bezpieczne w sposób weryfikowalny i nie mogą wywoływać niezarządzanych kodów.

UNSAFE jest przeznaczony dla wysoce zaufanego kodu, który można utworzyć tylko przez administratorów bazy danych. Ten zaufany kod nie ma ograniczeń zabezpieczeń dostępu do kodu i może wywoływać kod niezarządzany (natywny).

EXTERNAL_ACCESS zapewnia pośrednią opcję zabezpieczeń, umożliwiając kodowi dostęp do zasobów spoza bazy danych, ale nadal ma gwarancje niezawodności SAFE.

Program SQL Server używa warstwy zasad CAS na poziomie hosta do konfigurowania zasad hosta, które udzielają jednego z trzech zestawów uprawnień na podstawie zestawu uprawnień przechowywanych w wykazach programu SQL Server. Kod zarządzany uruchomiony wewnątrz bazy danych zawsze pobiera jeden z tych zestawów uprawnień dostępu do kodu.

Ograniczenia modelu programowania

Model programowania dla kodu zarządzanego w programie SQL Server obejmuje pisanie funkcji, procedur i typów, które zwykle nie wymagają użycia stanu przechowywanego w wielu wywołaniach ani udostępniania stanu w wielu sesjach użytkownika. Ponadto, jak opisano wcześniej, obecność stanu udostępnionego może spowodować krytyczne wyjątki wpływające na skalowalność i niezawodność aplikacji.

Biorąc pod uwagę te zagadnienia, zniechęcimy do używania zmiennych statycznych i statycznych składowych danych klas używanych w programie SQL Server. W przypadku zestawów SAFE i EXTERNAL_ACCESS program SQL Server analizuje metadane zestawu w CREATE ASSEMBLY czasie i kończy się niepowodzeniem podczas tworzenia takich zestawów, jeśli znajdzie użycie elementów członkowskich i zmiennych danych statycznych.

Program SQL Server nie zezwala również na wywołania interfejsów API programu .NET Framework, które są oznaczone za pomocą atrybutów ochrony hosta SharedState, Synchronizationi ExternalProcessMgmt. Uniemożliwia to SAFE i zestawom EXTERNAL_ACCESS wywoływanie wszystkich interfejsów API, które umożliwiają udostępnianie stanu, wykonywanie synchronizacji i wpływanie na integralność procesu programu SQL Server. Aby uzyskać więcej informacji, zobacz ograniczenia modelu programowania integracji CLR.