Projekt Rotor: Microsofts .NET-Plattform für Unix-Derivate

Veröffentlicht: 22. Aug 2002 | Aktualisiert: 22. Jun 2004
Von Mirko Matytschak

Mit dem Rotor-Projekt veröffentlicht Microsoft den Quellcode für eine Implementierung der Common Language Infrastructure (CLI). Sie ist Grundlage der .NET-Bibliotheken. Damit ist die technische Grundlage für die Portierung von .NET auf beliebige Betriebssysteme gegeben.

* * *

Auf dieser Seite

Download von Rotor Download von Rotor
So bringen Sie Rotor zum Laufen So bringen Sie Rotor zum Laufen
Inbetriebnahme von Rotor Inbetriebnahme von Rotor
Die Shared-Source-Lizenz Die Shared-Source-Lizenz

Hauptsächlich kann Ihnen Rotor helfen, die Common Language Infrastructure besser zu verstehen. Als Beispiel dienen im Folgenden die Default-Implementierungen der Funktionen GetHashCode und Equals. Tools wie Anakrino geben bei der normalen .NET-Implementierung nur an, dass diese Funktionen nativ implementiert worden sind. In der Datei sscli\CLR\SRC\BCL\SYSTEM\object.cs, der Quelldatei für die Klasse System.Object, findet sich ebenfalls nur die Extern-Deklaration einer nativen Methode. Die Lösung des Rätsels findet sich in der Datei sscli\CLR\SRC\VM\Comobject.cpp (Listing 1). Bei der Namensgebung dieser Datei und an vielen Stellen in den Sourcen zeigt sich übrigens, dass .NET früher als eine neue Version von COM verstanden wurde.

//
// Compare by ref for normal classes, by value for value types.
//  
//
FCIMPL2(BOOL, ObjectNative::Equals, Object *pThisRef, Object *pCompareRef)
{
    if (pThisRef == pCompareRef)    
        return TRUE;
    // Since we are in FCALL, we must handle NULL specially.
    if (pThisRef == NULL || pCompareRef == NULL)
        return FALSE;
    MethodTable *pThisMT = pThisRef->GetMethodTable();
    // If it's not a value class, don't compare by value
    if (!pThisMT->IsValueClass())
        return FALSE;
    // Make sure they are the same type.
    if (pThisMT != pCompareRef->GetMethodTable())
        return FALSE;
    // Compare the contents (size - vtable - sink block index).
    BOOL ret = !memcmp((void *) (pThisRef+1), (void *) (pCompareRef+1), 
      pThisRef->GetMethodTable()->GetBaseSize() - sizeof(Object) - sizeof(int));
    FC_GC_POLL_RET();
    return ret;
}
FCIMPLEND
// Note that we obtain a sync block index without actually building a sync block.
// That's because a lot of objects are hashed, without requiring support for
FCIMPL1(INT32, ObjectNative::GetHashCode, Object* or) {
    if (or == 0)
        return 0;
    VALIDATEOBJECTREF(or);
    DWORD      idx = or->GetSyncBlockIndex();
    _ASSERTE(idx != 0);
    // If the syncblock already exists, it has now become precious.  Otherwise the
    // hash code would not be stable across GCs.
    SyncBlock *psb = or->PassiveGetSyncBlock();
    if (psb)
        psb->SetPrecious();
    return idx;
}
FCIMPLEND

Listing 1: Die Implementierungen von Equals und GetHashCode

Value Types werden bitweise verglichen, während es bei Objekttypen ausreicht, zu prüfen, ob die beiden übergebenen Zeiger auf das gleiche Objekt zeigen. Sind die Zeiger beider Objekte gleich, gibt Equals true zurück. Ist eines der beiden Objekte null, ist das Resultat false. Handelt es sich jetzt nicht um einen Value Type, dann ist das Ergebnis ebenfalls false.

Nun wird getestet, ob beide Objekte vom gleichen Typ sind. Ist dies der Fall, verfügen sie über die gleiche Methodentabelle. Es wird also der Zeiger auf die Methodentabelle beider Objekte ermittelt und verglichen. Sind sie ungleich, ist das Resultat false. Nun werden die tatsächlichen Speicherorte beider Objekte ermittelt und bitweise verglichen.

Die Implementierung für GetHashCode ist einfacher. Für jedes Objekt ist im System ein sogenannter Sync-Block vorgesehen, wobei nur die wenigsten Objekte ihn benötigen. Der Sync-Block wird auf Anforderung angelegt. Der Hash-Code entspricht dem Index im Sync-Block, womit er für jedes Objekt unverwechselbar ist.

Neben dem Studium der Funktionsweise von Rotor lassen sich auch interessante Projekte mit Rotor verwirklichen. Im Rotor Diskussionsforum von DevelopMentor wurden dazu einige Vorschläge diskutiert:

  • Schreiben eines Apache-Moduls, das Rotor und damit Managed-Code-Module hosten kann, damit man eine Art ASP.NET für Apache zur Verfügung hat

  • Hosting von Rotor innerhalb einer Open-Source-Datenbank, damit man stored Procedures mit C# schreiben kann (eine ähnliche Implementierung wird wohl Bestandteil der nächsten Version des SQL Servers werden)

  • Hosting von Rotor in Open-Source-Browsern, damit man auf der Client-Seite .NET-Assemblies unterstützen kann

  • Interoperabilität mit Gnome oder Corba in der Art der COM-Interop von .NET

Natürlich wurde auch gleich die Portierung der FreeBSD-Version auf Linux in Angriff genommen.

Microsoft selbst sieht Rotor als eine Plattform für Forschung auf Basis von .NET. So wurde bei Microsoft ein Job für eine Person ausgeschrieben, die Forschungsaktivitäten unterstützt und die Kommunikation in diesem Bereich fördert. In diesem Zusammenhang ist es interessant, sich die Forschungsarbeiten von Microsoft Research anzusehen. Zum Beispiel wurde die CLI erweitert, um Sprachen mit Templates oder funktionellen Sprachen wie OCaml zu unterstützen - es gibt einen Dialekt von OCaml unter dem Namen F# (FSharp), den man sich von Microsoft Research herunterladen kann.

Ohne die Sourcen von .NET sind solche Forschungsarbeiten undenkbar. Mit Rotor jedoch kann sich jeder daran beteiligen. Es kann also durchaus sein, dass Forschungsergebnisse, die auf Basis von Rotor erzielt wurden, in spätere Versionen von .NET einfließen - denn .NET ist ein offener Standard, zu dem jeder beitragen kann.

Download von Rotor

Die Microsoft® Shared Source CLI -Implementierung steht unter http://www.microsoft.com/downloads/details.aspx?FamilyId=8C09FD61-3F26-4555-AE17-3121B4F51D4D&displaylang;=en kostenlos zum Download zur Verfügung.

Es ist ein tgz-Archiv, das einen funktionierenden Build für die CLI nach dem ECMA-335-Standard und einen C#-Compiler nach ECMA-334 enthält. Darüber hinaus finden sich noch einige Tools zum Testen von Anwendungen in dem Paket, sowie Beispielprogramme. Das Archiv lässt sich mit Winzip entpacken.

Wer sich das Paket auf den Rechner lädt, hat danach 110 Megabyte Daten in 351 Dateien und 38 Ordnern zusätzlich im Dateisystem. Zum Kompilieren empfiehlt sich ein System mit mindestens Pentium III und 256 MByte Hauptspeicher. Zur Kompilierung der nahezu zwei Millionen Codezeilen benötigt ein PIII-600-Rechner mit 256 MByte Speicher ca. 30 Minuten.

So bringen Sie Rotor zum Laufen

Auf einem Windows-2000-System klappte das Kompilieren ohne Probleme. Allerdings benötigen Sie zurzeit ein Visual Studio, um das System zu kompilieren. Das Rotor-Team arbeitet daran, die Verflechtung der Header-Dateien des Windows-SDKs soweit zu entschlüsseln, dass das System in Zukunft mit einem SDK-Compiler verarbeitbar ist. Im Augenblick ist dies jedoch nicht der Fall.

So kommen Sie zu einem Windows-Build
Das Entpacken der tgz-Datei erzeugt ein Verzeichnis sscli im Zielverzeichnis. Innerhalb dieses Verzeichnisses befindet sich ein Batch buildall.cmd. Öffnen Sie ein Konsolenfenster und stellen Sie sicher, dass die Kommandozeilenversion des C++-Compilers zur Verfügung steht. Dazu gibt es den Batch vsvars32.bat, der im Verzeichnis Common7\Tools Ihres Visual Studio-Verzeichnisses liegt. Nun wechseln Sie ins Sscli-Verzeichnis, rufen den Batch env.bat auf und dann Buildall. Am Bildschirm sollte nun folgende Meldung erscheinen:

--- Building the PAL ---
Build successful.

Danach kommen eine Menge Meldungen über Dateien, die kompiliert und mit einem so genannten Binplace-Tool bearbeitet werden. Das Ende der Bearbeitung sieht etwa so aus:

Binplacing - grep\grep\objdf\rotor_x86\ngrep.exe for all platforms
BUILD: Done
    6 files binplaced

Dies ist für Sie das Zeichen, dass die CLI-Implementierung fertiggestellt ist.

So kommen Sie zu einem FreeBSD-Build
Sollten Sie Rotor unter FreeBSD kompilieren wollen, benötigen Sie einen GNU C++-Compiler oder ein Äquivalent. Dann gehen Sie folgendermaßen vor: Wenn Sie sh oder bash benutzen, stellen Sie das Environment mit env.sh ein. Bei csh oder tcsh verwenden Sie env.csh. Diese Batches müssen Sie vorher überprüfen, damit der Compiler gefunden werden kann.

Dann starten Sie mit ./buildall den Build. Es kommen etwa folgende Meldungen:

creating cache ./config.cache
checking whether make sets ${MAKE}... yes
checking for gcc... gcc
checking whether the C compiler (gcc  ) works... yes
checking whether the C compiler (gcc  ) is a cross-compiler... no

Nach einer längeren Weile kommen die Binplace-Meldungen wie im Windows Build. Damit ist der Build fertig. Die ausführbaren Dateien und Tools liegen unter sscli\build\v1.x86fstchk.rotor. Für weitere Arbeiten mit Rotor ist es sinnvoll, diesen Pfad in der PATH-Environment-Variablen von Windows vorliegen zu haben (bzw. env.bat zu benutzen).

Es gibt eine Test-Suite für den Build, die allerdings als Perl-Script abgelegt ist. Damit das läuft, benötigen Sie Perl. Unter FreeBSD ist meist eine Perl-Implementierung vorhanden (/usr/ports/lang/perl5). Für Windows gibt es kostenlose Versionen von Perl unter anderem von ActiveState. Die Test-Suite findet sich in der Datei rrun.pl.

Nach Änderungen an den Sourcen können Sie mit build -z einen Rerun des Builds starten.

Inbetriebnahme von Rotor

Nun können Sie CSharp-Dateien anlegen, die sich mit dem CSharp-Compiler von Rotor kompilieren lassen. Unter Windows empfiehlt es sich, den Pfad des Compilers explizit anzugeben, damit nicht der csc von Visual Studio .NET gestartet wird:

sscli\build\v1.x86fstchk.rotor\csc /debug Test.cs

Daraus entsteht eine gültige .NET-Exe-Datei, die Sie mit .NET oder Rotor ausführen können. Die Ausführung mit Rotor stoßen Sie mit dem Clix-Tool an:

sscli\build\v1.x86fstchk.rotor\clix Test.Exe

Für das Debugging können Sie Cordbg benutzen:

sscli\build\v1.x86fstchk.rotor\sdk\bin\cordbg Test

Eine Dokumentation für Cordbg liegt vor, sie ist jedoch auch im MSDN erhältlich. Beachten Sie, dass sich die Rotor-Version von Cordbg in einigen Punkten von der .NET-Version unterscheidet.

Die Shared-Source-Lizenz

Die Sourcen stehen allerdings nicht für die unbegrenzte Nutzung zur Verfügung. In einer sogenannten Shared Source-Lizenz wird festgelegt, dass die Sourcen für die Forschung und Lehre sowie für interessierte Entwickler zur Verfügung stehen, die wissen wollen, wie die .NET-Klassen oder der C#-Compiler im Detail funktionieren. Sprich: Sie können die kompilierten Sourcen nicht als Produkt verkaufen.

In den Rotor-bezogenen Internet-Foren wurde diese Lizenz oft diskutiert. Ein umstrittener Punkt war, wie Entwickler von unabhängigen CLI-Implementierungen wie Mono oder sonstigen Produkten mit diesen Sourcen umgehen sollen. Ohne Anspruch auf Rechtssicherheit soll hier als Faustformel die Antwort eines US-Rechtsanwalts zitiert werden:

Studieren Sie die Implementierung der Sourcen und merken Sie sich die Funktionsweise. Kopieren Sie auf keinen Fall irgendwelche Codezeilen, sondern codieren Sie Ihre Implementierung aufgrund Ihrer Kenntnisse über die Funktionsweise.

Die Rotor-Entwickler selbst haben zum Thema folgendes beizutragen:

Die Autoren des CLI-Standards stellen sich eine Welt vor, in der verschiedene Implementierungen dieses Standards existieren, die alle eine bestimmte Grundmenge an Funktionalitäten, aber auch verschiedene zusätzliche Fähigkeiten wie Frameworks, Services, Utilities oder Spracheigenschaften aufweisen. Die Spezifikation ... ist sehr umfangreich; auf ihrer Basis eine Implementierung zu erstellen, kann sehr anstrengend sein. Microsoft realisierte, dass es zusätzlich zum Standard ein Beispiel geben sollte, das zeigt, wie man eine funktionierende Implementierung ... erstellt.

Damit sollte die Frage nach der Nutzung von Rotor ausreichend beantwortet sein.


Anzeigen: