Lade und zeichne

Linux ist auf dem Desktop, was die Treiberunterstützung der Hersteller angeht, bei Grafikhardware bei weitem nicht so gut ausgestattet wie Windows. Die neuste Version der Open-Source-Implementierung des X Window System für Linux, XFree86 4, kann da Abhilfe schaffen.

In Pocket speichern vorlesen Druckansicht 5 Kommentare lesen
Lesezeit: 16 Min.
Von
  • Dirk Hohndel
Inhaltsverzeichnis

XFree86 4 wurde seit Jahren immer wieder angekündigt und zugleich der Release-Zeitpunkt immer wieder verschoben. Viele Gründe spielten dabei eine Rolle, zum Beispiel die zeitliche Belastung der Akteure (wie so oft bei Open Source Software) oder der Versuch, gleichzeitig an Version 3 weiterzuarbeiten, denn im schnelllebigen Grafik-Hardware-Geschäft stünden Linux-Benutzer sonst schnell mit nicht unterstützter Hardware im Regen. Hauptsächlich aber war die Komplexität der geplanten Änderungen und die eher fundamentale Natur des Redesigns Ursache der Verzögerung.

Mehr Infos

Auch nach Freigabe von XFree86 Version 4.0 Anfang März 2000 empfiehlt das XFree86-Core-Team noch immer, die neue Release im produktiven Umfeld nur nach sorgfältiger Prüfung einzusetzen, ansonsten lieber auf die Version 3 zu vertrauen. Die Entwickler haben große Teile des XFree86-spezifischen Codes gemäß des neuen Designs neu implementiert, sodass trotz aller Beta-Tests hier noch unentdeckte Fehler lauern könnten. Angesichts dieser Empfehlung liefern alle Linux-Distributoren im Moment standardmäßig XFree86 Version 3 (zumeist 3.3.6) an ihre Kunden aus. SuSE hat in SuSE Linux 6.4 zusätzlich XFree86 4.0 beigepackt und erlaubt es, dieses anstelle von XFree86 3.3.6 zu installieren. Da aber auch das Dateiformat der Konfigurationsdatei erweitert wurde und damit die gewohnten Tools zur Konfiguration nicht mehr funktionieren, ist das derzeit noch eher etwas für den erfahrenen Anwender. Einen kleinen Überblick über einige der wichtigen Neuerungen gibt der Kasten ‘XFree86 4 - Neue Konfiguration’. Es existieren zwar bereits erste Versionen neuer Tools, wie SaX2 [2], diese erfordern noch einiges an Arbeit, bis die Einrichtung so einfach vonstatten geht, wie sich die Linux-Verfechter das wünschen.

Mehr Infos

XFree86-4.0-Highlights

X11R6.4: die aktuelle Version des X Window System.

Multi-Head-Support: mit dieser ‘XINERAMA’ benannten Technik lassen sich mehrere Monitore unter X als ein Screen ansprechen, das heißt, dass sich beispielsweise Fenster über Monitorgrenzen hinweg verschieben lassen.

DGA2: Hardwarebeschleunigung auch für Direct Graphics Access. Dies erlaubt Programmen wie VMware, die ‘am X-Server vorbei’ Grafik darstellen, von der Geschwindigkeit der Grafikkarte zu profitieren.

XAA2: schnellere Treiber dank neuer Version der XFree86 Acceleration Architecture. Diese dient dazu, alle Operationen des X-Servers, die sich auf beschleunigte Grafikprimitive einer Grafikkarte zurückführen lassen, auch optimal auszuführen.

DRI: Direct Rendering Infrastructure und Mesa für OpenGL-kompatible 3D-Unterstützung in Hard- und Software.

Silken Mouse und verbesserter Scheduler: Dank Mausbewegung zur Interrupt-Zeit und adaptivem Scheduler reagiert der Server auch unter Last wesentlich flüssiger.

TrueType-Fonts: die FreeType-Bibliothek ist nun direkt aus dem Server heraus nutzbar.

ISO 10646-1/UTF-8: Support sowohl in den mitgelieferten Fonts als auch (als Option) in xterm.

Warum also so viel Aufwand, so viel Komplikation für den Benutzer? Das fundamentale Redesign in XFree86 4 war dringend erforderlich, da das kaum dokumentierte Design in den XFree86-Versionen 1 bis 3 extrem SVGA-zentrisch ist und viele versteckte Abhängigkeiten und Annahmen in sich birgt, die die Entwicklung neuer Treiber erschweren. Dies zwang insbesondere die Entwickler für die Einbindung neuer und innovativer Grafikchipsätze zu immer komplizierteren Klimmzügen bei der Programmierung; aktuelle Features wie hardwarebeschleunigte 3D-Grafik oder Multi-Monitor-Betrieb ließen sich, basierend auf dem alten Design, kaum realisieren.

Ein weiteres fundamentales Problem alter XFree86-Versionen stellte das monolithische Design dar, was zu ausgesprochen großen und unhandlichen Server-Binaries führte. Beispielsweise ist der Framebuffer-Code für alle Farbtiefen ebenso stets im Server enthalten wie alle unterstützten Font-Renderer. Ein Nebeneffekt dieser Situation war auch das logistische Problem der Bereitstellung aktueller Treiber für neue Grafikhardware. Denn um einen neuer Treiber auszuliefern, mussten die jeweiligen Entwickler einen vollständigen neuen Server bereitstellen, obwohl oft nur einige wenige Routinen hinzugefügt wurden und sich weit mehr als 90 Prozent des Codes nicht geändert hatten. Hier war dringend Abhilfe erforderlich, um die gezielte Bereitstellung einzelner Treiber zu erlauben. Daher gingen die Entwickler Anfang 1997 als ersten Schritt genau die Beseitigung dieser monolithischen Struktur an.

Nach dem Abarbeiten von Kommandozeilenparametern und Konfigurationsdatei lädt der X-Server benötigte Programmteile zur Laufzeit nach (Abb. 1).

XFree86 4 verfügt über eine modulare Architektur, die es erlaubt, zur Laufzeit des X-Servers Module nachzuladen. Wie Abbildung 1 zeigt, benutzt der Server dieses Feature derzeit fast ausschließlich zur Startzeit, es ist aber durchaus denkbar, später zur Laufzeit des Servers, ausgelöst durch Benutzer-Requests Module, zu laden oder wieder zu entfernen. Das eigentliche Server-Binary (mit dem einfallsreichen Namen XFree86) ist nach Auslagerung vieler Bereiche in eigene Module trotz vieler neuer Features nur noch gut halb so groß wie bei XFree86 Version 3.

Metro Link erwies sich in diesem Zusammenhang als eine große Stütze der Open-Source-Entwickler und stellte eine Implementierung einer modularen Architektur samt plattformunabhängigem Loaders zur Verfügung. Das XFree86-Team nahm diese als Basis für die jetzt vorgestellte Version.

Technisch interessant an dieser Architektur ist, dass sie unabhängig vom Betriebssystem arbeitet. Der Server lädt also nicht über die Funktion dlopen() Shared Libraries nach, sondern benutzt mit Hilfe XFree86-eigener Routinen beliebige Object-Dateien (oder sogar ar-Archive). Dies führt zu der angenehmen Situation, dass alle iA32-Plattformen unter XFree86 identische Module benutzen können, also XFree86 unter Linux, Solaris, FreeBSD oder OS/2 dieselben Binary-Module verwendet. Ein Punkt, der den logistischen Aufwand für die Bereitstellung von Hardwaretreibern ganz erheblich vereinfacht.

Wie funktioniert dies in der Praxis? Der Server öffnet die Object-Datei und wertet die verschiedenen Sektionen gemäß des erkannten Dateiformats aus. Meist wird dies ELF-Format sein, a.out und COFF werden ebenfalls unterstützt. Der X-Server mappt den ausführbaren Code dieser Datei in seinen Speicherbereich und macht ihn sich damit als Erweiterung des Executables verfügbar. Dazu ist es erforderlich, die innerhalb des Moduls verwendeten symbolischen Adressen entsprechend aufzulösen (zu relozieren). Alle vom Modul exportierten globalen Symbole werden innerhalb des Loaders aufgelöst und in Symboltabellen eingetragen. Im Modul referenzierte Symbole werden entsprechend der Symboltabellen im Loader reloziert.

Für echte Portabilität der Module dürfen diese nur Symbole in anderen Modulen referenzieren oder Symbole, die der Server explizit exportiert. Direkte Referenzen auf die C-Bibliothek sind untersagt. Stattdessen bietet XFree86 eine eigene Sammlung von ANSI-C-Wrappern, die die wichtigsten Bibliotheksfunktionen bereitstellen, zum Beispiel xf86open() anstelle von open(). Zur Erleichterung für den Entwickler stellt XFree86 eine Header-Datei mit Makros zur Umsetzung bestehender Sourcen bereit. xf86_libc.h enthält also hierfür die Zeile #define open xf86open.

Damit ist nur noch das eigentliche Server-Binary selbst betriebssystemabhängig. Die Module an sich lassen sich mit jedem auf der gleichen Plattform unterstützten Betriebssystem verwenden, da die Wrapperfunktionen innerhalb des (betriebssystemabhängigen) Server-Binaries die Umsetzung auf die Gegebenheiten, Datentypen und Calling-Conventions des jeweiligen Betriebssystems transparent für die Module übernehmen. Sowohl aus dem eigentlichen Server-Binary als auch aus Modulen heraus lassen sich explizit weitere Module mittels des Aufrufs LoadModule(ModuleName, Path, SubDirs, Pattern, OptionList, ModReq, &errmaj, &errmin) nachladen. Dabei wird das Modul mit einem Namen gemäß der Regeln in Pattern in den in SubDirs angegebenen Unterverzeichnissen der in Path aufgeführten Verzeichnisse gesucht. OptionList erlaubt es, dem Modul direkt beim Aufruf Optionen mitzugeben, ModReq enthält Informationen über die ABI-Version (Application Binary Interface), die das Modul mindestens erfüllen muss.

Welche Module der Server lädt, bestimmen im Server-Binary fest eingestellte Defaults (zum Beispiel den Font-Renderer für Bitmap-Fonts, der immer gebraucht wird), Angaben in der XF86Config-Datei oder Abhängigkeiten unter den verschiedenen Modulen. Wird ein Modul geladen, sucht der Loader als allererstes - noch vor der Auflösung der symbolischen Referenzen innerhalb des Moduls - ein Datenelement <ModuleName>ModuleData, das neben Versionsinformationen die Referenz auf Initialisierungs- und Desinitialisierungsfunktionen für dieses Modul enthält. Die hier genannte setup-Funktion führt der Loader dann unmittelbar aus.

Die abschließende Relozierung aller Module findet erst nach ihrem Laden statt. Die Loader-Routinen lösen Referenzen innerhalb eines Moduls und Referenzen auf exportierte Funktionen des Server-Binaries schon zum Zeitpunkt des erstmaligen Ladens auf. Im letzten Relozierungsdurchgang werden nach dem Laden aller Module die Referenzen zwischen den einzelnen Modulen eingefügt. Dies ermöglicht auch gegenseitige Abhängigkeiten unter den einzelnen Modulen (circular references). Aus dem eigentlichen Server-Binary heraus lassen sich Symbole über die Funktion FunctionPtr = LoaderSymbol(FunctionName) auflösen.

Daraus ist schon ersichtlich, dass der Server wissen muss, welche Interfaces ein Modul anbietet, bevor er diese nutzen kann. Wie genau läuft also die Einbindung eines Moduls in den laufenden Prozess, und welche Funktionen lassen sich gegenwärtig in Modulen realisieren? Prinzipiell angedacht sind Module für Treiber der Ein- oder Ausgabegeräte, Extensions (also standardisierte Erweiterungen eines X-Servers, wie GLX oder die Shape-Extension) und Renderer (sowohl für Framebuffer als auch für Fonts). Das Design ist jedoch flexibel genug, um auch andere Dinge in Modulen realisieren zu können, zum Beispiel Herstellerdaten für die PCI-Routinen des Servers (für die namentliche Erkennung von PCI-Devices). Als Beispiel will ich kurz die Setup-Routinen verschiedener Modul-Typen erläutern.

Listing 1 enthält Ausschnitte des Matrox-Treibermoduls. Dies stellt zunächst die Datenstruktur mgaModuleData bereit, die ihrerseits die Setup-Funktion referenziert. Letztere fügt den Treiber mit einer aus dem Server-Binary exportierten Funktion xf86AddDriver() in die Liste der bekannten Treiber für Ausgabegeräte ein und stellt sie so dem Server zur Verfügung. Neue Treiber können also einen beliebigen Namensraum für eigene Funktionen benutzen und diese dem Server über xf86AddDriver() bekannt machen.

Mehr Infos

Listing 1: Ausschnitt aus ... mga/mga_driver.c

/* All drivers should typically include these */
#include "xf86.h"
#include "xf86_OSproc.h"
#include "xf86Resources.h"

/* All drivers need this */
#include "xf86_ansic.h"

[...]

static MODULESETUPPROTO(mgaSetup);
static XF86ModuleVersionInfo mgaVersRec =
{
"mga",
MODULEVENDORSTRING,
MODINFOSTRING1,
MODINFOSTRING2,
XF86_VERSION_CURRENT,
MGA_MAJOR_VERSION, MGA_MINOR_VERSION, MGA_PATCHLEVEL,
ABI_CLASS_VIDEODRV, /* This is a video driver */
ABI_VIDEODRV_VERSION,
MOD_CLASS_VIDEODRV,
{0,0,0,0}
};

XF86ModuleData mgaModuleData = { &mgaVersRec, mgaSetup, NULL };

static pointer
mgaSetup(pointer module, pointer opts, int *errmaj, int *errmin)
{
static Bool setupDone = FALSE;
/* This module should be loaded only once, but check to be sure. */
if (!setupDone) {
setupDone = TRUE;
xf86AddDriver(&MGA, module, 0);

/*
* Modules that this driver always requires may be loaded here
* by calling LoadSubModule().
*/

/*
* Tell the loader about symbols from other modules that this module
* might refer to.
*/
LoaderRefSymLists(vgahwSymbols, cfbSymbols, xaaSymbols,
xf8_32bppSymbols, ramdacSymbols,
ddcSymbols, i2cSymbols, shadowSymbols,
fbdevHWSymbols, vbeSymbols,
#ifdef XF86DRI
drmSymbols, driSymbols,
#endif

NULL);
/*
* The return value must be non-NULL on success even though there
* is no TearDownProc.
*/
return (pointer)1;
} else {
if (errmaj) *errmaj = LDR_ONCEONLY;
return NULL;
}
}

Andere Module werden direkt geladen und von den aufrufenden Routinen explizit eingebunden. Ein Beispiel kann das Framebuffer-Modul sein, das ein Treibermodul explizit per xf86LoadSubModule() angefordert. Das Treibermodul erklärt in seiner Setup-Funktion mittels LoaderRefSymLists, dass es das Symbol fbScreenInit referenziert und dass der Loader dies nach Laden der entsprechenden Module auflösen soll. Hier sind also explizit Abhängigkeiten und Funktionsnamen zwischen verschiedenen Modulen hart kodiert.

Auch ein Entfernen bereits geladener Module ist natürlich möglich, allerdings muss dabei der Aufrufer selber sicherstellen, dass kein Teil des Servers noch Referenzen auf Daten oder Funktionen aus diesem Modul benutzt. Dies kann zum Beispiel sinnvoll sein, wenn der X-Server ein Treibermodul geladen hat, dies dann aber keine von ihm unterstützte Hardware finden konnte und daher nicht mehr gebraucht wird.

Neben all den technischen Vorteilen stellt sich natürlich auch die Frage nach Nachteilen der neuen Architektur. Ein Problem ist sicherlich die Fehlersuche. gdb und andere Debugger können normalerweise Aufrufe in Module nicht nachvollziehen und geraten auch beim Erzeugen von Stack-Trace-Informationen durcheinander. Es existiert eine angepasste Version von gdb, die sich in die Lade-Routinen einklinkt und so auch Symbole und Adressen in Modulen richtig verwalten kann, trotzdem ist das Fehlen anderer Debugger ein recht deutlicher Nachteil.

Weitere potenzielle Probleme ergeben sich aus Security-Überlegungen. Der X-Server läuft unter Linux mit Super-User-Privilegien und dies gilt auch für Code aus Modulen, die er nachlädt. Daher sollte man bei der Installation von Modulen die übliche Vorsicht gegenüber Binary-Only-Code unbekannter Quelle walten lassen. Das XFree86-Team arbeitet an einer Methode zur Authentifizierung von Modulen, die aber erst in einer späteren Version von XFree86 zur Verfügung stehen wird.

Ein eher ideologisches Problem ist natürlich die Frage der Nachteile für die Open-Source-Bewegung. Die Möglichkeit für Hersteller von Grafikkarten, Treiber-Binaries zu erstellen und diese mit ihren Karten zu vertreiben, ist auf der einen Seite ein großer Schritt in Richtung bessere Hardwareunterstützung in XFree86. Auf der anderen Seite könnte dies aber den Anreiz für Hersteller senken, Dokumentationen zu ihren Chipsets und Sourcen zu den Treibern zu veröffentlichen.

Unter dem Strich überwiegen aber ganz klar die Vorteile. Es wird einfacher, neue Versionen zu vertreiben und möglich, bestehende Fehler in vielen Bereichen des X-Servers durch Austauschen eines Moduls sehr einfach zu beheben. Und die Flexibilität für Erweiterungen von XFree86 nimmt ganz wesentlich zu.

Bleibt zu hoffen, dass viele Hersteller ihre ersten Ankündigungen wahr machen, und Treiber für ihre Karten unter XFree86 4.0 entwickeln. NVIDIA zum Beispiel hat eine erste Version einer solchen Software freigegeben. Vielleicht werden sich schon im diesjährigen Weihnachtsgeschäft Linux/XFree86-Treiber auf viele Treiber-CDs verirrt haben, die Grafikkarten beiliegen. Dann hätte Linux eine weitere Hürde zur breiten Akzeptanz überwunden.

Dirk Hohndel
ist Vorstand und Chief Technology Officer der SuSE Linux AG. Er arbeitet seit Jahren aktiv an der Linux-und XFree86-Entwicklung mit und ist Vice President von The XFree86 Project, Inc. sowie Mitglied des XFree86 Core Teams.

Mehr Infos

iX-TRACT

  • Ein modulares Design von XFree86 4.0 erleichtert die Distribution und Integration von Treibern für neue Grafikhardware.
  • Die Möglichkeit, Binärmodule einzubinden, könnte dafür sorgen, dass Hardwarehersteller die unter Linux zur adäquaten Ansteuerung benötigten Treiber bereits mit ihren Produkten ausliefern.
  • Dank geringerer SVGA-Orientierung ergibt die neue Architektur schnellere und leistungsfähigere Treiber; auch die Integration beschleunigter 3D-Grafik ist vorgesehen.