DE:Linux Kernel Optimierung
From FrâG^.WIKI
| Error creating thumbnail: convert: unable to open image `/mnt/data/www/wiki.fragaholics.de/htdocs/images/7/78/Server.png': @ error/blob.c/OpenBlob/2498. convert: unable to open file `/mnt/data/www/wiki.fragaholics.de/htdocs/images/7/78/Server.png' @ error/png.c/ReadPNGImage/3072. convert: missing an image filename `/mnt/data/www/wiki.fragaholics.de/htdocs/images/thumb/7/78/Server.png/100px-Server.png' @ error/convert.c/ConvertImageCommand/2970. |
| Error creating thumbnail: convert: unable to open image `/mnt/data/www/wiki.fragaholics.de/htdocs/images/7/78/Server.png': @ error/blob.c/OpenBlob/2498. convert: unable to open file `/mnt/data/www/wiki.fragaholics.de/htdocs/images/7/78/Server.png' @ error/png.c/ReadPNGImage/3072. convert: missing an image filename `/mnt/data/www/wiki.fragaholics.de/htdocs/images/thumb/7/78/Server.png/100px-Server.png' @ error/convert.c/ConvertImageCommand/2970. |
Die Optimierung des Linux-Kernels ist sehr wichtig für eine gute Leistung des Game-Servers. Da der Kernel die verfügbare CPU-Zeit an die laufenden Prozesse vergibt, hat er direkte Auswirkungen auf die FPS des Servers. Dieses Howto ist weitgehend unabhängig der Linux-Distribution geschrieben, daher wird eine ausreichende Kenntnis der verwendeten Distribution vorausgesetzt. Unabhängig davon, ob hl1- oder hl2-basierte Spiele auf dem Server laufen sollen, wird die Verwendung des RT-Linux-Patches empfohlen, wie unten beschrieben. Außerdem wir die Verwendung eines 64-Bit-Systems empfohlen, trotz dem die Server selbst im 32-Bit-Modus laufen. Zusätzlich sollte keine SELinux-basierte Installation verwendet werden (wenn Du nicht weißt, was das ist, hast Du es wahrscheinlich nicht!).
Beachte, dass dieses HOWTO hauptsächlich auf Intel Core 2 Duo Maschinen getestet und verwendet wurde. Die generelle Technik wurde auch auf einem AMD-System getestet, aber vielleicht müssen hier einige Anpassungen vorgenommen werden. Bitte trage doch mit deinen Erfahrungen zur Verbesserung dieses HOWTOs bei!
Ein alternatives HOWTO speziell für Half-Life-1-basierte Server gibt es hier: DE:Linux_Kernel_Optimierung_HL1 Dieses HOWTO kann aber trotzdem für beide Server verwendet werden (jedoch nicht gleichzeitig!).
Vorbereitung des Systems
In Abhängigkeit von deiner Distribution wirst du ein paar Tools installieren müssen, um den Kernel zu kompilieren. Bei den meisten Distributionen sollte es reichen, die folgenden Pakete zu installieren (der Paketname kann unter Umständen ein wenig variieren):
- gcc (der Kompiler) (unter CentOS, RHEL, Fedora >11 kann ein yum groupinstall development-tools Vorteilhaft sein)
- make, gmake oder gnu-make (die GNU-Version des Tools make)
- ncurses und ncurses-dev (die ncurses Bibliothek und das development Paket) (unter CentOS, RHEL, Fedora >11 ncurses-devel)
- sudo
- chrt (Paket heißt evtl. schedutils)
- zlib1g-dev (für Debian/Ubuntu)
- patch (muss manuell installiert werden auf manchen Debian-Systemen)
- vixie-cron oder ein anderer Cron-Daemon
- rpm-build (für CentOS und Fedora)
- kernel-package (für Debian/Ubuntu)
Alle folgenden Operationen müssen unter dem Benutzername root auf dem Server ausgeführt werden. Es ist allgemein üblich, die Kernel im Verzeichnis /usr/src zu haben, also wechsele in dieses Verzeichnis:
cd /usr/src
Download des Kernel und des Patches
Suche unter http://kernel.org/pub/linux/kernel/projects/rt/ die aktuellste Version des RT patches. Downloade sie dann direkt auf den Server:
wget http://kernel.org/pub/linux/kernel/projects/rt/patch-2.6.26.8-rt16.gz
Dann downloade den zugehörigen Kernel (man benötigt die selbe Version wie im Patchnamen! Die RT Patches sind nicht immer für die aktuellste Kernel-Version verfügbar):
wget http://kernel.org/pub/linux/kernel/v2.6/linux-2.6.26.8.tar.gz
Jetzt entpacke den Kernel und wende den Patch an:
tar zxf linux-2.6.26.8.tar.gz cd linux-2.6.26.8 zcat ../patch-2.6.26.8-rt16.gz | patch -p1
Konfigurieren und erstellen des Kernels
Zuerst die aktuelle Kernel-Konfiguration kopieren (ist nicht nötig auf den meisten Systemen, schadet aber nicht):
cp -vi /boot/config-`uname -r` .config
Das Konfigurationsmenü öffnet man mit folgendem Befehl:
make menuconfig
In den meisten Fällen, abhängig von der Distribution, wird die Konfiguration des aktuellen Kernels als Grundlage verwendet. Damit der Kernel optimal für Gameserver ist, musst Du einige Änderungen vornehmen. Um durch das Menü zu navigieren, wähle mit den Pfeiltasten (hoch/runter) den Menüpunkt aus und öffne ihn mit Enter. Die Einstellungen kann mit der Leertaste geändert werden, wobei [*] angeschaltet, [M] angeschaltet als Kernelmodul und [ ] ausgeschaltet bedeutet. Um das Untermenü zu verlassen, wähle mit den Pfeiltasten (rechts/links) "Exit" aus und drücke Enter. Wenn du mehr Informationen über eine Option sehen möchtest, wähle analog zu oben "Help" aus und drücke Enter.
Dies sind die wichtigsten Einstellungen:
- General setup:
- RCU Subsystem --->:
- Ausschalten: Enable tracing for RCU (ignorieren, falls nicht vorhanden)
- RCU Subsystem --->:
- Processor type and features:
- Ausschalten: Tickless System (Dynamic Ticks)
- Einschalten: High Resolution Timer Support
- Wähle deinen Prozessor unter Processor family
- Ändere Preemtion Mode zu Complete Preemption (Real-Time)
- Einschalten: Enable priority boosting of RCU read-side critical sections (ignorieren, falls nicht vorhanden)
- Ausschalten: Enable tracing for RCU - currently stats in debugfs (ignorieren, falls nicht vorhanden)
- Einschalten: Machine Check Exception and select Intel or AMD depending on your CPU
- Ändere Timer frequency zu 1000 HZ
- Power management options
- Einschalten: Power Management support
- Ausschalten: Power Management Debug Support
- Ausschalten: Suspend to RAM and standby
- Ausschalten: Hibernation (aka 'suspend to disk')
- Einschalten: ACPI (Advanced Configuration and Power Interface) Support
- Ausschalten: CPU Frequency scaling
- Ausschalten: CPU idle PM support
- Einschalten: Power Management support
- Networking
- Networking options
- Einschalten: Packet socket: mmapped IO
- Optional ausschalten: Network packet filtering framework (Netfilter) (Achtung! dies schaltet deine Firewall aus!)
- Ausschalten: QoS and/or fair queueing (Außer du brauchst und benutzt es)
- Networking options
- Device Drivers
- Ausschalten: Watchdog Timer Support
- Einschalten: Real Time Clock
- Einschalten: PC-style 'CMOS'
- Kernel hacking
- Alles ausschalten
Schliesse nun das Hauptmenü und überprüfe, ob die Einstellungen gespeichert wurden. Um den Kernel jetzt zu kompilieren und zu installieren, führe einfach folgenden Befehl aus (hol dir einen Kaffee, das dauert jetzt ein Weilchen...):
make && make modules_install && make install
Unter CENTOS verwende folgenden Befehl (CENTOS scheint den Boot-Loader automatisch upzudaten, d.h. es muss nur der Eintrag "default" auf 0 geändert werden):
make bzImage modules modules_install install
Falls Probleme auftreten, hilft es evtl., vorher folgendes auszuführen:
make clean
Bootvorgang vorbereiten
Nach diesem Schritt musst du eventuell eine sogenannte initramdisk, kurz initrd, erstellen. Dies wird für den Bootvorgang benötigt, wenn die Kernelmodule geladen werden müssen, bevor der Festplattenzugriff für den Kernel möglich ist. Dieser Vorgang unterscheidet sich stark je nach Distribution. Unter Debian sollte es der Befehl mkinitramfs erledigen, man mkinitramfs liefert weitere Informationen. CentOS 5x, Fedora >11 und RHEL 5 nutzen mkinitrd (siehe man mkinitrd).
mkinitramfs -o /boot/initrd.img-2.6.26.8-rt16 2.6.26.8-rt16
Der letzte Schritt vor dem Neustarten ist es, den Kernel im Bootloader zu aktivieren. Wenn deine Distribution grub benutzt, was höchstwahrscheinlich der Fall ist, musst du die Datei /boot/grub/menu.lst so wie unten gezeigt verändern. Achtung: Bitte nicht Copy&Pasten, die Datei muss an deine Linux Konfiguration angepasst werden!
default 0 timeout 1 title Debian GNU/Linux, RT Kernel root (hd0,1) kernel /boot/vmlinuz-2.6.26.8-rt16 root=/dev/ram0 real_root=/dev/sda1 panic=5 initrd /boot/initrd.img-2.6.26.8-rt16 title Debian GNU/Linux, kernel 2.6.24.2 root (hd0,1) kernel /boot/vmlinuz-2.6.24.2 root=/dev/ram0 real_root=/dev/sda1 panic=5 initrd /boot/initrd.img-2.6.24.2
Anmerkungen:
- /dev/sda1 muss die Partition sein, welche das Root-Dateisystem enthält. (hd0,1) muss ebenfalls der Root-Partition entsprechen (hd0 = sda und hd0,1 = sda1 usw.). Schau dir das Bestehende einfach genau an. Kopiere einfach einen bestehenden Block, und ändere nur den Kernel und die InitRamDisk. Der Rest sollte unverändert bleiben.
- panic=5 bewirkt, dass der Kernel nach einem Kernel-Panic nach von 5 Sekunden rebootet. Das kann Zeit, Nerven und vielleicht sogar Remote-Hands sparen.
- Falls Ihr kein Rescue-System oder Webinterface zu eurem Rootserver habt, solltet Ihr die "Einmal-Boot"-Funktion einstellen. Denn dies rettet euch evtl. vor Remote-Hands-Kosten. Die "Einmal-Boot"-Funktion ändert den Boot-Kernel nur für den nächsten Boot. Falls etwas schief gehen sollte, wird beim einem erneutem Reboot der letzte Standard-Kernel gebootet. Hierzu müsst ihr eine leicht angepasste menu.lst verwenden, z.B. (Achtung, wieder kein Copy&Paste):
default saved fallback 1 timeout 1 title Debian GNU/Linux, RT Kernel root (hd0,1) kernel /boot/vmlinuz-2.6.26.8-rt16 root=/dev/ram0 real_root=/dev/sda1 panic=5 initrd /boot/initrd.img-2.6.26.8-rt16 savedefault fallback title Debian GNU/Linux, kernel 2.6.24.2 root (hd0,1) kernel /boot/vmlinuz-2.6.24.2 root=/dev/ram0 real_root=/dev/sda1 panic=5 initrd /boot/initrd.img-2.6.24.2
Zusätzlich müsst ihr noch mit folgendem Befehl den Kernel für den nächsten Boot auf den ersten setzen:
grub-set-default 0
Wenn der Kernel erfolgreich gebootet wurde, einfach default saved in default 1 ändern.
Neustart des Systems
shutdown -r now
Nach dem Neustart solltest du prüfen, ob der neue Kernel auch läuft:
uname -a
sollte etwas ähnliches wie unten ausgeben:
Linux fragaholics.de 2.6.26.8-rt13 #2 SMP PREEMPT RT Wed Jan 14 10:12:37 CET 2009 x86_64 Intel(R) Core(TM)2 Duo CPU E8200 @ 2.66GHz GenuineIntel GNU/Linux
Die Server als zeitkritische Prozesse eintragen
Ein Realtime Betriebsystem muss wissen, welche Prozesse Zeitkritisch sind und welche nicht. Standardmäßig wird jeder Prozess als nicht zeitkritisch angesehen. Der einfachste Weg ist es, einen Cronjob zu erstellen, welcher zum Beispiel alle 5 Minuten abläuft. Füge das Folgende beispielsweise in die Datei /usr/local/sbin/resched.sh ein:
#!/bin/sh PIDS=`ps ax | grep sirq-hrtimer | grep -v grep | sed -e "s/^ *//" -e "s/ .*$//"` for p in $PIDS; do chrt -f -p 99 $p done PIDS=`ps ax | grep sirq-timer | grep -v grep | sed -e "s/^ *//" -e "s/ .*$//"` for p in $PIDS; do chrt -f -p 51 $p done PIDS=`pidof srcds_linux` for p in $PIDS; do chrt -f -p 98 $p done PIDS=`pidof srcds_i686` for p in $PIDS; do chrt -f -p 98 $p done PIDS=`pidof srcds_i486` for p in $PIDS; do chrt -f -p 98 $p done PIDS=`pidof srcds_amd` for p in $PIDS; do chrt -f -p 98 $p done PIDS=`pidof hlds_i686` for p in $PIDS; do chrt -f -p 98 $p done PIDS=`pidof hlds_i486` for p in $PIDS; do chrt -f -p 98 $p done PIDS=`pidof hlds_amd` for p in $PIDS; do chrt -f -p 98 $p done
Anmerkungen: Sollte es Fehlermeldungen bei der Ausführung des cronjobs geben (evtl. per E-Mail) muss evtl. pidof durch /sbin/pidof ersetzt werden.
sirq-hrtimer scheint von der Kernelversion und der Distribution abzuhängen, ich habe auch schon softirq-hrtimer & ksoftirqd gesehen. Versuche Folgendes:
ps ax | grep hrtimer
Da sollte etwas wie [softirq-hrtimer/N] pro CPU (N ist die Nummer der CPU) auftreten. Entferne die Klammern und das /N und benutze das, was davon übrig bleibt, in diesem Beispiel also "softirq-hrtimer".
Die Datei ausführbar machen...
chmod 755 /usr/local/sbin/resched.sh
... und in /etc/crontab einfügen:
# m h dom mon dow user command [...] */5 * * * * root /usr/local/sbin/resched.sh > /dev/null 2>&1
Nun nicht vergessen, den Cron Deamon neu zu starten, unter Debian also:
/etc/init.d/cron restart
Alternativ kann man den Befehl crontab -e verwenden (als root ausführen) statt /etc/crontab zu editieren. Dann muss der Benutzer "root" in der Zeile ausgelassen werden, d.h.:
*/5 * * * * root /usr/local/sbin/resched.sh > /dev/null 2>&1
Ein Neustart des Daemons ist dann nicht notwendig.
Das war's!
Starte den Gameserver, warte 5 Minuten (oder führe /usr/local/sbin/resched.sh manuell aus) und schaue Dir die Server-FPS an, indem du den Befehl stats in der Serverkommandozeile ausführst). Es sollte stabil bei einem Wert knapp unter 1000 liegen. Für einen detaillierteren Test nutze das FPS-Meter ;-)
Kleine Schwankungen mit "idler" herausfiltern
Bei einigen Konfigurationen wird es einige Schwankungen der FPS Zahl geben. In diesem Fall kann es helfen, einen niedrig priorisierten Prozess laufen zu lassen, welcher 100% CPU Last beansprucht. Erstelle eine Datei namens idler.c mit folgendem Inhalt:
int main() {
while(1);
}
Kompiliere dieses Programm:
gcc idler.c -o idler
Um das System nicht zu verlangsamen, sollte der Prozess mit niedriger Priorität ausgeführt werden.
nice ./idler
Es sollte reichen, einen idler laufen zu lassen, selbst auf Systemem mit mehr als zwei Kernen oder Gameservern.
Die Schwankungen sollten verschwinden, solange der idler läuft. Im folgenden Bild wurde der Prozess nach ca. 10 Minuten gestartet:
Zum Ausprobieren
Es gibt einige Optionen, mit denen du weiter experimentieren kannst, um das System weiter zu verbessern. Eine kleine Liste, die keinen Anspruch auf Vollständigkeit erhebt:
- Probiere, Timer frequency auf 100 HZ zu setzen und schalte Tickless System (Dynamic Ticks) ein. Mit "high-resolution timers" hängt wie Verwaltung der Prozesse nicht mehr von der Herzzahl des Kernels ab, du kannst also den "Interrupt-Load" verringern. Dies ist allerdings noch nicht ausführlich getestet worden und kann genauso gut schlechtere Ergebnisse liefern.
- Versuche, andere sirq-Kernel-Threads als zeitkritisch anzugeben (z.B. sirq-net-tx und sirq-net-rx).
- Probiere verschiedene Timer-Quellen (fange mit hpet auf AMD- und tsc auf Intel-Prozessoren an):
cat /sys/devices/system/clocksource/clocksource0/available_clocksource
- zeigt die alle möglichen Quellen, während beispielsweise
echo hpet > /sys/devices/system/clocksource/clocksource0/current_clocksource
- die Quelle auf hpet setzt.
Man kann die clocksource auch beim Booten per grub setzen, dass sieht dann wie folgt aus (in der menu.lst):
kernel /boot/vmlinuz-2.6.26.8-rt16 root=/dev/ram0 real_root=/dev/sda1 panic=5 clocksource=hpet
- Auf manchen Systemen (insbesondern auf Quad-Cores wie der Q6600) kann ein Idler pro Core nötig sein, z.B.:
nice -n +19 taskset -c 0 screen -AmdS idler1 ./idler nice -n +19 taskset -c 1 screen -AmdS idler2 ./idler nice -n +19 taskset -c 2 screen -AmdS idler3 ./idler nice -n +19 taskset -c 3 screen -AmdS idler4 ./idler
- Ich empfehle, beide Varianten - nur ein Idler sowie ein Idler pro Core - zu testen, da es ohne Test keine Möglichkeit gibt, vorherzusagen, was besser ist!
- Ebenfalls typischerweise auf Quad-Cores kann es besser sein, jeden Server auf einem eigenen Core laufen zu lassen, z.B.:
taskset -c 0 screen -AmdS cs-server ./hlds_run put your options here taskset -c 1 screen -AmdS cs-server ./hlds_run put your options here taskset -c 2 screen -AmdS cs-server ./hlds_run put your options here
- Wiederum empfehle ich, beide Varianten (ohne und mit Festlegung auf die Cores) zu testen!
Alle diese Optionen wurden nicht sonderlich ausführlich getestet, oder haben keine messbare Verbesserung eingebracht. Bitte gib uns ein Feedback, falls du einen positiven Effekt mit dem Benutzen dieser Optionen erreichst.
Ein kleines Script um mit start/stop die Idler alle auf einmal zu starten und zu stoppen:
#!/bin/bash
#clear
#
# For every logical cpu core one idler is needed
# Do not run idlers as root user
#
# Fuer jede logische CPU wird ein Idler benoetigt
# Die Idler niemals mit dem Root User starten
#
# If you want to start the idlers on reboot do following as root
# Wenn die Idler beim automatisch beim Reboot gestartet werden sollen
# fuehre folgenes als root aus
# nano /etc/init.d/idlerstart (and paste this code)
# chmod 755 /etc/init.d/idlerstart
# update-rc.d idlerstart defaults
# User to run the idler with. Only needed for autostart
# Benutzer der den Idler ausfuehrt. Braucht man nur fuer den Autostart
USER=idleruser
#Pfad zum Idler
#Path to the idler
DIR=/path/to/idler
case "$1" in
start)
cd $DIR
if [ `id -u` = 0 ]; then
su -c "screen -d -m -S idler1 nice -n +19 ./idler" $USER
su -c "screen -d -m -S idler2 nice -n +19 ./idler" $USER
su -c "screen -d -m -S idler3 nice -n +19 ./idler" $USER
su -c "screen -d -m -S idler4 nice -n +19 ./idler" $USER
su -c "screen -d -m -S idler5 nice -n +19 ./idler" $USER
su -c "screen -d -m -S idler6 nice -n +19 ./idler" $USER
su -c "screen -d -m -S idler7 nice -n +19 ./idler" $USER
su -c "screen -d -m -S idler8 nice -n +19 ./idler" $USER
else
screen -d -m -S idler1 nice -n +19 ./idler
screen -d -m -S idler2 nice -n +19 ./idler
screen -d -m -S idler3 nice -n +19 ./idler
screen -d -m -S idler4 nice -n +19 ./idler
screen -d -m -S idler5 nice -n +19 ./idler
screen -d -m -S idler6 nice -n +19 ./idler
screen -d -m -S idler7 nice -n +19 ./idler
screen -d -m -S idler8 nice -n +19 ./idler
fi
;;
stop)
if [ `id -u` = 0 ]; then
su -c "screen -r idler1 -X quit" $USER
su -c "screen -r idler2 -X quit" $USER
su -c "screen -r idler3 -X quit" $USER
su -c "screen -r idler4 -X quit" $USER
su -c "screen -r idler5 -X quit" $USER
su -c "screen -r idler6 -X quit" $USER
su -c "screen -r idler7 -X quit" $USER
su -c "screen -r idler8 -X quit" $USER
else
screen -r idler1 -X quit
screen -r idler2 -X quit
screen -r idler3 -X quit
screen -r idler4 -X quit
screen -r idler5 -X quit
screen -r idler6 -X quit
screen -r idler7 -X quit
screen -r idler8 -X quit
fi
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
;;
esac
exit 0
- Falls du mehr als 1000 fps erreichen möchstes, probiere dies: BEpingboost.c
Aktuelle Entwicklung
- 2.6.26.8-rt16 scheint immer noch am besten zu sein, zumindest für CS:S
- 2.6.31.4-rt14 / 2.6.31.6-rt19 ist auch sehr gut, erreicht aber nicht ganz die Performance von 2.6.26.8-rt16
Trouble Shooting / FAQ
- Für srcds: prüfe, ob wirklich fps_max 0 in der Server-Konfiguration steht. Das ist absolut Pflicht.
- Für hlds: prüfe, ob wirklich sys_ticrate > 1000 (z.B. 2500) in der Server-Konfiguration steht. Außerdem prüfe, ob der Server mit -pingboost' 2 oder 3 gestartet wurde, z.B.
./hlds_run -game cstrike +map de_dust2 +sys_ticrate 1500 -pingboost 3
- Prüfe die Net-Settings auf deinem Server und Client
- Prüfe, ob deine Server und die sirq-hrtimer-Prozesse wirklich mit der richtigen Priorität laufen, in dem du chrt -p <pid> ausführst.
- Verwende niemans Bots zum Testen. Diese können ihn beiden Richtungen zu deutlich anderen Ergebnissen als menschliche Spieler führen (sie verwenden nicht die Net-Engine aber erhöhen den CPU-Load)!
- Falls nur ~950 FPS erreicht werden, ist das kein Problem. Den Unterschied von 50 Mikrosekunden (= 0.05 Millisekunden!) kann wirklich niemand feststellen.
- Alle Kernel bis 2.6.32-rc5 enthalten einen Bug, der es Nutzern erlaubt mittels einer Nullpointer Attacke root Rechte zu erlangen. Um das zu verhindern kann man das hier machen, was aber den Bug selber nicht behebt:
echo "vm.mmap_min_addr = 4096" > /etc/sysctl.d/mmap_min_addr.conf && /etc/init.d/procps restart
Links
- http://rt.wiki.kernel.org/index.php/Main_Page - (Englisch) Wiki für die RT-Kernel-Patches, mit FAQ und viel Hintergrundinformation
- http://www.howto-cs16-root.de/index.htm - Howto, das sich teilweise mit diesem überschneidet, und von dem ich auch ein paar Ideen übernommen habe.
- http://www.ulrich-block.de/?page_id=156 - Debian-Packages mit compilierten Images mit Vanilla, RT und ZEN-Patches


