Fieberthermometer

18.02.1999
Im ersten Teil dieses Artikels haben wir die gateway-Benchmarks im Überblick vorgestellt. Diesmal zeigen wir, wie ein portabler Unix-Agent die Meßwerte der aktuellen Serverbelastung im Netz als Service zur Verfügung stellt.

Die Server-Benchmarks der gateway haben die Aufgabe, die Leistungsfähigkeit eines Servers unter einer spezifischen Last beziehungsweise einem Lastmix, zum Beispiel aus POP3, FTP oder HTTP, zu bestimmen. Hierzu stehen insgesamt drei Datenquellen zur Verfügung, die sich auch kombiniert zur Auswertung heranziehen lassen.

Zuerst dokumentieren alle in Java simulierten Serviceclients (POP3, FTP, HTTP et cetera) die Zeit, die zwischen "Auftragsvergabe" und vollständiger Erledigung durch den Server verstreicht. Wächst dieser Faktor plötzlich stark an, so ist dies ein Indiz dafür, daß der Server in die Sättigung fährt, also die maximale sinnvolle Systemlast vorliegt. Mathematisch gesehen zeigt eine große Steigung (erste Ableitung) dieses Vordringen in die Sättigung an.

Der im Testnetzwerk mitlaufende Netzwerksniffer auf Basis von Suse Linux 6.0 bietet ebenfalls Anhaltspunkte für das Zeitverhalten zwischen den Clients und dem Server. Hierzu muß man die entsprechenden Netzwerk-Dumps auswerten beziehungsweise eine aktive Probe so aktivieren, daß diese auf bestimmte Anomalien auf dem Netzwerkstrang (TCP/IP-Fehler, bestimmte ICMP-Messages, Netzwerk-Load et cetera) reagiert.

Eine dritte Quelle betrifft den Server selbst, der naturgemäß am meisten über sich selbst "wissen" sollte. Hier läuft ein eigens für den Betrieb der Benchmarks programmierter Systemagent ab, der wichtige Meßdaten bei Bedarf über eine Socket-Verbindung im gesamten Netzwerk anbietet. Optional lassen sich die Meßwerte auch auf der lokalen Festplatte des Servers ablegen und nachträglich auswerten. Für den Betrieb im Rahmen der Benchmarks sind keinerlei Sicherheitsvorkehrungen eingeplant, die den Zugriff auf die Serverdaten einschränken würden.

So zentral der externe Master-Client auch ist, der für alle simulierten Clients und den Ablauf der Benchmarks verantwortlich ist, so essentiell ist der Code, der auf dem Testserver für das Messen und die Weitergabe der Server-"Fieberkurve", also die Weitergabe des aktuellen Systemstatus, verantwortlich ist. Dieser Code muß über einen direkten Zugang zu den allerheiligsten Systemdaten verfügen. Die wichtigste Restriktion im Zusammenhang mit den Benchmarks betrifft dabei die Portabilität: Die Systemprobe sollte in möglichst vielen Systemumgebungen ablauffähig sein und möglichst wenig Aufwand beim Aufsetzen auf eine neue Umgebung verursachen.

Der Systemmonitor

Das hierzu entwickelte Serverprogramm smartprobe.c stellt solche Funktionen für diverse Unix-Derivate zur Verfügung. Eine Version für Windows NT wird folgen.

Im Zusammenhang mit den gateway-Benchmarks kommt immer wieder die portable und universelle Programmiersprache Java zum Zuge. Doch unter Java läßt sich eine solch spezifische und vor allem systemnahe Anwendung nicht für jede Plattform universell implementieren. Zwar bieten manche Betriebssysteme wie Linux oder Solaris mit dem bereits 1984 vorgeschlagenen virtuellen Dateisystem /proc eine sehr elegante und einfache Möglichkeit, an die zentralen Systemdaten per Dateizugriff heranzukommen, doch ist dieser Weg leider alles andere als portabel.

Es kommt hinzu, daß derzeit kein allgemeingültiger Standard für die Implementierung eines /proc-Dateisystems über Betriebssystemgrenzen hinweg existiert. Zudem gibt es selbst zwischen einzelnen Versionsständen des Betriebssystems Unterschiede hinsichtlich Aufbau und Optionen dieses Zugangs. So änderte Sun Microsystems beim Übergang von Solaris 2.5 auf 2.6 die flache /proc-Dateistruktur durch eine hierarchisch angeordnete Struktur.

Eine Lösung wäre, das "Java Native Interface" (JNI) einzusetzen. Dieses erlaubt die Einbindung externer Bibliotheken in eine Java-Umgebung, um beispielsweise C-Bibliotheken nutzbar zu machen. Leider verläßt man mit JNI ebenfalls unweigerlich den sicheren Boden der portablen Programmierung, so daß dieser Lösungsweg nicht in Frage kam. Möchte man jedoch für eine spezifische Plattform entwickeln, so ist JNI sicherlich nicht nur ein interessanter Lösungsweg, sondern manchmal ohne Alternative.

Top als Ausgangsbasis

Im Portfolio verblieben zwei Möglichkeiten. So war bei den Planungen lange angedacht, die Skriptsprache Perl beziehungsweise die im Überfluß zur Verfügung stehenden Perl-Bibliotheken dazu zu nutzen, die Ausgabe der von den Unix-Betriebssystemen angebotenen Systemkommandos wie ps oder uptime entsprechend universell zu filtern und die Meßdaten zur Verfügung zu stellen. Daß dieser Weg letztlich nur noch als die Alternative für ganz heikle Fälle eingestuft wurde, liegt an einem im C-Quelltext frei verfügbaren Tool, welches sich als gute Ausgangsbasis für das weitere Vorgehen entpuppt hat.

Schon seit Jahren steht "Top" (ftp://ftp.groupsys.com) für eine Reihe unterschiedlicher Unix-Derivate im C-Quelltext zur Verfügung. Bei Systemverwaltern hat sich Top längst einen Namen gemacht und verrichtet auf vielen Systemen nützliche Informationsdienste. So gibt das Programm über die aktivsten Prozesse genauso Auskunft wie über die Prozessorauslastung oder die aktuellen Speicher- und Swap-Parameter. Weil die Quellen sehr portabel und modular gestaltet sind - die maschinenabhängigen Codeteile sind in eigenen Dateien untergebracht - ist es nicht sehr schwer, eine für den Einsatz im Dienste der Benchmarks modifizierte Variante zu erstellen, die alle relevanten Parameter bei Bedarf über einen Socket-Port zur Verfügung stellt.

Das Paket ist ein gutes Beispiel dafür, wie man portable Programme erstellen kann. Durch den modularen Aufbau sind die systemabhängigen Unterprogramme in einem eigenen Unterverzeichnis (/machine) in der Datei mit dem Namen m_systemname.c untergebracht. Während der Konfiguration als Vorarbeit zur Kompilierung und Installation von Top wählt man ein Zielsystem aus. Danach kopiert das Konfigurations-Script die entsprechende machine-Datei in die universelle Datei machine.c, die dann durch einen make-Lauf übersetzt und in die restlichen Codeteile eingebunden wird.

Tatsächlich nutzen wir aus dem so erzeugten machine.c hauptsächlich die Routine get_system_info(info), die bis auf die Daten zu den laufenden Prozessen alle weiteren Grunddaten in einer standardisierten Form zur Verfügung stellt. Darüber hinaus benötigen wir noch die generische Programmbibliothek utils.c, in der einige Top-Standardroutinen zusammengefaßt sind. Die restlichen Codefragmente bleiben bis auf den Initialisierungscode unberührt.

Je nach Betriebssystem muß eine Auswerteroutine die Systemdaten aufbereiten. Da Top vollständig parametrisiert arbeitet, ist dort eine genau festgelegte Reihenfolge der Parameter nicht zwingend notwendig.

Systemmanagement-Agent

Nach der "richtigen" Anordnung in einer eigenen C-Struktur stehen die Meßdaten entsprechend aufbereitet als ASCII-Werte zur Verfügung. Im Prinzip ist damit bereits die Grundlage für ein ausgewachsenes Netz- beziehungsweise Systemmanagement-System gelegt, da man die Daten der auf den einzelnen Systemen ablaufenden Top-Agents nur mehr erfassen und auswerten muß. (Wir kommen darauf in einem eigenen Artikel durch eine entsprechende fertig ausprogrammierte Lösung noch zurück).

Das modifizierte Top läuft als endloser "Dämon"-Prozeß mit Namen "Smartprobe". Externe Prozesse können die Meßdaten über einen voreingestellten Port abfragen und auswerten. Zudem lassen sich die Daten auf der lokalen Festplatte abspeichern. Hierzu verfügt der Client derzeit über folgende Kommandos:

- GET_ALL: Alle Meßdaten,

- GET_MEM: Speicherparameter,

- GET_SWAP: Swap-Parameter,

- GET_CPU: Prozessorparameter,

- GET_LOAD: Systemauslastung,

- GET_PROC: Prozeßparameter,

- PUSH_CLK: automatischer Push-Zyklus,

- SET_LOAD: Serverbasislast einstellen,

- DISCONNECT: Thread beenden,

- QUIT: Dämon beenden.

Damit ist das Grundgerüst für die Weitergabe bestimmter Systemstati gelegt. Folgende Meßdaten werden in der derzeit entwickelten ersten Version von Smartprobe zur Verfügung gestellt:

- Systemauslastung (aktuell, 5 Minuten, 15 Minuten),

- CPU-States (USER, SYSTEM, NICE, IDLE),

- Speicherstatus (verfügbar, derzeit genutzt, derzeit verfügbar, shared, Puffer),

- Auslagerungsdateien (verfügbar, derzeit genutzt, derzeit verfügbar).

Damit steht eine Reihe sehr interessanter Daten zur Verfügung, die einen guten Überblick über den Systemstatus vermitteln. Mit dem Kommando PUSH_CLK kann man angeben, ob der Dämon von sich aus Daten in regelmäßigen Abständen senden (hierbei gilt das letzte GET-Kommando) und in welchen Abständen (in Millisekunden) dies erfolgen soll.

DISCONNECT dient dazu, die aktuelle Serververbindung wieder zu beenden und den entsprechenden Thread freizugeben. Mit QUIT schließlich beendet man den kompletten Dämon-Prozeß.

Eine Sonderstellung nimmt das Kommando SET_LOAD ein. Dieses dient dazu, eine spezifische dynamische Serverlast zu erzeugen, um auf dem Server eine Grundlast aufzubauen, die beispielsweise die vom Benchmark nicht erfaßte Serveraktivitäten simulieren soll. Diese Grundlast wird in Prozent angegeben und führt dementsprechend dazu, daß für jeden Server andere Arbeitsparameter gelten, um diese Last zu erzeugen. Der Agent stellt nun in einer Regelschleife die gewünschte Last ein und meldet den Vollzug an den Client zurück. Danach können die Testläufe beginnen.

Weil ein sehr leistungsfähiges System im Vergleich zu einem Einsteigersystem bei der gleichen Grundlast bereits eine Menge Arbeiten so nebenbei erfüllen kann, geht die insgesamt während der Testläufe absolut erzeugte "Arbeit" auf dem Server als zusätzlicher multiplikativer Faktor in das Endergebnis ein.

Das Programm Smartprobe wird im Zuge der durchgeführten Server-Benchmarks an die zur Verfügung stehenden Systeme angepaßt. Je nach System gilt es, entweder auf die bereits zur Verfügung stehenden Module des Pakets Top zurückgreifen, diese zu verändern oder gänzlich neue Wege einzuschlagen. Dies dürfte vor allem für Multiprozessor-Maschinen gelten. Auch der Support der Benchmarks unter Windows NT macht eine Neuprogrammierung dieses Codeteiles für diese Plattform notwendig.

Wie bereits angedeutet, nutzt der Systemagent Smartprobe bereits bestehende Codefragmente des frei verfügbaren Admintools Top. Dieses Tool ist so aufgebaut, daß das Grundgerüst bestehend aus den zentralen und für alle Plattformen gleichen C-Quelltextdateien top.c, display.c, utils.c mit einer maschinenabhängigen Version der Datei machine.c harmoniert. In machine.c findet sich also der Code, der aus den unterstützten Systemen die Systemparameter entnimmt und diese in einer weitestgehend standardisierten Form übergibt. Top - und damit auch die Grundversion von Smartprobe - unterstützen eine große Auswahl von Betriebssystemvarianten (Kasten "Vielfalt für Top"):

Smartprobe-Server

Die Bereitstellung der Meßdaten erfolgt über einen multithreaded Server, der auf einem definierten Socket auf Kommunikation wartet. Die zur Verfügung stehenden Kommandos haben wir ja weiter oben kurz angesprochen. Im Prinzip wartet der Server darauf, daß ein Client entsprechende Kommandos sendet und gegebenenfalls gleich die passenden Parameter mitliefert. Das Kommando GET_ALL führt beispielsweise dazu, alle vorgesehenen Meßwerte festzustellen und diese als Ascii-Pakete zum Client zu schicken.

Wie kommt der maschinenabhängige Teil zu den aktuellen Systemwerten? Leider existiert kein Standardverfahren, wie man an zentrale Systemdaten wie die Systemauslastung herankommen kann. Zwar existieren unter Unix in der Regel typische Systemkommandos wie ps oder uptime, doch geben diese nicht über alle Werte Auskunft. Deshalb wurde auf deren Gebrauch verzichtet. Ein Tool wie Top paßte demgegenüber bestens zur gestellten Aufgabe.

Der Linux-spezifische Code zeigt sehr deutlich, wie leicht das /proc-Dateisystem zu nutzen ist. Hier ein Auszug zur Verdeutlichung:

/* get load averages */

{

fd = open("loadavg", O_RDONLY);

len = read(fd, buffer, sizeof(buffer)-1);

close(fd);

buffer[len] = ‘\0’;

info->load_avg[0] = strtod(buffer, &p);

info->load_avg[1] = strtod(p, &p);

info->load_avg[2] = strtod(p, &p);

p = skip_token(p); /* skip running/tasks */

p = skip_ws(p);

if (*p)

info->last_pid = atoi(p);

else

info->last_pid = -1;

}

Man greift in diesem Fall also auf die derzeitigen Systemload-Daten zu, indem man den Inhalt der virtuellen Datei loadavg einliest und auswertet. Die nachfolgenden Zugriffe aus stat und meminfo (Listing /proc liefert Systemdaten) offenbaren, wie man an weitere Daten herankommt. Die Solaris-Variante ist etwas komplexer, unterstützt aber Multiprozessorumgebungen direkt.

Multithreaded-Server

Zum Schluß stellen wir noch kurz das Programmgerüst des Servercodes von Smartprobe vor (Listing "Das Gerüst von Smartprobe"). Die ersten Zeilen haben die Aufgabe, die Socket-Kommunikation vorzubereiten. Hierzu muß man eine C-Struktur entsprechend initialisieren und danach per bind den Socket zuerst für sich reservieren. Mit listen meldet man an, daß man beabsichtigt, Kommunikationsanfragen auf dem Port in Zukunft zu bedienen.

Richtig "los" geht es allerdings erst mit dem C-Systemcall accept. Dieser sorgt als sogenannter "blocking"-Call dafür, daß das Programm an dieser Stelle untätig verharrt, bis ein externer Client eine passende Socket-Verbindung initiiert. Tritt dieser Vorgang ein, so kopiert accept die properties des zuvor per socket initialisierten logischen Sockets, übergibt diese an eine Variable zum weiteren Gebrauch und gibt die Programmkontrolle wieder zurück. Jetzt kann es also im Code weitergehen.

In unserem Code folgt jetzt sofort das Erzeugen eines eigenen Threads für die entsprechende Verbindung. Dieser Thread übernimmt die Kommunikation auf dem Port als eigenständiger Tochter-prozeß. Der Va- terprozeß kann dann sofort wieder mit accept auf weitere Socket-Verbindungen (auf dem gleichen Port) warten.

Somit ist es möglich, mehrere Clients gleichzeitig zu bedienen. Die per Kommentar etwas hervorgehobene do-while-Schleife ist für die Abarbeitung der Kommunikation verantwortlich. In unserem Fall sorgt ein einfacher Kommando-Dispatcher per switch für die entsprechende Verteilung der Aufgaben.

Aus Platzgründen können wir an dieser Stelle nicht näher auf den Code von Smartprobe eingehen. Sie können sich den ablauffähigen Agent jedoch von der gateway-FTP-Site laden und in Ruhe durchstreifen. Schrittweise kommen jetzt die Java-Clients hinzu, die die einzelnen Protokolle simulieren. In der nächsten Ausgabe stellen wir noch den dynamischen Lastgenerator sowie die Java-Clients vor. (ch)