OpenSSL: Implementierung innerhalb eines Client- und Server-Programms, Teil 3

Den funktionalen Anwendungstest besteht das Gespann aus WOPR-Server und -Client aus den vergangenen beiden Artikeln ohne Probleme. WOPR macht das, was erwartet wird. Wie sieht es jedoch im Dauerbetrieb und unter Last aus?

In Pocket speichern vorlesen Druckansicht
Lesezeit: 12 Min.
Von
  • Oliver Müller
Inhaltsverzeichnis

Das Gespann aus WOPR-Server und -Client aus den vergangenen beiden Artikeln funktioniert zweifellos, und zwar auch unverändert auf einer Vielzahl von Systemumgebungen. Den funktionalen Anwendungstest besteht WOPR ohne Probleme. WOPR macht das, was erwartet wird. Wie sieht es jedoch im Dauerbetrieb und unter Last aus?

In den vergangenen beiden Beiträgen wuchs ein einfacher OpenSSL-Server samt zugehörigem Client heran. Dieser Artikel betrachtet nun einige praktische Aspekte, die es im Dauerbetrieb zu berücksichtigen gilt.

Produziert sei zunächst kontinuierliche Last. Über eine einfache (Endlos-)Schleife auf der Unix-Shell lässt sich ein auf 127.0.0.1:2506 lauschender WOPR-Server automatisiert mit Terminal-Sessions unter Last setzen. Um die Last zu erhöhen, ist die Schleife in mehreren Shell-Fenstern beziehungsweise auf mehreren Konsolen zu starten.

while [ 1 ]; do
( echo help games; sleep 1
echo joshua ; sleep 1
echo list games; sleep 1
echo disconnect )|./terminal 127.0.0.1:2506 servercert.pem
done
Mehr Infos

OpenSSL – Struktur und Beispielcode

Den für die Umsetzung in der Praxis benötigten Beispielcode findet man hier (mueller_wopr-1.01.tar).

Das Resultat ist anfänglich wie erwartet: Ein Terminal etabliert eine Verbindung zum Server, setzt einige Befehle ab, trennt sich wieder vom Server und beginnt von Neuem. Die Meldungen rattern monoton über den Bildschirm. Doch plötzlich kommt alles unerwartet zum Stillstand. Der Server hält wie versteinert inne und bearbeitet keine weiteren Verbindungsversuche.

Wäre der Programmablauf inkorrekt, wäre zu erwarten, dass sich das Programm mit Fehlermeldungen oder mit einem Core-Dump verabschiedet. Da es nach anfänglich korrekter Funktion einfach stehen bleibt, liegt es nahe, dass es gegen eine systemdefinierte Begrenzung läuft. Von den Limits auf Unix (siehe ulimit -a) kommen nur die Anzahl offener Dateien und der allozierte Arbeitsspeicher in Betracht. Maximale Datei- und Coredump-Größen können das Problem nicht verursachen. Die Anzahl der (Kind-)Prozesse wäre bei fork() noch eine Überlegung wert. Da sich die diese Prozesse jedoch nach der Bearbeitung der Terminals wieder beenden, können maximal so viele Terminals zur gleichen Zeit mit dem Server verbunden sein, wie while-Schleifen in Shell-Fenstern beziehungsweise Konsolen gestartet wurden. Wäre das die Ursache des Fehlers, würden Meldungen der Art fork: resource temporarily unavailable darauf hindeuten.

Um nun die Übeltäter zu identifizieren, ist der WOPR-Server (erneut!) zu starten. Je nachdem ob das System eher UNIX System V oder BSD folgt, ermittelt danach ps -af|fgrep wopr oder ps ax|fgrep wopr die PID des Servers. Hat man diese als Argument dem folgenden Korn-Shell-Skript chkrsc als Parameter übergeben, startet darauf die Überwachung von Memory und File-Deskriptoren. Das Skript funktioniert auf den gängigen Unix-/Linux-Systemen, die eine Korn-Shell, GNU diff und lsof bereitstellen. Außen vor bleiben Systeme, die im ps keine Informationen über das verwendete RAM ausgeben oder kein lsof zur Verfügung stellen können, wie Cygwin und z/OS UNIX System Services.

#!/bin/ksh
if [ -z "$1" -o "$3" ] ; then
echo "Usage: ${0##*/} PID [ps-options]"
exit 1
fi

# script's name without path
ME=${0##*/}

# observed PID
OSVPID=$1

# options for ps
unset PSPREFIX
if [ "$2" ] ; then
PSOPT="$2"
else
case `uname` in
OS/390)
echo "OS/390 and z/OS are not supported." >&2
echo "ps which provides information about memory" >&2
echo "(main storage) usage is needed by $ME" >&2
echo "but the ps of UNIX System Services doesn't" >&2
echo "provide these information." >&2
exit 1
;;
CYGWIN*)
echo "Cygwin is not supported." >&2
echo "ps with -o flag and lsof are needed by $ME" >&2
echo "but aren't provided by Cygwin." >&2
exit 1
;;
AIX)
PSOPT="-o pid,ppid,rssize,vsz,command -p $OSVPID";;
OSF1)
PSOPT="-o pid,ppid,sz,vsz,comm,args -p $OSVPID";;
HP-UX)
PSPREFIX="UNIX95= "
PSOPT="-e -o pid,ppid,sz,vsz,comm,args -p $OSVPID";;
Linux)
PSOPT="-o pid,ppid,rss,vsz,command -p $OSVPID";;
NetBSD|OpenBSD|FreeBSD)
PSOPT="-a -o pid,ppid,rss,vsz,command -p $OSVPID";;
SunOS)
PSOPT="-o pid,ppid,rss,vsz,comm -p $OSVPID";;
*) echo "Operating system `uname` not configured yet." >&2
echo "Use command line argument ps-options, please." >&2
exit 1
;;
esac
fi

function trap_cleanup {
rm -f /tmp/$ME.?.$$.txt >/dev/null 2>&1
exit
}

function openfiles {
lsof -n -P -p $OSVPID 2>/dev/null | awk '
### EMBEDDED AWK : BEGIN ###
$1 == "COMMAND" { next }
{ for(n=4;n<NF;n++) { printf("%s ", $(n)) }
if($(NF) !~ /^\(.*\)$/) print $(NF)
else print "" }
### EMBEDDED AWK : END ###
' | sort
}

# set trap handler for clean up
# in case of SIGTERM and SIGINT
trap trap_cleanup INT TERM

x=start
openfiles > /tmp/$ME.a.$$.txt
while [ 1 ] ; do
xx=`$PSPREFIX ps $PSOPT | awk '$1 != "PID" { print $0 }'`
if [ "$x" != "$xx" ] ; then
echo $xx
x="$xx"
fi
openfiles > /tmp/$ME.b.$$.txt
y=`diff -U 0 /tmp/$ME.a.$$.txt /tmp/$ME.b.$$.txt`
if [ "$y" ] ; then
echo "$y" | awk '$1 == "+++" || $1 == "---"
|| $1 == "@@" { next } { print $0 }'
mv /tmp/$ME.b.$$.txt /tmp/$ME.a.$$.txt
fi
done

Daraufhin meldet das Skript eine Zeile der Form:

8521 8373 2332 21704 ./wopr *:2506 servercert.pem private.key

Sie zeigt von links nach rechts: PID, PPID, allozierten physischen (RSS) und virtuellen Speicher sowie die Kommandozeile des untersuchten Prozesses. Das Skript gibt bei jeder Veränderung dieser Werte jeweils eine neue solche Zeile aus. Des Weiteren listet es das Öffnen und das Schließen von Dateien.

Sobald sich ein Terminal auf den WOPR-Server verbindet, ändert sich die Anzeige:

+4u IPv4 46050 0t0 TCP 127.0.0.1:2506->127.0.0.1:50913
8521 8373 2368 21704 ./wopr *:2506 servercert.pem private.key

Es ist eine neue geöffnete Datei hinzugekommen (+4u), nämlich ein TCP-Socket. Sobald der Client terminiert, ändert sich wieder das Bild:

-4u IPv4 46050 0t0 TCP 127.0.0.1:2506->127.0.0.1:50913
+4u sock 0,6 0t0 46050 can't identify protocol

Das TCP-Socket wechselt von "verbunden mit 127.0.0.1:50913" auf "can't identify protocol". Interessanterweise bleibt es geöffnet, andernfalls hätten sich die Anzahlen von +4u und -4u aufgehoben. Das geschieht bei jeder Verbindung eines Clients. Offensichtlich bleiben hier File-Deskriptoren beziehungsweise Sockets geöffnet.