vom auffaelligen Prozess bis zur konkreten Ursache
Ein einzelner Prozess frisst dauerhaft 100 Prozent CPU, aber niemand weiss warum, es gab keinen Deploy und keine Konfigurationsaenderung. Dieser Artikel zeigt den vollstaendigen Diagnoseweg fuer mysterioesen CPU-Verbrauch: vom ersten Verdacht in top ueber Thread-Analyse und Systemaufrufe mit strace bis zum konkreten Code-Pfad mit perf.
Inhaltsverzeichnis
- 1. Der erste Verdacht: top und die richtigen Fragen
- 2. Prozess oder einzelner Thread? -H in top nutzen
- 3. Wann begann es? Startzeit und Prozessbaum pruefen
- 4. strace: Systemaufrufe live mitschneiden
- 5. perf top: die heissesten Funktionen live sehen
- 6. PHP-FPM-Sonderfall: Xdebug, Endlosschleifen, Regex
- 7. Phantom-Prozesse durch Cron und Zombie-Kinder
- 8. Sofortmassnahmen: cgroups, nice, cpulimit
- 9. Werkzeuge im Vergleich: Wann was einsetzen
- 10. Zusammenfassung
- 11. FAQ
1. Der erste Verdacht: top und die richtigen Fragen
Der Einstieg in jede Diagnose von mysterioesem CPU-Verbrauch ist top oder htop, sortiert nach %CPU mit der Taste P. Doch die reine Beobachtung, dass ein Prozess viel CPU verbraucht, ist noch keine Diagnose, sondern der Ausgangspunkt fuer eine Reihe gezielter Fragen: Ist es immer derselbe Prozess oder wechselt die PID staendig? Laeuft die Last dauerhaft oder in Wellen? Begann sie nach einem konkreten Ereignis wie einem Deploy, einem Cron-Job oder einem eingehenden Request?
Diese Vorfragen entscheiden ueber die weitere Richtung. Ein dauerhaft bei einer festen PID haengender CPU-Verbrauch deutet auf eine Endlosschleife oder einen haengenden Systemaufruf hin. Wechselnde PIDs bei gleichbleibendem Prozessnamen, etwa immer wieder neue php-fpm-Worker, deuten eher auf ein wiederkehrendes Muster in eingehenden Requests hin, das jeden Worker gleichermassen belastet. Ohne diese Einordnung fuehrt die weitere Suche oft ins Leere.
Ebenfalls hilfreich ist ein kurzer Blick auf uptime, um zu pruefen, ob die allgemeine Load Average des Systems ebenfalls angestiegen ist oder ob sich der CPU-Verbrauch ausschliesslich auf einen einzelnen Prozess konzentriert, waehrend der Rest des Systems unbeeintraechtigt bleibt.
Ein weiterer nuetzlicher Fruehindikator ist die absolute Anzahl betroffener Prozesse. Betrifft der CPU-Verbrauch nur einen einzelnen Worker unter zwanzig, deutet das auf einen spezifischen, reproduzierbaren Ausloeser hin, etwa eine konkrete Anfrage. Sind hingegen alle Worker gleichzeitig betroffen, liegt die Ursache eher in einer globalen Konfigurationsaenderung oder einem systemweiten Ereignis wie einem fehlgeschlagenen Deploy.
2. Prozess oder einzelner Thread? -H in top nutzen
Viele moderne Anwendungen, darunter Java-Prozesse, Node.js mit Worker-Threads und manche PHP-Erweiterungen, sind Multi-Thread-faehig. Ein Prozess, der insgesamt 100 Prozent CPU zeigt, kann diese Last auf zehn Threads mit je zehn Prozent verteilen oder auf einen einzigen Thread konzentrieren, der voll ausgelastet ist. Diese Unterscheidung ist fuer die weitere Diagnose von mysterioesem CPU-Verbrauch entscheidend, weil sich Werkzeuge wie strace und perf auf einzelne Thread-IDs richten lassen.
top -H -p <PID> schaltet die Thread-Ansicht ein und zeigt jeden Thread des Zielprozesses mit eigener Thread-ID (TID) und individueller CPU-Last. Ist genau ein Thread fuer den Grossteil der Last verantwortlich, laesst sich die weitere Analyse gezielt auf diese eine TID fokussieren, statt den gesamten Prozess mit all seinen Threads zu betrachten.
Eine Aequivalenz-Regel hilft bei der Einordnung: Die Summe der einzelnen Thread-Prozentwerte in der TID-Ansicht sollte grob dem Gesamtwert des Prozesses in der normalen Prozessansicht entsprechen. Weicht diese Summe deutlich ab, deutet das auf kurzlebige Threads hin, die zwischen zwei Messungen entstanden und wieder beendet wurden, was fuer die Diagnose von mysterioesem CPU-Verbrauch ein zusaetzlicher Hinweis auf ein Thread-Pool-Muster ist.
# Show per-thread CPU usage for a specific process
top -H -p 28841
# PID TID USER %CPU COMMAND
# 28841 28855 www-data 98.7 php-fpm
# 28841 28856 www-data 0.3 php-fpm
# 28841 28857 www-data 0.2 php-fpm
# thread 28855 is clearly the culprit, not the process as a whole
# Cross-check with pidstat for a time-resolved view of the same thread
pidstat -t -p 28841 2 5
# Time UID TGID TID %usr %system %CPU Command
# 14:40 33 28841 - 0.10 0.20 0.30 php-fpm
# 14:40 33 - 28855 97.80 0.90 98.70 |__php-fpm
3. Wann begann es? Startzeit und Prozessbaum pruefen
ps -o pid,ppid,lstart,etime,cmd -p <PID> zeigt den exakten Startzeitpunkt und die verstrichene Laufzeit des betroffenen Prozesses. Ein Prozess, der seit Tagen laeuft und erst seit wenigen Minuten hohen CPU-Verbrauch zeigt, hat vermutlich einen internen Zustandswechsel durchlaufen, etwa eine Endlosschleife nach dem Erreichen eines bestimmten Datensatzes. Ein Prozess, der genau zum Beginn des Problems gestartet wurde, verweist dagegen direkt auf seinen Aufrufer.
pstree -p <PID> zeigt den Prozessbaum mit allen Eltern- und Kindprozessen und deckt auf, ob der auffaellige Prozess von cron, einem Supervisor wie systemd oder einer Webserver-Instanz erzeugt wurde. Bei PHP-FPM-Workern ist der Elternprozess immer der FPM-Master, weshalb hier stattdessen der Blick auf die aktuell verarbeitete Anfrage ueber den FPM-Statusendpunkt (pm.status_path) weiterhilft, um die auloesende URL oder das Script zu ermitteln.
Ergaenzend liefert systemctl status <service> bei systemd-verwalteten Diensten den Zeitpunkt des letzten Starts sowie die Anzahl bisheriger Neustarts. Ein Dienst, der sich seit dem letzten Deploy mehrfach unerwartet neu gestartet hat, deutet auf ein bereits bekanntes, aber noch ungeloestes Problem hin, das im Zusammenhang mit dem aktuellen CPU-Verbrauch stehen kann.
4. strace: Systemaufrufe live mitschneiden
strace ist das zentrale Werkzeug, um mysterioesen CPU-Verbrauch auf Systemaufruf-Ebene zu verstehen. strace -c -p <PID> sammelt fuer eine bestimmte Zeit alle Systemaufrufe des Zielprozesses und fasst sie am Ende nach Haeufigkeit und verbrauchter Zeit zusammen. Zeigt die Zusammenfassung ueberwiegend futex-Aufrufe, deutet das auf Locking-Konkurrenz zwischen Threads hin. Dominieren dagegen read oder recvfrom-Aufrufe in einer engen Schleife, liegt vermutlich ein Busy-Loop beim Warten auf Daten vor, statt eines blockierenden Warteaufrufs.
# Summarize syscalls over 5 seconds for a suspicious PID
strace -c -p 28855
# ^C (after ~5 seconds)
# % time seconds usecs/call calls syscall
# ------ ----------- ----------- --------- ------------------
# 94.20 2.981022 120 24801 futex
# 3.10 0.098213 8 12277 read
# 1.80 0.056904 45 1264 mmap
# Heavy futex activity suggests lock contention between threads
Zeigt dieses Beispiel, wie hier 94 Prozent der Zeit in futex-Aufrufen verbracht werden, ist das ein starkes Indiz fuer konkurrierendes Locking, etwa durch einen schlecht implementierten Cache mit zu granularen Sperren oder eine Bibliothek, die intern staerker synchronisiert als noetig. Ohne strace waere dieser Zusammenhang aus einer reinen CPU-Prozent-Anzeige niemals sichtbar geworden, weil top nur die verbrauchte Zeit zeigt, nicht deren Zusammensetzung.
Fuer eine noch feinere Aufloesung eignet sich strace -T -tt -p <PID>, das jeden einzelnen Systemaufruf mit exaktem Zeitstempel und individueller Dauer protokolliert, statt nur einer aggregierten Zusammenfassung. Damit lassen sich einzelne, ungewoehnlich lange Aufrufe innerhalb einer sonst unauffaelligen Sequenz identifizieren, was besonders bei sporadischem, schwer reproduzierbarem CPU-Verbrauch wertvoll ist.
5. perf top: die heissesten Funktionen live sehen
Waehrend strace auf der Ebene der Systemaufrufe ansetzt, zeigt perf top -p <PID> die tatsaechlich heissesten Funktionen im User- und Kernel-Space live an, basierend auf Sampling der Instruction-Pointer-Register per Hardware-Performance-Counter. Das macht es moeglich, bei kompiliertem Code oder auch bei PHP mit aktiviertem Opcache die konkrete Funktion zu identifizieren, die den CPU-Verbrauch verursacht, statt nur zu wissen, dass irgendetwas in diesem Prozess viel rechnet.
Bei interpretierten Sprachen wie PHP zeigt perf top primaer Funktionen der Zend Engine selbst, etwa execute_ex oder Regex-Routinen aus PCRE, was zwar nicht direkt die PHP-Codezeile verraet, aber bereits deutliche Hinweise liefert. Kombiniert mit Debug-Symbolen (perf top --call-graph dwarf) laesst sich zusaetzlich der Aufrufpfad rekonstruieren und damit oft die auslösende PHP-Funktion eingrenzen, insbesondere bei rechenintensiven Operationen wie komplexen regulaeren Ausdruecken oder Bildverarbeitung.
Fuer eine dauerhafte Aufzeichnung statt einer Live-Ansicht eignet sich perf record -p <PID> -g -- sleep 10, gefolgt von perf report. Dieses Vorgehen erfasst genau zehn Sekunden Aktivitaet in einer Datei, die spaeter in Ruhe analysiert werden kann, was bei einem sporadisch auftretenden CPU-Verbrauch deutlich praktikabler ist als eine kontinuierliche Live-Beobachtung mit perf top.
6. PHP-FPM-Sonderfall: Xdebug, Endlosschleifen, Regex
In PHP-lastigen Umgebungen wie Magento-Installationen gibt es wiederkehrende Muster fuer mysterioesen CPU-Verbrauch. Ein versehentlich in Produktion aktiviertes xdebug-Modul mit aktiviertem Profiler kann jede Anfrage massiv verlangsamen und CPU-Last erzeugen, die in der Entwicklungsumgebung nie auffiel, weil dort weniger parallele Requests liefen. php -m | grep xdebug und ein Blick in php.ini gehoeren deshalb zu den ersten Checks bei ploetzlich hoher PHP-FPM-Last.
Ein weiterer, oft vergessener Check ist die PHP-OPcache-Konfiguration selbst: Ist opcache.validate_timestamps aktiviert, prueft PHP bei jedem Request den Zeitstempel jeder eingebundenen Datei, was bei sehr grossen Codebasen mit tausenden Dateien spuerbaren zusaetzlichen CPU-Verbrauch erzeugen kann. In Produktionsumgebungen sollte diese Option deaktiviert und der Opcache stattdessen gezielt beim Deploy geleert werden.
Ein zweites haeufiges Muster ist katastrophales Backtracking in regulaeren Ausdruecken, sogenanntes ReDoS. Eine harmlos wirkende Regex mit verschachtelten Quantifizierern kann bei bestimmten Eingabestrings exponentiell lange Laufzeiten erzeugen, was sich als ploetzlicher CPU-Verbrauch auf genau einem Worker zeigt, sobald ein Nutzer eine entsprechende Eingabe sendet. strace zeigt in diesem Fall typischerweise sehr wenige Systemaufrufe bei gleichzeitig hoher CPU-Last, ein starkes Indiz fuer reine Rechenschleifen ohne IO.
Ein dritter, seltener diskutierter Fall ist ineffizientes Autoloading oder wiederholtes Neuparsen grosser Konfigurationsdateien bei jedem einzelnen Request, wenn ein Opcache-Eintrag durch eine fehlerhafte Invalidierungsregel staendig verworfen wird. Der resultierende CPU-Verbrauch verteilt sich hierbei gleichmaessig auf alle Worker, statt sich wie bei ReDoS auf einen einzelnen zu konzentrieren, was diese beiden Ursachen anhand der Verteilung ueber die Worker gut voneinander unterscheidbar macht.
7. Phantom-Prozesse durch Cron und Zombie-Kinder
Ein oft uebersehener Fall von mysterioesem CPU-Verbrauch entsteht, wenn ein Cron-Job durch einen Fehler in der Ausfuehrungslogik parallel mehrfach gestartet wird, weil das vorherige Lauf noch nicht beendet war und kein Locking-Mechanismus dies verhindert. ps aux | grep <scriptname> | wc -l zeigt schnell, ob statt eines erwarteten einzelnen Laufs ploetzlich zehn oder zwanzig parallele Instanzen desselben Skripts aktiv sind und gemeinsam die CPU saettigen.
Ein verwandtes Muster: Ein Elternprozess erzeugt Kindprozesse fuer Teilaufgaben, wartet aber wegen eines Bugs nicht korrekt auf deren Beendigung, wodurch sich Zombie- oder verwaiste Prozesse ansammeln. Waehrend echte Zombies selbst keine CPU verbrauchen, kann der fehlerhafte Elternprozess in einer Polling-Schleife auf sie warten und dabei selbst unnoetig CPU-Last erzeugen. Ein Blick mit ps --ppid <PID> auf alle Kindprozesse eines Verdaechtigen deckt solche Muster zuverlaessig auf.
Ein systemd-Timer, der versehentlich parallel zu einem klassischen Cron-Eintrag denselben Job ausloest, ist eine weitere haeufige Quelle fuer verdoppelten CPU-Verbrauch nach einer Migration von Cron zu systemd-Timern. systemctl list-timers zusammen mit crontab -l auf demselben System deckt eine solche Doppelkonfiguration schnell auf, bevor sie durch aufwendige Prozessanalyse gesucht werden muss.
8. Sofortmassnahmen: cgroups, nice, cpulimit
Bevor die eigentliche Ursache vollstaendig geklaert ist, muss ein Produktivsystem oft sofort entlastet werden. renice mit einem hoeheren Nice-Wert reduziert die Prioritaet eines auffaelligen Prozesses gegenueber anderen, ohne ihn zu beenden, was bei nicht kritischen Batch-Prozessen die Auswirkung auf den restlichen Server sofort abmildert. cpulimit -p <PID> -l 50 begrenzt einen Prozess auf einen festen Prozentsatz eines Kerns, ideal fuer einen einzelnen ausser Kontrolle geratenen Prozess, waehrend die eigentliche Ursache noch untersucht wird.
Bei PHP-FPM-Workern ist ein gezielter Neustart des betroffenen Workers oft die schnellste Sofortmassnahme, ohne den gesamten Pool neu zu starten. kill -QUIT <worker-PID> beendet einen einzelnen FPM-Worker geordnet, der Master-Prozess startet automatisch einen Ersatz-Worker, was die Auswirkung des CPU-Verbrauchs auf einen kurzen Moment begrenzt, statt den gesamten Dienst fuer alle Nutzer zu unterbrechen.
Fuer eine dauerhaftere Eindaemmung eignen sich cgroups: systemctl set-property <service>.service CPUQuota=50% begrenzt einen systemd-verwalteten Dienst nachtraeglich, ohne die Anwendung selbst zu aendern. Diese Massnahmen loesen die Ursache des CPU-Verbrauchs nicht, verhindern aber, dass ein einzelner fehlerhafter Prozess das gesamte System in Mitleidenschaft zieht, waehrend die Diagnose aus den vorherigen Abschnitten parallel weiterlaeuft.
Eine dokumentierte Eskalationsschwelle, etwa ein fester Nice-Wert oder ein CPU-Quota-Prozentsatz, der bei jedem verdaechtigen Prozess automatisch per Monitoring-Skript angewendet wird, verkuerzt die Reaktionszeit im Ernstfall erheblich. So muss niemand im Team unter Zeitdruck ueberlegen, welchen Befehl er gerade eintippen soll, sondern folgt einem bereits getesteten Ablauf fuer den Umgang mit unerklaertem CPU-Verbrauch.
9. Werkzeuge im Vergleich: Wann was einsetzen
Die folgende Uebersicht ordnet die vorgestellten Werkzeuge nach Einsatzgebiet, damit im Ernstfall sofort das passende Werkzeug fuer die jeweilige Verdachtsrichtung gewaehlt wird.
| Werkzeug | Zeigt | Wann einsetzen |
|---|---|---|
top -H |
CPU pro Thread | Einzelner heisser Thread identifizieren |
ps -o lstart,etime |
Startzeit, Laufzeit | Zeitpunkt des Problembeginns eingrenzen |
strace -c |
Systemaufruf-Statistik | Locking, blockierendes IO, Busy-Loops erkennen |
perf top |
Heisseste Funktionen | Konkreten Code-Pfad eingrenzen |
cpulimit, cgroups |
CPU-Begrenzung | Sofort-Eindaemmung waehrend der Diagnose |
pstree -p |
Prozessbaum, Herkunft | Auloesenden Elternprozess identifizieren |
| FPM-Statusendpunkt | Aktuelle Anfrage pro Worker | Verursachende URL bei PHP-FPM finden |
Die Kombination dieser Werkzeuge verwandelt eine vage Beobachtung wie ein Prozess ist bei 100 Prozent in eine konkrete, handlungsfaehige Diagnose. In der Praxis reicht selten ein einzelnes Werkzeug: top -H lokalisiert den Thread, strace die Art der Aktivitaet und perf den konkreten Code-Pfad, waehrend cpulimit die Zeit fuer diese Analyse ueberhaupt erst schafft.
Mironsoft
Performance-Diagnose, PHP-FPM-Tuning und Magento-Hosting
Ein Prozess frisst CPU und niemand weiss warum?
Wir gehen mit strace, perf und Thread-Analyse gezielt der Ursache eines auffaelligen CPU-Verbrauchs auf den Grund und liefern eine konkrete Empfehlung statt vager Vermutungen.
CPU-Deep-Dive
strace und perf gezielt auf verdaechtige Prozesse und Threads anwenden
PHP-FPM-Tuning
Regex-Fallen, Xdebug-Reste und Worker-Konfiguration pruefen
Sofort-Eindaemmung
cgroups und cpulimit fuer stabilen Betrieb waehrend der Analyse
10. Zusammenfassung
Mysterioesen CPU-Verbrauch aufzuspueren gelingt selten durch eine einzige Beobachtung, sondern durch eine Kette aufeinander aufbauender Werkzeuge. top -H engt den Verdacht auf einen einzelnen Thread ein, ps und pstree klaeren Herkunft und Zeitpunkt, strace -c zeigt die Art der Aktivitaet auf Systemaufruf-Ebene, und perf top liefert den konkreten Code-Pfad. PHP-spezifische Fallen wie Xdebug-Reste und Regex-Backtracking gehoeren zu den haeufigsten Ursachen in PHP-lastigen Umgebungen.
Wer diesen Diagnoseweg einmal dokumentiert hat, muss ihn beim naechsten Vorfall nicht neu erfinden, sondern kann direkt mit der Thread-Analyse beginnen, statt wertvolle Zeit mit wahllosem Ausprobieren verschiedener Werkzeuge zu verschwenden.
Waehrend die eigentliche Diagnose laeuft, verhindern cpulimit oder cgroup-basierte CPU-Quotas, dass ein einzelner fehlerhafter Prozess den gesamten Server in Mitleidenschaft zieht. Wer diesen Werkzeugkasten einmal beherrscht, verwandelt vage Alarme in konkrete, nachvollziehbare Ursachen, die sich dauerhaft beheben lassen, statt den betroffenen Prozess immer wieder neu zu starten.
Mysterioesen CPU-Verbrauch aufspueren — Das Wichtigste auf einen Blick
Thread lokalisieren
top -H -p PID zeigt, ob ein einzelner Thread oder der gesamte Prozess die Last verursacht.
Aktivitaetsart bestimmen
strace -c -p PID zeigt, ob Locking, IO oder reine Rechenschleifen dominieren.
Code-Pfad finden
perf top -p PID zeigt die tatsaechlich heissesten Funktionen im Prozess.
Sofort eindaemmen
cpulimit oder CPUQuota in systemd begrenzen die Auswirkung waehrend der Analyse.