Gehostete CLR-Umgebung

Die Microsoft .NET Framework-CLR (Common Language Runtime) ist eine Umgebung, die viele moderne Programmiersprachen ausführt, einschließlich Microsoft Visual C#, Microsoft Visual Basic und Microsoft Visual C++. Die CLR bietet der Garbage Collection unterworfenen Arbeitsspeicher, präemptives Threading, Metadatendienste (geben Sie „reflection“ ein), Codeüberprüfbarkeit und Codezugriffssicherheit. Die CLR verwendet Metadaten zum Suchen und Laden von Klassen, Anordnen von Instanzen im Speicher, Auflösen von Methodenaufrufen, Generieren von systemeigenem Code, Erzwingen von Sicherheit und zum Festlegen von Begrenzungen im Laufzeitkontext.

Die CLR und SQL Server unterscheiden sich als Laufzeitumgebungen in der Weise, wie sie Arbeitsspeicher, Threads und Synchronisierung behandeln. Dieses Thema beschreibt, wie die beiden Laufzeiten integriert werden, sodass alle Systemressourcen einheitlich verwaltet werden. Ferner wird hier behandelt, wie CLR-Codezugriffssicherheit (CAS) und SQL Server-Sicherheit integriert werden, um eine zuverlässige und sichere Ausführungsumgebung für Benutzercode bereitzustellen.

Grundlegende Konzepte der CLR-Architektur

In .NET Framework schreibt ein Programmierer in einer Hochsprache, die eine Klasse implementiert, die die Struktur (z. B. die Felder oder Eigenschaften der Klasse) und Methoden definiert. Einige dieser Methoden können statische Funktionen sein. Bei der Kompilierung des Programms wird eine Datei erstellt, eine so genannte Assembly, die den kompilierten Code in der Microsoft Intermediate Language (MSIL) sowie ein Manifest enthält, das alle Verweise auf abhängige Assemblys umfasst.

HinweisHinweis

Assemblys sind ein wichtiges Element in der Architektur der CLR. Sie sind die Einheiten für Verpackung, Bereitstellung und Versionsverwaltung des Anwendungscodes in .NET Framework. Durch die Verwendung von Assemblys können Sie Anwendungscode in der Datenbank verfügbar machen und eine einheitliche Methode zur Verwaltung, Sicherung und Wiederherstellung kompletter Datenbankanwendungen bereitstellen.

Das Assemblymanifest enthält Metadaten über die Assembly, die sämtliche Strukturen, Felder, Eigenschaften, Klassen, Vererbungsbeziehungen, Funktionen und Methoden beschreiben, die im Programm definiert sind. Das Manifest legt die Identität der Assembly fest, gibt die Dateien an, aus denen die Assemblyimplementierung besteht, gibt die Typen und Ressourcen an, aus denen die Assembly besteht, legt die Abhängigkeiten von anderen Assemblys für die Kompilierungszeit einzeln fest und gibt den Berechtigungssatz an, der für die ordnungsgemäße Ausführung der Assembly erforderlich ist. Diese Informationen werden zur Laufzeit verwendet, um Verweise aufzulösen, Versionsbindungsrichtlinien zu erzwingen und die Integrität geladener Assemblys zu validieren.

.NET Framework unterstützt benutzerdefinierte Attribute zum Hinzufügen von Anmerkungen zu Klassen, Eigenschaften, Funktionen und Methoden in Form von zusätzlichen Informationen, die von der Anwendung in Metadaten erfasst werden können. Alle .NET Framework-Compiler verwenden diese Anmerkungen ohne Auslegung und speichern sie als Assemblymetadaten. Diese Anmerkungen können auf die gleiche Weise wie beliebige andere Metadaten untersucht werden.

Verwalteter Code ist MSIL-Code, der in der CLR anstatt direkt vom Betriebssystem ausgeführt wird. Anwendungen mit verwaltetem Code verfügen über CLR-Dienste, z. B. automatische Garbage Collection, Typüberprüfung zur Laufzeit, Sicherheitsunterstützung usw. Diese Dienste stellen ein einheitliches plattform- und sprachunabhängiges Verhalten von Anwendungen mit verwaltetem Code bereit.

Entwurfsziele der CLR-Integration

Bei der Ausführung von Benutzercode in der CLR-gehosteten Umgebung in SQL Server (CLR-Integration genannt), gelten die folgenden Entwurfsziele:

Zuverlässigkeit (Sicherheit)

Vom Benutzercode sollten keine Vorgänge ausgeführt werden dürfen, die die Integrität des Prozesses des Datenbankmoduls gefährden, z. B. die Anzeige eines Meldungsfelds, in dem eine Benutzerantwort verlangt wird, oder in dem der Prozess beendet wird. Benutzercode sollte nicht in der Lage sein, Arbeitsspeicherpuffer des Datenbankmoduls oder interne Datenstrukturen zu überschreiben.

Skalierbarkeit

SQL Server und die CLR verfügen über unterschiedliche interne Modelle zur Planung und Arbeitsspeicherverwaltung. SQL Server unterstützt ein kooperatives, nicht präemptives Threadingmodell, das bei den Threads zur freiwilligen Ausführung in regelmäßigen Abständen oder beim Warten auf Sperren oder E/A führt. Die CLR unterstützt ein präemptives Threadingmodell. Wenn die Threadinggrundelemente des Betriebssystems direkt von Benutzercode aufgerufen werden können, der in SQL Server ausgeführt wird, ist keine einwandfreie Integration in den SQL Server-Taskplaner möglich, und die Skalierbarkeit des Systems kann beeinträchtigt werden. Die CLR macht keinen Unterschied zwischen virtuellem und physischem Arbeitsspeicher, SQL Server verwaltet physischen Arbeitsspeicher jedoch direkt und muss physischen Arbeitsspeicher innerhalb eines konfigurierbaren Grenzwerts verwenden.

Die unterschiedlichen Modelle für Threading, Planung und Arbeitsspeicherverwaltung stellen eine Integrationsherausforderung für ein relationales Datenbankverwaltungssystem (RDBMS) dar, das durch Skalierung Tausende von gleichzeitigen Benutzersitzungen unterstützt. Durch die Architektur sollte sichergestellt werden, dass die Skalierbarkeit des Systems nicht durch Benutzercode beeinträchtigt wird, der APIs (Application Programming Interfaces, Schnittstellen zur Anwendungsprogrammierung) für Threading-, Arbeitsspeicher- und Synchronisierungsgrundelemente direkt aufruft.

Sicherheit

In der Datenbank ausgeführter Benutzercode muss beim Zugreifen auf Datenbankobjekte, wie Tabellen und Spalten, SQL Server-Authentifizierungs- und Autorisierungsregeln folgen. Darüber hinaus sollten Datenbankadministratoren in der Lage sein, den Zugriff auf Ressourcen des Betriebssystems, wie Dateien und Netzwerkzugriff, vom Benutzercode aus zu steuern, der in der Datenbank ausgeführt wird. Dies ist wichtig, da verwaltete Programmiersprachen (im Gegensatz zu nicht verwalteten Sprachen wie Transact-SQL) APIs zum Zugreifen auf diese Ressourcen bereitstellen. Das System muss eine sichere Methode bereitstellen, damit über Benutzercode außerhalb des Database Engine (Datenbankmodul)-Prozesses auf Computerressourcen zugegriffen werden kann. Weitere Informationen finden Sie unter Sicherheit der CLR-Integration.

Leistung

In Database Engine (Datenbankmodul) ausgeführter verwalteter Benutzercode sollte über eine vergleichbare Ausführungsleistung verfügen wie derselbe Code, der außerhalb des Servers ausgeführt wird. Datenbankzugriff von verwaltetem Benutzercode ist nicht so schnell wie systemeigener Transact-SQL. Weitere Informationen finden Sie unter Leistungsfähigkeit der CLR-Integration.

CLR-Dienste

Die CLR bietet eine Reihe von Diensten, um die Entwurfsziele der CLR-Integration mit SQL Server zu erreichen.

Typsicherheitsüberprüfung

Als typsicherer Code wird Code bezeichnet, der nur auf genau definierte Weise auf Arbeitsspeicherstrukturen zugreift. Bei typsicherem Code ist z. B. bei einem gültigen Objektverweis der Speicherzugriff an festen Offsets möglich, die tatsächlichen Feldmembern entsprechen. Wenn der Code jedoch auf den Speicher an beliebigen Offsets innerhalb oder außerhalb des Gültigkeitsbereichs des Speichers zugreift, der zu dem Objekt gehört, ist er nicht typsicher. Wenn Assemblys in die CLR geladen werden, bevor die MSIL mithilfe der JIT-Kompilierung (Just-In-Time) kompiliert wurde, führt die Laufzeit eine Überprüfungsphase aus, die den Code hinsichtlich seiner Typsicherheit untersucht. Code, der diese Überprüfung erfolgreich besteht, wird nachweisbar typsicherer Code genannt.

Anwendungsdomänen

Die CLR unterstützt die Definition von Anwendungsdomänen als Ausführungszonen in einem Hostprozess, bei dem verwaltete Codeassemblys geladen und ausgeführt werden können. Die Anwendungsdomänengrenze bietet Isolierung zwischen Assemblys. Die Assemblys werden hinsichtlich der Sichtbarkeit von statischen Variablen und Datenelementen und der Möglichkeit, Code dynamisch aufzurufen, isoliert. Anwendungsdomänen sind auch der Mechanismus zum Laden und Entladen von Code. Code kann aus dem Arbeitsspeicher durch das Entladen der Anwendungsdomäne entfernt werden. Weitere Informationen finden Sie unter Anwendungsdomänen und Sicherheit der CLR-Integration.

Codezugriffssicherheit (Code Access Security, CAS)

Das CLR-Sicherheitssystem bietet eine Methode zum Steuern der Vorgangstypen, die von verwaltetem Code ausgeführt werden können, indem dem Code Berechtigungen zugewiesen werden. Codezugriffsberechtigungen werden auf der Grundlage der Identität des Codes (z. B. die Signatur der Assembly oder die Quelle des Codes) zugewiesen.

Die CLR sorgt für eine Richtlinie, die auf dem gesamten Computer gilt und vom Computeradministrator festgelegt werden kann. Diese Richtlinie definiert die Berechtigungen für den gesamten verwalteten Code, der auf dem Computer ausgeführt wird. Ferner besteht eine Sicherheitsrichtlinie auf Hostebene, die vom Host verwendet werden kann, z. B. SQL Server zum Festlegen zusätzlicher Einschränkungen des verwalteten Codes.

Wenn eine verwaltete API in .NET Framework Operationen für Ressourcen verfügbar macht, die durch eine Codezugriffsberechtigung geschützt sind, fordert die API vor dem Zugriff auf die Ressource diese Berechtigung an. Diese Anforderung löst im CLR-Sicherheitssystem eine umfangreiche Überprüfung jeder Codeeinheit (Assembly) in der Aufrufliste aus. Nur wenn die ganze Aufrufkette über eine Berechtigung verfügt, wird der Zugriff auf die Ressource gewährt.

Beachten Sie, dass die Funktion zum dynamischen Generieren von verwaltetem Code mit der Reflektionsausgabe-API innerhalb der CLR-gehosteten Umgebung in SQL Server nicht unterstützt wird. Solcher Code hätte die CAS-Berechtigungen zum Ausführen nicht und würde deshalb zur Laufzeit fehlschlagen. Weitere Informationen finden Sie unter CLR-Integration und Codezugriffssicherheit.

Hostschutzattribute

Die CLR stellt einen Mechanismus zur Verfügung, um verwaltete APIs, die Teil von .NET Framework sind, mit Anmerkungen in Form von bestimmten Attributen zu versehen, die für einen Host der CLR interessant sein können. Beispiele für solche Attribute:

  • SharedState - gibt an, ob die API die Fähigkeit zur Verfügung stellt, einen Freigabezustand (z. B. statische Klassenfelder) zu erstellen oder zu verwalten.

  • Synchronization - gibt an, ob die API die Fähigkeit zur Verfügung stellt, die Synchronisierung zwischen Threads auszuführen.

  • ExternalProcessMgmt - gibt an, ob die API eine Möglichkeit zur Kontrolle des Hostprozesses zur Verfügung stellt.

Anhand dieser Attribute kann der Host eine Liste mit Hostschutzattributen festlegen, wie das SharedState-Attribut, die in der gehosteten Umgebung nicht zugelassen werden. In diesem Fall weist die CLR Versuche des Benutzercodes zurück, APIs aufzurufen, die mit nicht zugelassenen Hostschutzattributen aus der Liste versehen sind. Weitere Informationen finden Sie unter Hostschutzattribute und Programmierung der CLR-Integration.

Zusammenarbeit von SQL Server und der CLR

In diesem Abschnitt wird besprochen, wie SQL Server die Modelle für Threading, Planung, Synchronisierung und Arbeitsspeicherverwaltung von SQL Server und der CLR integriert. Dieser Abschnitt untersucht insbesondere die Integration vor dem Hintergrund der Skalierbarkeit, Zuverlässigkeit und der Sicherheitsziele. SQL Server fungiert im Wesentlichen als Betriebssystem für die CLR, wenn sie innerhalb von SQL Server gehostet wird. Die CLR ruft von SQL Server für Threading, Planung, Synchronisierung und Speicherverwaltung implementierte Routinen auf niedriger Ebene auf. Diese sind dieselben Grundelemente, die vom übrigen SQL Server-Modul verwendet werden. Dieser Ansatz bietet mehrere Vorteile für Skalierbarkeit, Zuverlässigkeit und Sicherheit.

Skalierbarkeit: Allgemeines Threading, Planung und Synchronisierung

CLR ruft SQL Server-APIs zur Erstellung von Threads auf, sowohl für ausgeführten Benutzercode als auch zur eigenen internen Verwendung. Um zwischen mehreren Threads zu synchronisieren, ruft die CLR SQL Server-Synchronisierungsobjekte auf. Dadurch kann das SQL Server-Zeitplanungsmodul andere Aufgaben planen, wenn ein Thread auf ein Synchronisierungsobjekt wartet. Wenn die CLR beispielsweise die Garbage Collection initiiert, warten alle zugehörigen Threads auf die Fertigstellung der Garbage Collection. Da das SQL Server-Zeitplanungsmodul die CLR-Threads und die Synchronisierungsobjekte, auf die sie warten, kennt, kann SQL Server Threads planen, die andere Datenbankaufgaben ausführen, bei denen die CLR nicht benötigt wird. Ferner kann SQL Server dadurch Deadlocks erkennen, an denen von CLR-Synchronisierungsobjekten benötigte Sperren beteiligt sind, und traditionelle Techniken zur Entfernung von Deadlocks einsetzen.

Verwalteter Code wird präemptiv in SQL Server ausgeführt. Das SQL Server-Zeitplanungsmodul kann Threads, die über eine längere Zeitspanne nicht aktiv waren, erkennen und anhalten. Die Funktion zum Verknüpfen von CLR-Threads mit SQL Server-Threads beinhaltet, dass das SQL Server-Zeitplanungsmodul „Ausreißerthreads“ in der CLR erkennen und deren Priorität verwalten kann. Solche Ausreißerthreads werden angehalten und in die Warteschlange zurückgestellt. Threads, die mehrfach als Ausreißerthreads identifiziert werden, dürfen für einen gewissen Zeitraum nicht ausgeführt werden, damit andere Arbeitsthreads ausgeführt werden können.

HinweisHinweis

Verwalteter Code mit langer Laufzeit, der auf Daten zugreift oder genug Arbeitsspeicher zuordnet, um Garbage Collection auszulösen, wird automatisch aktiv. Verwalteter Code mit langer Laufzeit, der nicht auf Daten zugreift oder nicht genug Arbeitsspeicher zuweist, um Garbage Collection auszulösen, sollte explizit durch Aufrufen der Funktion System.Thread.Sleep() von .NET Framework aktiv werden.

Skalierbarkeit: Allgemeine Arbeitsspeicherverwaltung

Die CLR ruft SQL Server-Grundelemente zum Zuordnen und zum Aufheben der Zuordnung von Arbeitsspeicher auf. Da der von der CLR verwendete Arbeitsspeicher in der Gesamtarbeitsspeicherverwendung des Systems eingerechnet ist, kann SQL Server innerhalb der konfigurierten Speichergrenzen bleiben und sicherstellen, dass die CLR und SQL Server sich nicht gegenseitig Arbeitsspeicher streitig machen. SQL Server kann CLR-Speicheranfragen auch zurückweisen, wenn der Systemspeicher beschränkt ist, und die Reduzierung von Speicher, der von der CLR verwendet wird, veranlassen, wenn andere Aufgaben Speicher benötigen. 

Zuverlässigkeit: Anwendungsdomänen und nicht behebbare Ausnahmen

Wenn in verwaltetem Code in .NET Framework-APIs schwerwiegende Ausnahmen auftreten, wie nicht genügend Arbeitsspeicher oder Stapelüberlauf, ist es nicht immer möglich, diese Fehler zu beheben und konsistente und korrekte Semantik für die Implementierung zu gewährleisten. Diese APIs lösen als Reaktion auf diese Fehler eine Threadabbruchausnahme aus.

Beim Hosten in SQL Server werden solche Threadabbrüche folgendermaßen behandelt: Die CLR erkennt jeden Freigabezustand in der Anwendungsdomäne, in der der Threadabbruch auftritt. Die CLR überprüft dazu das Vorhandensein von Synchronisierungsobjekten. Wenn es einen Freigabezustand in der Anwendungsdomäne gibt, dann wird die Anwendungsdomäne selbst entladen. Die Entladung aus der Anwendungsdomäne beendet Datenbanktransaktionen, die gerade in dieser Anwendungsdomäne ausgeführt werden. Da das Vorhandensein eines Freigabezustands die Auswirkungen dieser schwerwiegenden Ausnahmen auf Benutzersitzungen außer der Sitzung, die die Ausnahme ausgelöst hat, vergrößern kann, haben SQL Server und die CLR Schritte unternommen, um die Wahrscheinlichkeit eines Freigabezustands zu verringern. Weitere Informationen hierzu finden Sie in der .NET Framework- Dokumentation.

Sicherheit: Berechtigungssätze

SQL Server-Benutzer können die Zuverlässigkeits- und Sicherheitsanforderungen für Code angeben, der in der Datenbank bereitgestellt wird. Beim Hochladen von Assemblys in die Datenbank kann der Verfasser der Assembly für diese einen von drei Berechtigungssätzen angeben: SAFE, EXTERNAL_ACCESS und UNSAFE.

Berechtigungssatz

SAFE

EXTERNAL_ACCESS

UNSAFE

Codezugriffssicherheit

Nur ausführen

Ausführen + Zugriff auf externe Ressourcen

Uneingeschränkt

Beschränkungen des Programmiermodells

Ja

Ja

Keine Einschränkungen

Überprüfbarkeit erforderlich

Ja

Ja

Nein

Aufrufbarkeit von systemeigenem Code

Nein

Nein

Ja

SAFE ist der zuverlässigste und sicherste Modus, der mit Einschränkungen hinsichtlich des zulässigen Programmiermodells einhergeht. Assemblys der Stufe SAFE verfügen über ausreichende Berechtigungen für die Ausführung, die Durchführung von Berechnungen und den Zugriff auf die lokale Datenbank. Assemblys der Stufe SAFE müssen nachweislich typsicher sein und dürfen keinen nicht verwalteten Code aufrufen.

UNSAFE ist für hoch vertrauenswürdigen Code vorgesehen, der nur von Datenbankadministratoren erstellt werden kann. Dieser vertrauenswürdige Code verfügt über keine Codezugriffs-Sicherheitsbeschränkungen und kann nicht verwalteten (systemeigenen) Code aufrufen.

EXTERNAL_ACCESS stellt eine mittlere Sicherheitsebene dar, bei der Code auf Ressourcen außerhalb der Datenbank zugreifen kann, die aber dennoch so zuverlässig wie SAFE ist.

In SQL Server wird mithilfe der auf Hostebene festgelegten Stufe der CAS-Richtlinie eine Hostrichtlinie eingerichtet, die anhand des in den SQL Server-Katalogen gespeicherten Berechtigungssatzes einen von drei Berechtigungssätzen gewährt. Verwaltetem Code, der in der Datenbank ausgeführt wird, wird immer einer dieser Codezugriffsberechtigungssätze abgerufen.

Beschränkungen des Programmiermodells

Im Programmiermodell für verwalteten Code in SQL Server werden Funktionen zum Schreiben, Prozeduren und Typen benötigt, für die normalerweise weder der über mehrere Aufrufe gespeicherte Zustand noch die Freigabe des Zustands über mehrere Benutzersitzungen erforderlich sind. Darüber hinaus kann ein Freigabezustand, wie bereits erläutert, zu schwerwiegenden Ausnahmen führen, die die Skalierbarkeit und Zuverlässigkeit der Anwendung gefährden können.

Aus diesen Gründen raten wir von der Verwendung von statischen Variablen und Datenelementen von Klassen ab, die in SQL Server verwendet werden. Bei Assemblys vom Typ SAFE und EXTERNAL_ACCESS untersucht SQL Server bei der Ausführung von CREATE ASSEMBLY die Metadaten der Assembly. Wenn die Verwendung statischer Datenelemente oder Variablen ermittelt wird, wird die Assembly nicht erstellt.

Darüber hinaus ist in SQL Server das Aufrufen von .NET Framework-APIs nicht möglich, die mit den Hostschutzattributen SharedState, Synchronization und ExternalProcessMgmt versehen sind. Dies verhindert, dass Assemblys des Typs SAFE und EXTERNAL_ACCESS APIs aufrufen, die die Freigabe des Zustands aktivieren, Synchronisierungen durchführen und die Integrität des SQL Server-Prozesses beeinträchtigen. Weitere Informationen finden Sie unter Beschränkungen des Programmiermodells für die CLR-Integration.