
Linux commando: top
Een server is traag, dus typ je top. Bijna iedereen doet dat. En vervolgens leest bijna iedereen de load average op de eerste regel, ziet een getal dat hoger is dan verwacht, concludeert dat de CPU overbelast is, en zoekt het daarna een uur lang op de verkeerde plek. De load average is geen CPU-meting. De geheugenregel zegt niet wat de meeste mensen denken dat hij zegt. En een %CPU van 400 is geen fout. top hoort al sinds 1984 bij Unix en is nog steeds het programma dat met het meeste zelfvertrouwen verkeerd wordt gelezen.
1. De basis
top laat je een lijst met processen zien, gesorteerd op CPU-gebruik, elke paar seconden ververst, met daarboven een samenvatting van de hele machine. Dat is het hele idee. Alles wat de moeite waard is zit in wat die getallen werkelijk betekenen, want een aantal ervan betekent iets heel anders dan het lijkt.
1.1 De eenvoudigst mogelijke toepassing
Typ de naam, verder niets:
$ top
Het scherm wordt leeggemaakt en vult zich met een vaste indeling. Druk op q om te stoppen, of op h voor hulp. Dat zijn de enige twee toetsen die je vooraf moet kennen.
top - 17:21:38 up 6:11, 1 user, load average: 1.63, 1.39, 1.29
Tasks: 517 total, 2 running, 515 sleeping, 0 stopped, 0 zombie
%Cpu(s): 4.7 us, 2.6 sy, 0.0 ni, 92.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 64026.3 total, 34464.3 free, 14897.2 used, 16868.6 buff/cache
MiB Swap: 8192.0 total, 8192.0 free, 0.0 used. 49129.1 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
118289 pe7er 20 0 1448.5g 557844 260284 R 91.7 0.9 1:42.29 chrome
10681 pe7er 20 0 10.6g 3.7g 1.3g S 16.7 5.9 87:49.77 phpstorm
106453 pe7er 20 0 1448.4g 251576 157480 S 8.3 0.4 16:10.38 chrome
120269 pe7er 20 0 14868 5584 3536 R 8.3 0.0 0:00.02 top
1 root 20 0 24500 15008 9248 S 0.0 0.0 0:04.40 systemd
Alle uitvoer in dit artikel komt van die machine: zestien CPU-kernen, 62,5 GiB geheugen, Ubuntu, top uit procps-ng 4.0.4.
1.2 De vijf samenvattingsregels
Het blok bovenaan beschrijft de machine, niet een afzonderlijk proces. Elke regel heeft zijn eigen schakeltoets, en elke regel wordt op zijn eigen manier verkeerd gelezen.
| Regel | Toont | Toets | De gebruikelijke misvatting |
|---|---|---|---|
top - 17:21:38 up 6:11 ... |
Klok, uptime, ingelogde gebruikers, load average | l |
Dat de load average een CPU-percentage is. Dat is het niet (paragraaf 4.1). |
Tasks: 517 total ... |
Aantal processen per toestand | t |
Dat "running" hetzelfde is als "bezig". Het betekent klaar om te draaien. |
%Cpu(s): 4.7 us ... |
CPU-tijd verdeeld over categorieën | t |
Dat dit een CPU is. Het is het gemiddelde van alle zestien (paragraaf 5.1). |
MiB Mem : ... |
Fysiek geheugen | m |
Dat een lage free een probleem is. Lees avail Mem (paragraaf 4.3). |
MiB Swap: ... |
Swap, plus avail Mem |
m |
Dat avail Mem bij swap hoort. Het staat op de swapregel maar gaat over RAM. |
1.3 De kolommen van de processenlijst
Onder de kop komt een regel per proces. Dit zijn de standaardkolommen:
| Kolom | Betekenis |
|---|---|
PID |
Proces-ID. |
USER |
De effectieve gebruikersnaam van de eigenaar. |
PR |
Planningsprioriteit van de kernel. Normaal NI plus 20 (paragraaf 6.4). |
NI |
Nice-waarde, -20 tot 19. Lager is hebberiger. |
VIRT |
Alles wat in de adresruimte is gekoppeld. Grotendeels betekenisloos (paragraaf 7.3). |
RES |
Resident geheugen: wat er echt in RAM staat. Het getal dat ertoe doet, maar je mag het niet optellen (paragraaf 7.2). |
SHR |
Het deel van RES dat met andere processen gedeeld kan worden. |
S |
Toestand: R, S, D, I, T, t of Z. |
%CPU |
Aandeel CPU-tijd sinds de vorige verversing. Geen gemiddelde over de levensduur (paragraaf 9.1). |
%MEM |
Simpelweg RES gedeeld door het totale fysieke geheugen. |
TIME+ |
Totale CPU-tijd sinds het proces startte, in honderdsten van een seconde. Geen kloktijd. |
COMMAND |
Programmanaam, of de volledige commandoregel met c. |
De toestandsletters komen voortdurend langs in supportgesprekken, dus het loont om ze goed te kennen:
R running, or ready to run and waiting for a CPU
S sleeping, waiting for something that can be interrupted
D uninterruptible sleep, nearly always waiting for disk or network storage
I idle kernel thread
T stopped by a job control signal (Ctrl-Z)
t stopped by a debugger
Z zombie: finished, but its parent has not collected the exit status
D is de interessante. Een proces in D kun je niet afbreken, zelfs niet met kill -9, omdat het midden in een kernelaanroep zit die nog niet is teruggekeerd. Het telt bovendien mee in de load average, en daar begint de meeste verwarring over dat getal.
1.4 Het mentale model
Een zin verklaart bijna elke verrassing in dit artikel.
topbemonstert. Elk veld met een procentteken is een verschil tussen twee metingen, genomen over het verversingsinterval. Elk veld zonder procentteken is een totaal, opgebouwd sinds het proces startte of sinds de opstart.
Daarom kunnen %CPU en TIME+ zo hevig met elkaar in tegenspraak zijn. TIME+ zegt dat een proces sinds dinsdag negentig minuten CPU heeft gebruikt. %CPU zegt dat het in de laatste drie seconden niets gebruikte. Allebei waar, en ze beantwoorden verschillende vragen.
Het verklaart ook waarom het allereerste scherm anders is dan de schermen erna, en waarom een script dat top een keer draait een antwoord krijgt dat het niet moet vertrouwen. Paragraaf 6.2 meet precies hoe erg dat is.
2. Waar komt de naam vandaan?
De naam is een beschrijving van de uitvoer. top sorteert processen op CPU-gebruik en laat je de processen bovenaan die lijst zien. Niet de bovenkant van het scherm, en ook niet "top" in de zin van beste: de zwaarste verbruikers, op volgorde.
Het programma werd in 1984 geschreven door William LeFebvre, destijds aan Rice University. Zijn reden was eenvoudig, en hij heeft die zelf helder verwoord: Unix had geen goede realtime monitoring, VMS wel, en hij vroeg zich af waarom. Dus schreef hij er een en plaatste die in een USENET-groep voor softwaredistributie, zodat iedereen hem kon downloaden en compileren. Zo verspreidde software zich in 1984, en het werkte goed genoeg dat zijn programma veertig jaar later nog steeds op jouw server staat.
Een detail uit die eerste versie is onaangeroerd blijven bestaan en je kunt het vandaag nog zien. De top van LeFebvre ververste elke drie seconden. Die op deze machine doet dat ook, en hij vertelt het je als je om hulp vraagt:
$ top
h # then press h for the help screen
Window 1:Def: Cumulative mode Off. System: Delay 3.0 secs; Secure mode Off.
Drie namen zijn het waard om uit elkaar te houden, want pakketzoekopdrachten en foutmeldingen gebruiken ze alle drie:
procps the original Linux package of /proc tools: ps, top, free, uptime, watch
procps-ng the "next generation" fork from 2011, which is what you now run
top(1) the manual page; note the 1, because there is no top(8)
En top is niet een programma. Minstens drie ongerelateerde implementaties delen de naam, en daarom kan een optie die je op een Linux-server hebt geleerd op een BSD of in een container niet bestaan. Sectie 3 legt uit hoe dat zo gekomen is.
3. Een korte geschiedenis
De Linux-top stamt niet af van die van LeFebvre. Het is een herimplementatie die de naam, de indeling en het ritme van drie seconden overnam en daarna zijn eigen weg ging. De tweede helft van dat verhaal lees je in het copyrightbestand op elke Debian- of Ubuntu-machine:
$ grep -B1 -A1 "James C. Warner" /usr/share/doc/procps/copyright
Files: src/top/*
Copyright: 2002-2022 James C. Warner
License: LGPL-2.0+
| Periode | Mijlpaal |
|---|---|
| 1984 | William LeFebvre schrijft top en verspreidt het via USENET. Het wordt de standaard procesmonitor van Unix en wordt nog steeds apart onderhouden als "unix-top". |
| 1992 | Roger Binns schrijft een top voor Linux, die snel door anderen wordt overgenomen en in het pakket procps belandt. Een apart programma, dat alleen de naam en het idee deelt. |
| 2002 | James C. Warner herschrijft de Linux-top volledig. Vensters, veldgroepen, het configuratiebestand en de interactieve hulp stammen allemaal uit deze versie, en zijn copyright staat nog altijd boven de broncode. |
| 2011 | De fork procps-ng neemt het onderhoud over nadat het oorspronkelijke procps stilvalt. Elke grote distributie volgt. |
| 2022 | procps-ng 4.0.0 voegt lange opties toe (--batch, --delay), de velden EXE en LOGID, vier velden voor I/O-boekhouding, en de kolommen %CUU / %CUC voor CPU-gebruik over de levensduur. |
| 2023 | procps-ng 4.0.4, de versie in dit artikel. Onjuiste argumenten op de commandoregel geven nu een foutcode, en gasttijd van virtuele machines telt mee als systeemoverhead. |
Daar volgen twee praktische gevolgen uit, en mensen lopen er allebei tegenaan.
Ten eerste: top zit niet in POSIX en heeft daar nooit in gezeten. Er is geen standaard voor, dus er is geen enkele garantie dat de opties uit jouw aantekeningen op de volgende machine werken. Op FreeBSD of macOS krijg je een afstammeling van de versie van LeFebvre, met een eigen set opties en eigen veldnamen. In een minimale containerimage krijg je vaak BusyBox-top, alweer een derde implementatie met een compleet andere uitvoer. Controleer wat je werkelijk draait voordat je op een optie vertrouwt, en lees de lokale handleiding in plaats van een die je online vond:
$ top -V
top from procps-ng 4.0.4
Ten tweede: er zijn echt steeds opties bij gekomen. -E, -e, -O en de lange vormen zijn recent. Een server van vijf jaar oud heeft ze misschien niet, en een handleiding uit 2015 beschrijft mogelijk een programma dat zich inmiddels anders gedraagt.
4. Eenvoudige toepassingen
4.1 De load average, die bijna iedereen verkeerd leest
Dit is het meest misbegrepen getal in Linux-beheer, dus het is de moeite waard om precies te zijn. De drie cijfers zijn de belasting gemiddeld over de laatste 1, 5 en 15 minuten:
$ uptime
17:21:38 up 6:11, 1 user, load average: 1.63, 1.39, 1.29
top rekent dit niet uit. Hij leest het ongewijzigd uit een bestand van de kernel:
$ cat /proc/loadavg
1.63 1.39 1.29 2/2953 120271
En hier is de definitie, uit de handleiding van dat bestand. Lees het middelste stuk twee keer:
$ man proc_loadavg
The first three fields in this file are load average figures giving
the number of jobs in the run queue (state R) or waiting for disk I/O
(state D) averaged over 1, 5, and 15 minutes.
Of wachtend op schijf-I/O. Die bijzin maakt Linux anders dan elke andere Unix, en daarom is de load average geen CPU-maat. Een machine met een falende schijf, een NFS-koppeling die weg is, of een database die zware synchrone schrijfacties doet, kan een load average van 40 tonen terwijl de CPU's vrijwel niets doen. Elk van die geblokkeerde processen staat in toestand D en elk ervan telt mee.
De load average beantwoordt dus de vraag "hoeveel taken willen nu vooruit", niet "hoe druk is de processor". Drie regels maken hem bruikbaar:
- Deel door het aantal kernen. Een load van 8,0 op deze machine met zestien kernen is halve capaciteit. Diezelfde 8,0 op een VPS met twee kernen betekent dat het werk zich flink opstapelt.
nprocgeeft je de deler. - Lees de drie getallen als een richting, niet als drie feiten.
8.0, 2.0, 1.0is een piek die net begon.1.0, 2.0, 8.0is een probleem dat aan het wegtrekken is. Het getal van een minuut zegt op zichzelf niets over welke van de twee. - Kijk naar de CPU-regel voordat je de CPU de schuld geeft. Hoge load met een hoge
id(idle) betekent dat de wachtrij op iets anders wacht, en dat iets anders is bijna altijd opslag.
Het vierde veld van /proc/loadavg is de momentopname van hetzelfde, zonder middeling: 2/2953 betekent dat twee planningseenheden van de kernel klaar staan om te draaien, van de 2953 die bestaan.
4.2 De CPU-regel
De percentages op de %Cpu(s)-regel zijn aandelen van de CPU-tijd die sinds de vorige verversing verstreek, opgeteld over alle kernen en gedeeld door het aantal kernen.
%Cpu(s): 4.7 us, 2.6 sy, 0.0 ni, 92.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
| Veld | Staat voor | Betekenis |
|---|---|---|
us |
user | Gewone gebruikerscode draaien. Jouw applicatie. |
sy |
system | Kernelcode draaien namens iemand: systeemaanroepen, bestandssysteem, netwerkstack. |
ni |
nice | Gebruikerscode draaien die naar een lagere prioriteit is geniced. Meestal 0. |
id |
idle | Helemaal niets doen. |
wa |
wait | Niets doen terwijl minstens een taak wachtte op I/O. |
hi |
hardware interrupt | Hardware-interrupts afhandelen. |
si |
software interrupt | Software-interrupts afhandelen. Loopt op bij zwaar netwerkverkeer. |
st |
steal | Tijd die de hypervisor aan de virtuele machine van iemand anders gaf in plaats van aan die van jou. |
Drie hiervan verdienen meer dan een tabelregel.
Een hoge sy betekent dat de kernel het werk doet, niet jouw code. Op een applicatieserver is dat meestal een signaal en geen oorzaak. De kernel is druk namens iemand, en de vraag is namens wie. De gebruikelijke bronnen zijn zwaar netwerkverkeer, veel bestandssysteemactiviteit, een programma dat een enorm aantal kleine systeemaanroepen doet, en veel proces- of threadverloop: iets dat per verzoek een worker afsplitst, of een script dat in een lus een commando start. Een webserver met 40 procent sy en 10 procent us rekent zelden ergens aan; die doet meestal een enorme hoeveelheid kleine I/O. strace -c -p PID telt systeemaanroepen per naam en wijst de schuldige meestal in een keer aan.
wa is een vorm van niets doen, niet van bezig zijn. De CPU doet niets; hij doet alleen niets terwijl iets op een schijf wacht. Dat heeft een gevolg dat mensen tegenintuitief vinden: wa wordt begrensd door het aantal vrije kernen, dus op een machine met zestien kernen kan een enkel proces dat op I/O vastzit hem niet hoger duwen dan ongeveer 6 procent. Dit is wat twaalf parallelle directe leesacties ermee deden op deze machine:
$ top -b -n 2 -d 1 | grep '^%Cpu' | tail -1
%Cpu(s): 3.6 us, 3.5 sy, 0.0 ni, 73.1 id, 19.8 wa, 0.0 hi, 0.1 si, 0.0 st
# and one second after the readers finished:
%Cpu(s): 6.3 us, 1.3 sy, 0.0 ni, 92.5 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
Negentien procent, van twaalf processen die een NVMe-schijf bestookten. Op een drukke webserver met een trage schijf kun je echte I/O-uithongering zien terwijl wa op 3 procent staat. Behandel het als een aanwijzing en bevestig het met iostat -x of met de druk-bestanden van de kernel:
$ cat /proc/pressure/io
some avg10=8.31 avg60=2.19 avg300=0.48 total=12829609
full avg10=8.07 avg60=2.13 avg300=0.47 total=11957041
Die bestanden verdienen een eigen alinea, want ze beantwoorden de vraag waar wa alleen naar wijst. Pressure Stall Information meet welk deel van de tijd taken vertraagd werden doordat ze op een bron wachtten, in plaats van welk deel van de tijd die bron bezig was. Er zijn er drie:
/proc/pressure/cpu waiting for a CPU to become free
/proc/pressure/memory stalled on reclaim, page faults, swapping
/proc/pressure/io stalled waiting for storage
Elk bestand heeft twee regels. some is het deel van de tijd dat minstens een taak vastliep; full is het deel van de tijd dat elke taak vastliep, en dat is degene die echte problemen betekent. De drie getallen zijn gemiddelden over 10, 60 en 300 seconden, en je leest ze net als de load average.
Het voordeel boven wa is dat PSI niet verdund wordt door het aantal kernen. Een enkel proces dat op I/O vastzit kan wa op een machine met zestien kernen niet boven de 6 procent krijgen, terwijl de io-druk eerlijk oploopt. Bij de leesacties hierboven kwam wa op 19,8 terwijl de some-druk voor io van 0,50 naar 8,31 ging. Als een server traag aanvoelt en elk bezettingscijfer redelijk lijkt, zijn deze drie bestanden de snelste plek om te ontdekken waar werkelijk op gewacht wordt.
st is je hostingprovider. Op een virtuele machine is steal time CPU die de hypervisor bij jou weghaalde en aan een andere huurder gaf. Een aanhoudende st boven een paar procent betekent dat de fysieke host overboekt is, en geen enkele afstelling binnen je VM lost dat op. Op fysieke hardware blijft hij op 0,0 staan.
4.3 De geheugenregels, en "Linux eet mijn RAM op"
Deze twee regels veroorzaken meer onnodige supporttickets dan al het andere in de uitvoer:
MiB Mem : 64026.3 total, 34464.3 free, 14897.2 used, 16868.6 buff/cache
MiB Swap: 8192.0 total, 8192.0 free, 0.0 used. 49129.1 avail Mem
Elk van die getallen komt rechtstreeks uit /proc/meminfo, en het rekensommetje is het waard om een keer te maken zodat je er nooit meer aan twijfelt:
used = MemTotal - MemAvailable
buff/cache = Buffers + Cached + SReclaimable
avail Mem = MemAvailable
Gecontroleerd tegen het bestand op deze machine, op een afrondingsverschil na:
$ awk '/^MemTotal|^MemAvailable|^Buffers|^Cached:|^SReclaimable/ {printf "%-14s %10.1f MiB\n", $1, $2/1024}' /proc/meminfo
MemTotal: 64026.3 MiB
MemAvailable: 49164.1 MiB
Buffers: 1445.1 MiB
Cached: 14704.2 MiB
SReclaimable: 1185.2 MiB
MemTotal - MemAvailable = 14852.2 MiB # matches "used"
Buffers + Cached + SRecl = 17334.1 MiB # matches "buff/cache"
Nu het belangrijke deel. buff/cache is geen verloren geheugen. Het is de paginacache: kopieën van bestanden die de kernel in RAM houdt omdat dat RAM anders leeg zou staan, en het wordt teruggegeven op het moment dat een programma erom vraagt. Een gezonde server die lang draait en genoeg geheugen heeft, hoort een kleine free en een grote buff/cache te tonen. Ongebruikt geheugen is verspild geheugen.
Dus lees avail Mem en niet free. Het staat aan het eind van de swapregel, wat een ongelukkige plek is, maar het is een getal over fysiek geheugen en het is het eerlijke antwoord op "hoeveel kan ik nog starten?". Hier is free 33,7 GiB en avail Mem 48 GiB, en met het tweede getal moet je plannen.
De swapregel is eenvoudiger. Swap die gebruikt wordt is niet automatisch slecht; de kernel verplaatst echt inactieve pagina's met opzet naar buiten. Swap die actief heen en weer gaat is wel slecht, en top vertelt je het verschil niet. vmstat 1 en de kolommen si/so doen dat wel.
Als de eenheden je storen, verander ze dan. -E (kort voor scale-summary-mem) schaalt de kop en -e (kort voor scale-task-mem) schaalt de procesregels:
$ top -b -n 1 -E g -e m
GiB Mem : 62.5 total, 33.4 free, 14.7 used, 16.5 buff/cache
GiB Swap: 8.0 total, 8.0 free, 0.0 used. 47.9 avail Mem
Interactief zijn dat dezelfde schakelaars: de toetsen E en e, die door KiB tot en met EiB lopen.
4.4 Sorteren
top start gesorteerd op %CPU, hoog naar laag. Vier losse toetsen veranderen dat. Ze zijn bewaard voor compatibiliteit met de oudere Linux-top, en juist daarom vermeldt de handleiding dat ze bewust op geen enkel hulpscherm verschijnen, dus je vindt ze niet door op h te drukken:
P sort by %CPU (the default)
M sort by %MEM the one you want when memory is the question
T sort by TIME+ total CPU burned since the process started
N sort by PID
Druk eerst op x. Dat markeert de kolom waarop nu gesorteerd wordt, wat elke twijfel wegneemt over wat je zit te bekijken. R keert de richting om, en < en > verplaatsen de sortering een kolom naar links of rechts.
T wordt onderschat. %CPU laat zien wie op dit moment bezig is; TIME+ laat zien wie de hele week bezig is geweest. Een proces dat stilletjes veertig uur CPU heeft verzameld op een server die niets zou moeten doen, is een veel beter spoor dan wat er toevallig nu bovenaan staat.
4.5 De toetsen die je als eerste moet leren
De volledige lijst is lang. Deze acht dekken bijna alles wat je in een eerste sessie nodig hebt:
| Toets | Doet |
|---|---|
h |
Hulp. Nog een keer drukken geeft het tweede hulpscherm. |
q |
Stoppen. |
M / P / T |
Sorteren op geheugen / CPU / totale CPU-tijd. |
c |
Wisselen tussen programmanaam en volledige commandoregel. |
1 |
De CPU-regel opsplitsen in een regel per kern (paragraaf 5.1). |
u |
Maar een gebruiker tonen. Leeg invoeren wist het weer. |
k |
Een proces afbreken op PID. |
i |
Taken verbergen die sinds de vorige verversing geen CPU gebruikten. Maakt van 500 regels een handjevol. |
d |
Het verversingsinterval veranderen. |
i is degene waar mensen het blijst mee zijn als ze hem ontdekken. Het grootste deel van de processenlijst van een server slaapt en zal blijven slapen, en als je dat verbergt hou je alleen de taken over die sinds de vorige verversing echt iets hebben gedaan. Hij bestaat ook als -i op de commandoregel, en = zet alles weer terug.
Een toets verdient een waarschuwing: W schrijft je huidige instellingen naar een configuratiebestand en ze worden blijvend, ook voor scripts. Paragraaf 6.3 legt uit waarom dat meer uitmaakt dan het klinkt.
5. Gemiddelde toepassingen
5.1 Een kern volledig bezet, vijftien niets doen
De %Cpu(s)-regel is een gemiddelde, en gemiddelden verbergen precies het probleem waar je meestal naar op zoek bent. Een proces met een thread dat een kern volledig bezet houdt op een machine met zestien kernen kan het gemiddelde maar met 6 procent laten stijgen, dus de kop ziet er rustig uit terwijl een kern in brand staat.
Druk op 1. Dat splitst die ene regel op in een regel per kern. Hier is een machine met een enkele bezige lus, op beide manieren bekeken:
# the default, combined view
%Cpu(s): 16.2 us, 0.7 sy, 0.0 ni, 83.0 id, 0.0 wa, 0.0 hi, 0.2 si, 0.0 st
# after pressing 1
%Cpu0 : 3.0 us, 1.0 sy, 0.0 ni, 96.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 : 5.1 us, 1.0 sy, 0.0 ni, 93.9 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu2 : 3.0 us, 1.0 sy, 0.0 ni, 96.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu3 : 98.0 us, 0.0 sy, 0.0 ni, 2.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu4 : 3.0 us, 1.0 sy, 0.0 ni, 96.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
...
%Cpu15 : 5.0 us, 1.0 sy, 0.0 ni, 94.1 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
Zestien procent in de samenvatting. Achtennegentig procent op kern 3. Dit is de handtekening van een knelpunt met een enkele thread, en het is een van de nuttigste dingen die top je kan laten zien: een PHP-script, een cron-taak of een databasequery die niet over kernen verdeeld kan worden, hoeveel je er ook bijkoopt.
Op een machine met veel kernen passen de regels per kern niet allemaal. 4 zet meerdere CPU's op elke regel, en ! voegt ze samen per twee, dan per vier, dan per acht. -1 op de commandoregel start met de schakelaar al omgezet.
5.2 De kernen die een proces niet mag gebruiken
Paragraaf 5.1 liet een proces zien dat maar een kern kon gebruiken omdat het maar een thread had. Er is een tweede reden waarom een druk proces vijftien vrije kernen negeert, en die is onzichtbaar in elke kolom die top standaard toont: het proces mag er misschien niet op draaien.
Linux laat je een taak vastzetten op een deel van de CPU's. taskset leest dat masker en stelt het in:
$ taskset -cp 130667 # -c for a CPU list, -p for an existing pid
pid 130667's current affinity list: 2
$ taskset -c 0,1 ./import.sh # start something on CPUs 0 and 1 only
$ taskset -cp 0-3 130667 # move a running process onto CPUs 0 to 3
Een bezige lus die vastgezet is op CPU 2 ziet er in de processenlijst volstrekt normaal uit. Hij gebruikt een hele kern, en top zegt dat ook:
$ top -b -n 2 -d 1 -p 130667 | tail -1
130667 pe7er 20 0 10308 3508 3252 R 99.0 0.0 0:12.48 bash
Druk op 1 en de beperking wordt zichtbaar. Een kern draait op volle toeren en zijn buren slapen, en geen enkele hoeveelheid vrije capaciteit elders helpt daarbij:
%Cpu1 : 1.7 us, 0.0 sy, 0.0 ni, 98.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu2 :100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu3 : 2.3 us, 0.6 sy, 0.0 ni, 97.2 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
...
Dat beeld is identiek aan dat van de enkele thread in paragraaf 5.1, en juist daarom is het de moeite waard om dit te weten. De twee oorzaken zien er op het scherm hetzelfde uit en hebben totaal verschillende oplossingen: de applicatie van threads voorzien, of de vastzetting weghalen. taskset -cp is het ene commando dat ze uit elkaar houdt.
Affiniteit wordt zelden per ongeluk ingesteld, maar vaak wel ingesteld en daarna vergeten. De gebruikelijke bronnen op een server:
| Waar de vastzetting vandaan kwam | Hoe die is ingesteld |
|---|---|
| Een systemd-servicebestand | CPUAffinity= in het unitbestand, of in /etc/systemd/system.conf voor alles |
| Een container | docker run --cpuset-cpus 0,1 |
| Een afstelscript dat iemand in 2019 schreef | taskset in een wrapper, jaren later nog steeds draaiend op een grotere machine |
| De applicatie zelf | Een configuratieoptie, gebruikelijk bij databases en bij het afstellen van de JVM |
top kan je laten zien op welke kern een taak het laatst draaide. Zet het veld P aan met f, en kijk hoe het stil blijft staan bij een vastgezet proces en rondzwerft bij een normaal proces. De handleiding geeft een eerlijke waarschuwing bij die kolom: "the very act of running top may break this weak affinity and cause more processes to change CPUs more often".
Een onderscheid om goed te onthouden, want deze twee worden voortdurend verward. Affiniteit zegt waar een taak mag draaien. Een CPU-quotum van een cgroup, zoals een container dat krijgt, zegt hoeveel hij mag draaien, zonder hem tot bepaalde kernen te beperken. Een proces kan afgeknepen worden tot een halve CPU en toch over alle zestien kernen ingepland worden. Paragraaf 7.1 behandelt wat top daarvan wel en niet kan zien.
5.3 Een %CPU van 400 is geen fout
Standaard draait top in wat de handleiding Irix-modus noemt, waarin 100 procent een volledig bezette kern betekent. Een proces met vier bezige threads toont daarom 400. Dit is een compressietaak met vier werkthreads:
$ top -b -n 2 -d 1 -p 123850 | tail -1
123850 pe7er 20 0 372564 70144 2304 S 401.0 0.1 0:16.83 xz
Druk op H (kort voor threads-show) en hetzelfde proces valt uiteen in zijn threads, elk met een eigen regel en een eigen aandeel:
$ top -b -n 2 -d 1 -H -p 123850 | tail -5
123852 pe7er 20 0 372564 70144 2304 R 99.9 0.1 0:05.40 xz
123853 pe7er 20 0 372564 70144 2304 R 99.9 0.1 0:05.39 xz
123854 pe7er 20 0 372564 70144 2304 R 99.9 0.1 0:05.39 xz
123855 pe7er 20 0 372564 70144 2304 R 99.9 0.1 0:05.39 xz
123850 pe7er 20 0 372564 70144 2304 S 1.0 0.1 0:00.05 xz
Vier threads op 100 procent en een ouderproces dat de coördinatie doet. De 401 was nooit mysterieus; het waren vier kernen plus afronding.
Dat is het antwoord dat je wilt als een proces op 100 procent blijft steken en niet hoger komt: is het een verzadigde thread, of veertig threads die elk een beetje doen? Twee commando's buiten top beantwoorden dat prettiger, en allebei laten ze zien op welke CPU elke thread het laatst draaide:
$ ps -L -p 130929 -o pid,tid,psr,pcpu,comm
130929 130929 4 0.6 xz # the parent, idle
130929 130931 6 102 xz # tid, psr = which core, pcpu
130929 130932 3 102 xz
130929 130933 2 102 xz
$ pidstat -t -p 130929 1 1
Average: 1000 130929 - 300.00 1.00 ... xz
Average: 1000 - 130931 100.00 0.00 ... |__xz
Average: 1000 - 130932 100.00 0.00 ... |__xz
Average: 1000 - 130933 100.00 0.00 ... |__xz
pidstat -t is de duidelijkste van de twee: hij drukt het totaal van het proces op een regel af en zet elke thread daaronder ingesprongen. Drie threads op 100 met een totaal van 301 is een gezonde parallelle taak. Een thread op 100 met een totaal van 101, bij een proces met veertig threads, is een knelpunt met een naam.
Als je de andere afspraak prettiger vindt, schakelt I over naar de Solaris-modus, die deelt door het aantal kernen zodat 100 procent de hele machine betekent. Hetzelfde proces, een toetsaanslag later:
Irix mode Off
123978 pe7er 20 0 372564 70272 2304 S 25.1 0.1 0:25.04 xz
401 gedeeld door zestien kernen is 25,1. Geen van beide getallen is fout; ze beantwoorden verschillende vragen. De Irix-modus vertelt je hoeveel kernen een proces gebruikt, de Solaris-modus vertelt je welk deel van de machine het gebruikt. Weet welke van de twee je bekijkt voordat je iemand een getal noemt, want I is een blijvende schakelaar en een collega kan hem omgezet hebben.
5.4 Minder tonen
Vijfhonderd regels die voorbijschuiven is geen diagnose. top heeft drie aparte manieren om de lijst te versmallen, en ze gedragen zich verschillend.
-u (kort voor filter-only-euser) kijkt naar de effectieve gebruiker; -U (kort voor filter-any-user) kijkt naar de echte, effectieve, opgeslagen of bestandssysteemgebruiker. Zet er ! voor om het om te keren:
$ top -u www-data # only the web server's processes
$ top -u mysql # only the database
$ top -U '!root' # everything that is not root
-p (kort voor pid) volgt specifieke processen, tot twintig stuks:
$ top -p 1234,5678
$ top -p $(pgrep php-fpm | head -20 | paste -sd,) # the 20-pid limit, safely
En interactief bouwen o en O een filter op werkelijk elk veld. De syntaxis is een veldnaam, een operator en een waarde:
o then type COMMAND=chrome only chrome processes (case ignored)
O then type RES>500000 only processes above ~500 MiB resident
o then type !USER=root everything except root
^O show which filters are active
= clear all filters in this window
Nu de valkuil, en die overkomt iedereen een keer. Het filteren van de processenlijst filtert de samenvatting niet. Met een filter dat alleen chrome toont, telt de kop nog steeds de hele machine:
$ top -b -n 1 -u root | sed -n '2p'
Tasks: 515 total, 1 running, 514 sleeping, 0 stopped, 0 zombie
$ top -b -n 1 -u root | tail -n +8 | wc -l
305 # only 305 root processes are listed
515 in de kop, 305 in de lijst. De enige uitzondering is -p, die het aantal wel beperkt, dus een gefilterde Tasks:-regel betekent dat je -p gebruikte en verder niets. Noem nooit een getal uit de samenvatting van een gefilterd scherm.
5.5 Zien wat een proces werkelijk is
COMMAND toont standaard de programmanaam, wat nutteloos is als twintig regels allemaal php-fpm zeggen. c (ook beschikbaar als -c, kort voor cmdline-toggle) vervangt dat door de volledige commandoregel:
$ top -b -n 1 -c -w 200 -o +%MEM | sed -n '8p'
10681 pe7er 20 0 10.6g 3.8g 1.3g S 45.5 6.0 89:33.95 /home/pe7er/.local/share/JetBrains/Toolbox/apps/phpstorm/bin/phpstorm
Kernelthreads hebben geen commandoregel, dus die verschijnen tussen vierkante haken: [kthreadd], [kworker/0:0H]. Zo herken je in een oogopslag een kernelthread, en kernelthreads tonen altijd 0 bij VIRT, RES en SHR.
V zet de boomweergave aan, die de lijst ordent op ouderschap en de boom tekent:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
871 root 19 -1 84512 22256 21092 S 0.0 0.0 0:02.22 `- systemd-journal
987 root 20 0 31520 9372 5148 S 0.0 0.0 0:00.51 `- systemd-udevd
1743 systemd+ 20 0 17564 7528 6760 S 0.0 0.0 0:09.54 `- systemd-oomd
1749 systemd+ 20 0 21872 13264 11088 S 0.0 0.0 0:03.22 `- systemd-resolve
1977 avahi 20 0 11544 6396 3836 S 0.0 0.0 1:34.01 `- avahi-daemon
2108 avahi 20 0 8480 1440 1164 S 0.0 0.0 0:00.00 `- avahi-daemon
1985 root 20 0 13692 6328 6072 S 0.0 0.0 0:00.19 `- bluetoothd
Zo ontdek je dat het mysterieuze proces dat CPU opat door een cron-taak was afgesplitst en niet door de webserver. v klapt de kinderen van het bovenste proces in of uit, en elke sorteertoets verlaat de boomweergave, wat handig is om te weten voordat je je afvraagt waar je boom gebleven is.
5.6 De kolommen veranderen, en die verandering bewaren
f opent het veldbeheer. Met de pijltjestoetsen navigeer je, d of de spatiebalk zet een veld aan of uit, s maakt het het sorteerveld, en q gaat terug. Er zijn ruim zeventig velden beschikbaar, veel meer dan het tiental dat standaard getoond wordt:
$ top -O | tr '\n' ' '
PID PPID UID USER RUID RUSER SUID SUSER GID GROUP PGRP TTY TPGID SID PR NI nTH P
%CPU TIME TIME+ %MEM VIRT SWAP RES CODE DATA SHR nMaj nMin nDRT S COMMAND WCHAN
Flags CGROUPS SUPGIDS SUPGRPS TGID OOMa OOMs ENVIRON vMj vMn USED nsIPC nsMNT
nsNET nsPID nsUSER nsUTS LXC RSan RSfd RSlk RSsh CGNAME NU LOGID EXE RSS PSS
PSan PSfd PSsh USS ioR ioRop ioW ioWop AGID AGNI STARTED ELAPSED %CUU %CUC ...
Een handjevol daarvan is het waard om aan te zetten voor echt werk:
| Veld | Waarom je het zou toevoegen |
|---|---|
nTH |
Aantal threads. Verklaart een %CPU boven 100 zonder naar H te schakelen. |
PPID |
PID van de ouder. Wie heeft dit gestart? |
OOMs |
Out-of-memory-score, 0 tot 1000. Wie de kernel als eerste afbreekt. |
SWAP |
Hoeveel van dit proces naar swap is geduwd. |
ioR / ioW |
Gelezen en geschreven bytes. Het dichtst dat top bij schijfactiviteit per proces komt. |
%CUU |
CPU-gemiddelde over de levensduur, zoals ps het berekent. Handig naast %CPU (paragraaf 9.1). |
ELAPSED |
Hoe lang het proces al leeft. |
USED |
RES plus SWAP, en dat is de eerlijke geheugenvoetafdruk van een proces dat geswapt is. |
Alles wat je verandert blijft gelden tot je stopt, tenzij je op W drukt. Dat schrijft een configuratiebestand en meldt waar het terechtkwam:
W
Wrote configuration to '/home/pe7er/.config/procps/toprc'
De oude locatie was ~/.toprc, met een punt ervoor, en die wordt nog steeds gehonoreerd. De naam volgt eigenlijk de naam van het programma, dus een alias die t heet leest en schrijft procps/trc. Het bestand legt het sorteerveld vast, de zichtbare kolommen, de kleuren, de vertraging en de stand van elke schakelaar:
$ head -2 ~/.config/procps/toprc
top's Config File (Linux processes with windows)
Id:k, Mode_altscr=0, Mode_irixps=1, Delay_time=1.0, Curwin=0
Let op Delay_time=1.0 daar, bewaard uit een sessie die met -d 1 was gestart. Die ene regel heeft gevolgen die veel verder reiken dan het interactieve scherm, en dat is paragraaf 6.3.
6. Gevorderde toepassingen
6.1 Batchmodus
top is een schermvullend programma dat de terminal overneemt, dus je kunt hem niet zomaar door een pipe halen. Probeer het en hij weigert, met een foutmelding en een foutcode:
$ top -n 1 | head -3
top: failed tty get
$ top -n 1 > out.txt; echo $?
top: failed tty get
1
-b (kort voor batch) is de oplossing. Hij drukt gewone regels af, accepteert geen invoer, en stopt na het aantal beelden dat je met -n (kort voor iterations) opgeeft:
$ top -b -n 1 # one frame, then exit
$ top -b -n 5 -d 2 # five frames, two seconds apart
$ top -b -n 1 -o +%MEM # sorted by memory, no config needed
$ top -b -n 1 -u www-data # one user
$ top -b -n 1 >> /var/log/top.log # a crude sampler for cron
-o (kort voor sort-override) is degene om te onthouden, want die maakt een batchrun onafhankelijk van wat er in het configuratiebestand staat. Een + ervoor sorteert hoog naar laag, een - sorteert laag naar hoog, en -O somt de geldige veldnamen op.
Er zit nog een valkuil in de batchmodus, en het is een stille. Zonder terminal om op te meten valt top terug op 80 kolommen en kapt hij COMMAND af tot zoiets als kworker+. -w (kort voor width) lost dat op, tot 512:
$ top -b -n 1 -c | awk '{print length}' | sort -n | tail -1
80 # truncated
$ top -b -n 1 -c -w 512 | awk '{print length}' | sort -n | tail -1
511 # the whole command line
6.2 De tweebeeldsregel
Dit is het nuttigste onderdeel van het artikel voor iedereen die top in scripts gebruikt, en het staat niet in de handleiding.
Elk percentage in top is een verschil tussen twee metingen. Bij het eerste beeld is er nog geen vorige meting, dus top doet er een, wacht, en doet er nog een. Je kunt meten hoe lang hij wacht:
$ time top -b -n 1 -p 1 >/dev/null
real 0m0.20s
$ time top -b -n 2 -p 1 >/dev/null
real 0m3.20s # 0.20 + one 3.0s delay
$ time top -b -n 3 -p 1 >/dev/null
real 0m6.20s # 0.20 + two 3.0s delays
Een vijfde seconde, elke keer. Dus top -b -n 1 geeft je geen gemiddelde over drie seconden, en ook geen gemiddelde over de levensduur. Het geeft je een steekproef van 0,2 seconde, en de kernel boekt CPU-tijd alleen in tikken van 10 milliseconden (getconf CLK_TCK geeft 100), dus in een vijfde seconde passen ongeveer twintig telbare eenheden.
Dat is net zo onbetrouwbaar als het klinkt. Hier is een proces dat een tiende seconde CPU verbrandt en dan negen tiende slaapt, dus het werkelijke gebruik is ongeveer 14 procent. Twaalf opeenvolgende metingen van het eerste beeld:
$ for i in $(seq 12); do top -b -n 1 -p $PID | tail -1 | awk '{print $9}'; done
27.3 0.0 0.0 0.0 10.0 20.0 0.0 0.0 0.0 0.0 60.0 0.0
Meestal nul, en een keer 60 procent. Nu hetzelfde proces, gelezen uit het tweede beeld van een run met twee beelden en een seconde vertraging:
$ for i in $(seq 6); do top -b -n 2 -d 1 -p $PID | tail -1 | awk '{print $9}'; done
16.0 15.0 15.8 18.0 10.0 15.0
Stabiel, en juist. De regel volgt daar rechtstreeks uit:
Handel nooit op het eerste beeld. Draai voor alles wat gescript, bewaakt of gerapporteerd wordt
top -b -n 2 -d 1en lees het tweede beeld. Het eerste is een gok van 0,2 seconde.
# the second frame only, and from it the five heaviest processes:
$ top -b -n 2 -d 1 | awk '/^ *PID/{f++} f==2' | tail -n +2 | head -5
# anything over 50%, again from the second frame only:
$ top -b -n 2 -d 1 | awk '/^ *PID/{f++} f==2 && $9+0>50'
127695 pe7er 20 0 10308 3452 3196 R 100.0 0.0 0:02.22 bash
Geheugenvelden hebben hier geen last van. RES, VIRT en %MEM worden rechtstreeks uit /proc gelezen en kloppen al bij het eerste beeld. Alleen de verschillen, %CPU en de %Cpu(s)-regel, hebben twee metingen nodig.
6.3 Het configuratiebestand verandert de batchuitvoer
Paragraaf 5.6 noemde dat W de vertraging bewaart. Hier is waarom dat een echt operationeel gevaar is. Hetzelfde commando, op dezelfde machine, met en zonder bewaard configuratiebestand:
# a user with no configuration file of their own:
$ time top -b -n 2 -p 1 >/dev/null
real 0m3.21s # the built-in 3.0s delay
# the same command, after somebody once pressed W in a session started with -d 1:
$ time top -b -n 2 -p 1 >/dev/null
real 0m1.21s # Delay_time=1.0, read from the rcfile
De batchmodus leest het configuratiebestand. Vertraging, sorteerkolom, zichtbare velden, Irix-modus, filters, threadmodus: alles. Een bewakingsscript dat een jaar lang werkte kan dus andere getallen gaan geven omdat iemand inlogde, op I en W drukte en weer uitlogde. Er veranderde niets aan het script.
Twee gewoontes halen dat risico weg. Zet alles waar het script van afhangt expliciet op de commandoregel:
$ top -b -n 2 -d 1 -o +%CPU -w 512
Of haal het configuratiebestand helemaal uit beeld door het ergens leegs aan te wijzen:
$ XDG_CONFIG_HOME=/nonexistent top -b -n 2 -d 1
Dezelfde redenering geldt voor /etc/topdefaultrc, dat standaardwaarden levert aan elke gebruiker die geen eigen bestand heeft.
6.4 Afbreken, renicen en PR lezen
k vraagt om een PID en daarna om een signaal, standaard SIGTERM. De PID staat standaard op het eerste proces op het scherm, en dat is de grootste CPU-verbruiker, dus twee keer k en Enter is een snelle manier om het verkeerde proces af te breken. Typ de PID.
r renicet. Positieve waarden maken een proces beleefder, negatieve hebberiger, en alleen root mag negatief gaan. Je hebt top daar niet voor nodig, en buiten een interactieve sessie moet je hem er niet voor gebruiken:
$ nice -n 10 ./nightly-import.sh # start something at a lower priority
$ renice 10 -p 1234 # lower the priority of a running process
$ sudo renice -5 -p 1234 # raise it; negative needs root
$ renice 10 -u www-data # every process owned by a user
Een ding om helder te hebben voordat je ernaar grijpt: een nice-waarde is een gewicht, geen plafond. Een proces dat op 19 geniced is gebruikt nog steeds 100 procent van een kern als niets anders het wil. Nicen bepaalt alleen wie er wint als twee draaibare taken concurreren, dus het weerhoudt een backup er niet van een verder inactieve machine te verzadigen. Als je een echt plafond nodig hebt, dan is dat een CPU-quotum van een cgroup, wat een container krijgt en wat systemd aanbiedt als CPUQuota= op een servicebestand.
Dat brengt ons bij de twee prioriteitskolommen:
PR the kernel's scheduling priority
NI the nice value you can set, from -20 to 19
Voor gewone processen ligt de verhouding vast op PR = NI + 20, en je kunt zien dat dat over een hele machine standhoudt:
$ top -b -n 1 -w 200 | awk 'NR>=8 {print $3, $4}' | sort -u -k2 -n
0 -20 # PR NI
9 -11
10 -10
19 -1
20 0 # the normal case
21 1
25 5
39 19
Realtimeprocessen doorbreken het patroon, en de twee vormen verwarren mensen:
rt real-time priority 99, the highest there is (kernel migration threads)
-51 real-time priority 50, shown as -1 minus the rt priority
-2 real-time priority 1
Een negatieve PR is dus geen heel beleefd proces, het is een realtimeproces, en het dringt voor bij alles wat van jou is. Op een normale server is rt of een negatieve PR naast een applicatieproces een tweede blik waard.
6.5 Beveiligde modus
Op een gedeelde machine of een machine die aan het internet hangt wil je misschien niet dat elke ingelogde gebruiker processen kan afbreken vanuit een schermvullend programma. Als je /etc/toprc aanmaakt met precies twee regels, raken gewone gebruikers k, r en de bediening van de vertraging kwijt:
$ sudo tee /etc/toprc <<'EOF'
s
5.0
EOF
# line 1: s = secure mode on
# line 2: the forced delay interval, in seconds
Root merkt er niets van, tenzij je ook -s meegeeft (kort voor secure-mode), wat de beperkingen zelfs voor root afdwingt. Om te weten of een draaiende top beveiligd is, druk je op h en lees je de tweede regel van het hulpscherm: daar staat Secure mode Off of On.
Verwar dit niet met beveiliging. Het haalt gemak weg, geen mogelijkheden; wie top kan draaien kan nog steeds kill draaien. Het echte nut is het voorkomen van ongelukken op een machine waar meerdere mensen werken.
6.6 Vier vensters tegelijk
Bijna niemand weet dat dit bestaat. top houdt vier onafhankelijke veldgroepen bij, elk met eigen kolommen, sorteervolgorde, filters en kleuren. A toont ze alle vier tegelijk:
A switch between one window and all four
a / w move to the next / previous window
g jump to window 1 to 4 by number
- hide or show the current window's task list
= reset the current window
G rename the current window
De voor de hand liggende indeling is een CPU-weergave, een geheugenweergave, een weergave gefilterd op een gebruiker, en een gesorteerd op TIME+. Stel ze een keer in, druk op W, en ze komen elke keer terug. Het is het dichtst dat top bij een dashboard komt, en het kost niets extra om te draaien.
Twee kleinere gemakken uit dezelfde familie. Lange uitvoer kun je scrollen met de pijltjestoetsen, PgUp, PgDn, Home en End, en C zet een coördinatenmelding aan zodat je weet waar je bent. En L zoekt een tekst ergens in een procesregel, met & voor de volgende treffer, wat prettiger is dan vijfhonderd regels afturen op zoek naar een PID.
7. Iets wat de meeste gebruikers niet weten
7.1 In een container rapporteert top de host
Deze kost echte tijd en echt geld, want hij zorgt ervoor dat mensen containers dimensioneren op een getal dat nooit over hun container ging.
Dit is top die draait in een container met een harde limiet van 256 MiB geheugen en een CPU:
$ docker run --rm -m 256m --cpus 1 mariadb:10.6.18 sh -c 'top -b -n 1 | head -5'
top - 15:28:56 up 6:18, 0 users, load average: 2.52, 1.67, 1.43
Tasks: 3 total, 1 running, 2 sleeping, 0 stopped, 0 zombie
%Cpu(s): 25.4 us, 3.0 sy, 0.0 ni, 71.6 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 64026.3 total, 34062.5 free, 12709.0 used, 17254.8 buff/cache
MiB Swap: 8192.0 total, 8192.0 free, 0.0 used. 49266.0 avail Mem
62,5 GiB in totaal. De container mag 256 MiB gebruiken. En die limiet geldt echt:
$ docker run --rm -m 256m --cpus 1 mariadb:10.6.18 sh -c \
'cat /sys/fs/cgroup/memory.max; cat /sys/fs/cgroup/cpu.max; nproc'
268435456 # 256 MiB, exactly as requested
100000 100000 # one CPU's worth of quota
16 # but the machine still looks like 16 cores
Kijk wel naar de regel Tasks: 3 total, want die laat zien wat er aan de hand is. De PID-namespace wordt gerespecteerd, dus top ziet alleen de drie processen in de container. De cgroups voor geheugen en CPU niet, want top leest /proc/meminfo en /proc/stat, en dat zijn de bestanden van de host, rechtstreeks doorgekoppeld.
In een container vertelt top dus de waarheid over processen en liegt hij over capaciteit. De load average is die van de host. De CPU-percentages zijn aandelen van alle zestien kernen van de host. Het geheugentotaal is het RAM van de host, wat betekent dat %MEM een breuk met de verkeerde noemer is: een proces op 90 procent van zijn containerlimiet verschijnt als 0,4 procent.
Als je de eigen getallen van de container nodig hebt, lees dan de cgroup-bestanden of vraag het de runtime:
$ cat /sys/fs/cgroup/memory.current /sys/fs/cgroup/memory.max
$ cat /sys/fs/cgroup/cpu.stat
$ docker stats --no-stream # from the host: real per-container figures
Die PID-namespace heeft nog een tweede gevolg, en dat bijt op het moment dat je iets wilt doen met wat je zag. Een PID betekent alleen iets binnen de namespace waarin je hem las. Hetzelfde proces heeft een nummer in de container en een ander nummer op de host:
$ C=$(docker run -d --rm -m 256m mariadb:10.6.18 sleep 120)
# inside the container:
$ docker exec $C ps -eo pid,comm
PID COMMAND
1 sleep
# on the host, the very same process:
$ docker inspect -f '{{.State.Pid}}' $C
130140
Een PID die je uit de top van een container overneemt is dus betekenisloos in de top van de host, en hoort daar heel goed bij iets heel anders. Erger nog: een kill 1 die de applicatie van een container zou stoppen, is op de host gericht op systemd. Bepaal aan welke kant je staat voordat je handelt, en gebruik docker inspect of docker top om tussen de twee te vertalen.
Dezelfde waarschuwing geldt voor free, uptime en nproc. Het is geen fout in top; het is wat er gebeurt als een programma dat voor hele machines is geschreven in een plakje van een machine draait.
7.2 Je kunt RES niet optellen
RES is het eerlijke geheugengetal per proces, en de voor de hand liggende volgende stap is de kolom optellen om te zien waar het RAM heen ging. Dat werkt niet, en de fout is groot.
$ ps -eo rss= | awk '{s+=$1} END {printf "sum of RES over all processes: %.1f MiB\n", s/1024}'
sum of RES over all processes: 23660.6 MiB
$ top -b -n 1 | sed -n 4p
MiB Mem : 64026.3 total, 33770.9 free, 15078.0 used, 17595.8 buff/cache
23,6 GiB aan RES tegenover 15,0 GiB die werkelijk in gebruik is. De som is ruim anderhalf keer te groot, en op een machine die veel kopieën van hetzelfde programma draait is het gat veel erger.
De oorzaak is delen. Gedeelde bibliotheken, het programma-image zelf, copy-on-write-pagina's die van een fork zijn geerfd, gedeelde geheugensegmenten: elke pagina wordt volledig geteld bij elk proces dat hem koppelt. Twintig PHP-FPM-workers die van een ouderproces zijn afgesplitst rapporteren allemaal het geheugen van de ouder als het hunne. De kolom SHR vertelt je ruwweg hoeveel van RES het risico loopt dubbel geteld te worden, en daarom staat SHR er direct naast.
Als je een getal nodig hebt dat wel optelt, vraag dan om de proportional set size, waarbij elke gedeelde pagina wordt gedeeld door het aantal processen dat hem deelt:
PSS proportional set size: shared pages divided by the number of sharers
USS unique set size: the pages this process alone holds
RSS a more precise resident figure, from smaps_rollup
Alle drie zijn in top beschikbaar via f, en de handleiding en de werkelijkheid zijn het eens over de prijs: "Accessing smaps values is 10x more costly than other memory statistics", en om ze voor de processen van een andere gebruiker te lezen heb je root nodig. Zet ze aan als je geheugen onderzoekt, en zet ze daarna weer uit.
De praktische regel: gebruik RES om processen met elkaar te vergelijken, en de samenvattingsregel om te vragen hoeveel geheugen de machine gebruikt. Leid nooit het een uit het ander af.
7.3 VIRT betekent bijna niets
Het breedste getal op het scherm is het getal waar je je het minst druk om hoeft te maken:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
106453 pe7er 20 0 1448.4g 251576 157480 S 16.7 0.4 16:48.36 chrome
1448,4 gibibyte virtueel geheugen, op een machine met 62,5 GiB RAM en 8 GiB swap. Dat is ongeveer twintig keer meer geheugen dan er ergens op die machine bestaat, en er is niets mis.
VIRT is adresruimte, geen geheugen. Het telt alles wat het proces heeft gekoppeld: code, gedeelde bibliotheken, bestandskoppelingen, geheugen dat is aangevraagd maar nooit aangeraakt, en grote reserveringen die runtimes uit principe doen. Java, Go, Chrome, PostgreSQL en alles wat een grote mmap-arena gebruikt reserveren routinematig honderden gigabytes aan adressen die ze nooit zullen gebruiken. Een adres reserveren kost niets; alleen een pagina aanraken kost geheugen, en aangeraakte pagina's verschijnen in RES.
Er is een geval waarin VIRT een blik waard is: als hij gestaag en eindeloos oploopt terwijl RES vlak blijft, lekt er iets koppelingen in plaats van geheugen. Negeer de kolom verder, of druk op f en haal hem weg zodat hij je oog niet meer trekt.
7.4 Waar de getallen werkelijk vandaan komen
top berekent bijna niets over je machine. Hij opent bestanden, leest ze, trekt de ene meting van de vorige af, en tekent het resultaat. Elk getal in dit artikel staat in een bestand dat je zelf kunt lezen.
De vijf samenvattingsregels komen uit drie bestanden, een keer per verversing gelezen:
/proc/loadavg the load average line
/proc/meminfo both memory lines
/proc/stat the %Cpu(s) line and the task state counts
/proc/uptime the "up 6:11" part of the first line
Elke procesregel komt uit nog twee bestanden, een keer per proces per verversing gelezen:
/proc/PID/stat PID, state, PR, NI, utime, stime, last used CPU <- most columns
/proc/PID/statm VIRT, RES, SHR
/proc/PID/cmdline the COMMAND column, but only when `c' is on
/etc/passwd turning the numeric UID into the USER column
Je hoeft dat niet op mijn woord aan te nemen. strace laat je elk bestand zien dat top voor een enkel beeld opent:
$ strace -f -e trace=openat -o tr.txt top -b -n 1 >/dev/null
$ grep -c openat tr.txt
2231
$ grep -oE '"/proc/[0-9]+/[a-z]+"' tr.txt | sed 's|/proc/[0-9]*/|/proc/PID/|' \
| sort | uniq -c
1042 "/proc/PID/statm"
1042 "/proc/PID/stat"
Maak nu het rekensommetje, want het bevestigt bij toeval iets uit paragraaf 6.2. Er draaiden op dat moment 521 processen op de machine. 521 maal twee is 1042. top -b -n 1 las elk proces twee keer, en dat zijn de aanloopmeting en het beeld zelf, en daarom levert een batchrun met een enkel beeld toch een %CPU-kolom op. De 0,2 seconde uit paragraaf 6.2 is het gat tussen die twee rondes.
Zet commandoregels aan en er verschijnt een derde bestand per proces, precies even vaak geopend als de andere twee. De aantallen zijn 1044 in plaats van 1042 alleen omdat er tussen de twee runs een proces startte:
$ strace -f -e trace=openat -o tr2.txt top -b -n 1 -c >/dev/null
1044 "/proc/PID/statm"
1044 "/proc/PID/stat"
1044 "/proc/PID/cmdline" <- new, and only because of -c
Geen van die bestanden bestaat op een schijf. Ze zijn nul bytes groot, en de kernel maakt het antwoord op het moment dat je het leest:
$ ls -l /proc/meminfo /proc/stat /proc/1/stat
0 /proc/1/stat
0 /proc/meminfo
0 /proc/stat
Daar volgen drie nuttige dingen uit.
Je kunt top volledig overslaan. Voor een script is het bestand lezen beter dan een scherm ontleden: geen steekproef van 0,2 seconde, geen configuratiebestand, geen afkapping op 80 kolommen, geen failed tty get, en niets dat verandert als een collega op W drukt.
$ cut -d' ' -f1-3 /proc/loadavg # the load average, three fields
1.63 1.39 1.29
$ awk '/^MemAvailable/ {print $2/1024 " MiB"}' /proc/meminfo
49164.1 MiB
$ awk '{print $1, $2}' /proc/1234/statm # VIRT and RES, in pages
Alles wat dezelfde bestanden leest rapporteert dezelfde getallen. htop, btop, free, uptime, ps en de meeste monitoringagents zijn allemaal lezers van /proc. Daarom verandert het inruilen van top voor een mooier programma de presentatie en nooit de betekenis, en dat is paragraaf 7.8.
Wiens /proc je leest bepaalt wat je te horen krijgt. In een container horen de procesmappen bij de PID-namespace van de container, terwijl meminfo en stat rechtstreeks van de host komen. Die ene zin is de hele paragraaf 7.1.
7.5 top verandert wat het meet
top verschijnt in zijn eigen uitvoer, en niet onderaan:
120269 pe7er 20 0 14868 5584 3536 R 8.3 0.0 0:00.02 top
Paragraaf 7.4 telde de kosten: 2231 bestandsopeningen voor een enkel beeld, twee per proces plus de samenvattingsbestanden. Dat is niet gratis, en het is meetbaar:
$ ls /proc | grep -c '^[0-9]'
516 # processes to walk
$ time top -b -n 1 >/dev/null
real 0m0.23s user 0m0.00s sys 0m0.02s
Onschadelijk bij de standaard drie seconden. Niet onschadelijk bij -d 0.1 op een drukke productieserver met duizenden processen, waar top een van de zwaardere dingen kan worden die er draaien. De handleiding is ook eerlijk over de bijwerkingen en merkt bij het veld P (laatst gebruikte CPU) op: "the very act of running top may break this weak affinity and cause more processes to change CPUs more often".
Twee gewoontes volgen daaruit. Laat het interval met rust tenzij je een reden hebt, en als je top een week laat draaien in een vergeten SSH-sessie, besef dan dat hij 1.200 keer per uur is wakker geworden, elk uur, om elk proces op de machine langs te lopen.
7.6 De kernelthreads verbergen
Op deze machine meldt top ruim vijfhonderd taken, en de meeste zijn geen processen in enige zin die er voor jou toe doet. Een omgevingsvariabele haalt elke kernelthread weg uit zowel de lijst als de tellingen:
$ top -b -n 1 | sed -n 2p
Tasks: 518 total, 1 running, 516 sleeping, 0 stopped, 1 zombie
$ LIBPROC_HIDE_KERNEL=1 top -b -n 1 | sed -n 2p
Tasks: 262 total, 1 running, 260 sleeping, 0 stopped, 1 zombie
518 wordt 262. De helft van alles wat top je liet zien was kworker, ksoftirqd, rcu_gp en consorten. De waarde die je instelt maakt niet uit, alleen dat de variabele bestaat, en het werkt voor de hele procps-familie, dus ps gehoorzaamt hem ook.
Zet hem in je shellprofiel op een server die je vaak beheert. Het maakt top merkbaar leesbaarder, en de kernelthreads die je echt moet zien, een kworker op 100 procent of een vastgelopen jbd2, zijn precies degene die je bewust gaat opzoeken.
7.7 Weten waar top ophoudt
top beantwoordt een vraag goed: wat gebeurt er nu op deze machine. Bijna elke frustratie ermee komt voort uit het stellen van een andere vraag.
| Wanneer je nodig hebt | Grijp naar |
|---|---|
| Wat de machine een uur geleden deed | atop, die momentopnamen naar schijf schrijft, of sar uit sysstat |
| Een vriendelijkere interactieve weergave | htop, of btop voor grafieken. Geen van beide is standaard geïnstalleerd (paragraaf 7.8). |
| Schijf-I/O per proces | pidstat -d 1, of iotop |
| Of de schijf het knelpunt is | iostat -x 2, en /proc/pressure/io |
| Snelheden: contextwisselingen, swap in en uit, paginafouten | vmstat 1 |
| Een stabiele lijst die je kunt scripten | ps, die daar precies voor is gemaakt |
| De echte getallen van een container | docker stats, of de cgroup-bestanden (paragraaf 7.1) |
| Welke service of container wat gebruikt | systemd-cgtop: top voor cgroups in plaats van processen |
| Wat een proces nu werkelijk doet | strace -p, perf top, of cat /proc/PID/stack |
| Om om drie uur 's nachts gewaarschuwd te worden | Een monitoringsysteem. top heeft geen geschiedenis en geen waarschuwingen. |
atop verdient de bovenste regel. Als service geïnstalleerd bemonstert hij op een timer en schrijft hij elke meting naar een log, zodat je terug kunt kijken naar de tien minuten voordat de machine omviel. Dat is het ene ding dat top principieel niet kan, en het is precies wat je het liefst wilt als iemand een probleem meldt dat al voorbij is. Hij moet wel voor het incident geïnstalleerd zijn, en daar zit de hele moeilijkheid.
En voordat je een tool toevoegt: kijk of top al een smaller familielid heeft dat je vraag in een regel beantwoordt. uptime voor de load average, free -h voor de geheugensamenvatting, pgrep en pidof om een PID te vinden, w voor wie er ingelogd is. Ze komen allemaal uit hetzelfde procps-pakket en lezen dezelfde bestanden.
7.8 htop en btop, en waarom top nog steeds de moeite waard is
Vraag iemand naar top en binnen een zin krijg je te horen dat je beter htop kunt gebruiken. Dat advies klopt grotendeels. Het is ook, op een server die je net hebt gekregen, vaak nutteloos, en de reden is een regel pakketinformatie:
$ dpkg-query -W -f='${Package} ${Priority}\n' procps htop btop
btop optional
htop optional
procps required
procps, waar top in zit, is een required-pakket. Het zit in elke Debian- en Ubuntu-installatie, ook in de minimale. htop en btop zijn optional: iemand moet ze installeren, en atop stond op deze machine helemaal niet.
Dat onderscheid doet er juist toe op de momenten dat het er het meest toe doet. Een net opgeleverde VPS, een rescue-shell, een minimale containerimage, de server van een klant die je voor het eerst bekijkt, een machine waarvan het uitgaande netwerk precies datgene is dat stuk is. In al die gevallen is top wat je hebt, en is apt install htop geen optie. Leer top omdat hij er altijd is; gebruik htop als iemand zo vriendelijk is geweest hem te installeren.
Als hij er wel is, dan doet hij dit werkelijk beter:
| htop doet | Waar top je ervoor laat werken |
|---|---|
| Toont standaard een meter per CPU-kern | Je moet op 1 drukken, en de meeste mensen leren dat nooit (paragraaf 5.1). |
| Scrollt omhoog, omlaag, links en rechts | Scrollen bestaat wel, maar wordt zelden ontdekt, en kolommen vallen van de rand. |
Selecteer een regel, druk op F9, breek hem af |
k en dan de PID typen, met het drukste proces als gevaarlijke standaard (paragraaf 9.3). |
F5 boom, F3 zoeken, F4 filteren terwijl je typt |
V, L en o, waarvoor je eerst de handleiding nodig hebt. |
F2 opent een instelscherm voor meters, kolommen en kleuren |
f, Z en W, verspreid over drie aparte schermen. |
| Telt processen, threads en kernelthreads apart | Een getal dat die drie stilletjes door elkaar haalt (paragraaf 7.6). |
Die laatste regel is een echte verbetering, en je ziet hem door beide op hetzelfde moment te draaien:
top Tasks: 521 total, 2 running, 519 sleeping, 0 stopped, 0 zombie
htop Tasks: 265, 2390 thr, 261 kthr; 3 running
Een getal tegenover drie. top zegt 521 taken; htop zegt 265 echte processen, 2390 threads daarbinnen, en 261 kernelthreads apart geteld. De tweede regel is simpelweg informatiever.
btop is weer iets anders: grafieken over tijd, muisondersteuning, en een uiterlijk dat een moderne terminal nodig heeft om goed te tonen. Het is prettig, en het is een kijkvenster in plaats van een tool waar je iets op bouwt.
Twee dingen aan htop verrassen mensen die van top komen, en het is goed om ze te weten voordat je ze op een draaiende machine typt:
$ top -d 5 # 5 SECONDS between refreshes
$ htop -d 5 # 0.5 seconds. htop counts in TENTHS of a second.
De handleiding is er duidelijk over dat htop die waarde afkapt tussen 1 en 100 tienden, dus zijn traagste verversing is tien seconden en zijn snelste een tiende. En het tweede verschil doet ertoe als je hem hoopte te scripten:
$ top -n 1 | head -3
top: failed tty get # refuses cleanly, exit code 1
$ htop -n 1 | head -3
[?1049h[22;0;0t[1;24r(B[m[4l[?7h[H[2J ...
# raw escape codes into your pipe, exit code 0
htop heeft geen batchmodus. Hij heeft wel -n om het aantal beelden te beperken, maar die beelden zijn nog steeds schermvullende tekeninstructies, dus een script dat ze opvangt krijgt een scherm vol terminalstuurcodes en geen foutmelding die dat vertelt. Voor alles wat onbeheerd draait blijven top -b en ps over, en dat is de conclusie van paragraaf 9.1 die uit een andere richting komt aanlopen.
Nu het deel dat belangrijker is dan al het bovenstaande. htop en btop lezen dezelfde /proc-bestanden die top leest. Het zijn mooiere vensters op identieke getallen, dus elke misvatting in dit artikel overleeft de overstap ongeschonden. De containerval uit paragraaf 7.1 is het duidelijkste bewijs, met htop geïnstalleerd in dezelfde container van 256 MiB:
$ docker run --rm -t -m 256m --cpus 1 ubuntu:24.04 \
sh -c 'apt-get install -y htop >/dev/null; cat /sys/fs/cgroup/memory.max; htop'
268435456 # the container's real limit: 256 MiB
15.1G/62.5G # what htop's memory meter shows
Load average: 1.40 1.27 1.29 # and the host's load average
62,5 GiB, het RAM van de host, in een container die 256 MiB mag gebruiken. Precies wat top deed, want het is precies hetzelfde bestand dat gelezen wordt. Een mooiere meter zorgt er niet voor dat het getal iets anders betekent.
De eerlijke samenvatting is dus deze. htop is het betere programma om voor te zitten, en het is de moeite waard om op elke machine die van jou is te installeren. Het is geen vervanging voor het weten wat de load average telt, wat RES dubbel telt, of van wie het geheugen is waar je naar kijkt, en dat zijn de dingen die mensen werkelijk een middag kosten.
8. Best practices
- Deel de load average door het aantal kernen. Kijk daarna of de CPU-regel werkelijk druk is. Hoge load met een hoge
idbetekent opslag, geen processoren. - Onthoud dat de load average toestand
Dmeetelt. Processen die vastzitten op een schijf of een dode NFS-koppeling laten hem net zo hard stijgen als processen die rekenen. - Lees alle drie de loadcijfers als een trend. Het getal van een minuut kan op zichzelf een piek niet van een herstel onderscheiden.
- Lees
avail Mem, nooitfree. Een lagefreemet een grotebuff/cacheis een gezonde server, geen zieke. - Druk vroeg op
1. Een verzadigde kern van de zestien is onzichtbaar in de gemiddelde regel en duidelijk in de regels per kern. - Druk op
iom de inactieve taken te verbergen. Het grootste deel van de processenlijst van een server slaapt en blijft slapen. Als je dat verbergt hou je alleen over wat sinds de vorige verversing bewogen heeft. - Controleer
taskset -cp PIDvoordat je threading de schuld geeft. Een vastgezet proces en een proces met een thread zien er op het scherm identiek uit, en maar een van de twee los je op door de code te veranderen. - Onthoud dat een nice-waarde een gewicht is en geen plafond. Een proces dat op 19 geniced is pakt nog steeds een hele kern als niets anders het wil. Voor een echt plafond heb je een cgroup-quotum nodig.
- Druk op
Mals de vraag over geheugen gaat. De standaardsortering op CPU beantwoordt een andere vraag, en de helft van de geheugenonderzoeken begint met kijken naar de verkeerde lijst. - Gebruik
com de volledige commandoregel te zien. Twintig regels diephp-fpmzeggen vertellen je niets; twintig commandoregels vertellen je welke site. - Handel nooit op het eerste beeld.
top -b -n 1is een steekproef van 0,2 seconde. Gebruiktop -b -n 2 -d 1en lees het tweede beeld. - Lees in een script
/procin plaats vantopte ontleden./proc/loadavg,/proc/meminfoen/proc/PID/statzijn toch al waar de getallen vandaan komen, en geen van de valkuilen uit sectie 6 geldt daarvoor. - Grijp naar
/proc/pressure/als de bezetting er prima uitziet maar de server niet. PSI meet hoe lang taken vertraagd werden, en anders danwawordt het niet verdund door het aantal kernen. - Neem een PID nooit mee over een containergrens. PID 1 binnen is buiten een of ander vijfcijferig getal, en
kill 1op de host is gericht opsystemd. - Schrijf elke optie voluit in een script. De batchmodus leest het configuratiebestand, dus
-d,-oen-whoren op de commandoregel, waar niemands bewaarde schakelaars ze kunnen veranderen. - Zet
-w 512bij elke batchrun die je gaat ontleden. Zonder terminal gaattopuit van 80 kolommen en kapt hijCOMMANDaf. - Tel
RESniet op. Gedeelde pagina's worden geteld bij elk proces dat ze koppelt. Gebruik het om processen te vergelijken, en de samenvattingsregel voor totalen van de machine. - Negeer
VIRT. Het is adresruimte. Een getal dat twintig keer groter is dan je RAM is normaal voor Java, Go en Chrome. - Weet in welke modus je zit voordat je een
%CPUnoemt. 400 procent in de Irix-modus en 25 procent in de Solaris-modus zijn dezelfde meting. - Grijp naar
Hals een proces boven de 100 procent zit. Dat splitst het proces in threads en laat zien of er een thread of allemaal bezig zijn. - Onthoud dat de samenvatting nooit gefilterd wordt. Alleen
-pverandert de telling inTasks:.-u,-Uenodoen dat niet. - Zet
LIBPROC_HIDE_KERNEL=1op servers die je vaak gebruikt. Het halveerde hier een lijst van 518 taken naar 262, en de kernelthreads die het najagen waard zijn, zoek je toch bewust op. - Dimensioneer een container niet op basis van
topbinnenin. Die meldt het geheugen en de CPU's van de host. Gebruikdocker statsof de cgroup-bestanden. - Installeer
htop, en leertoptoch.htopis het prettigere programma, maarprocpsis een required-pakket enhtopeen optioneel pakket. Op een rescue-shell of een verse VPS istopwat je krijgt. - Verwacht niet dat
htopeen misvatting oplost. Hij leest dezelfde/proc-bestanden, dus hij meldt in een container precies zoalstophet RAM van de host. - Installeer iets dat geschiedenis bewaart.
toptoont altijd alleen het heden, en het incident waar je naar gevraagd wordt ligt altijd in het verleden. - Druk op
Wzodra je een indeling hebt die je bevalt, en onthoud dat je dat deed, want het verandert wat je scripts te zien krijgen. - Lees de handleiding minstens een keer goed. Het is een van de meest gedetailleerde handleidingen op het systeem, en het meeste wat mensen een eigenaardigheid van
topnoemen staat erin beschreven.
$ man 1 top # long, thorough, and worth an evening
$ top -h # the full option list, one screen
$ top -V # check the version before trusting a flag
$ top -O # every available column name
h # inside top: help, then h again for page two
$ man 1 ps # the scriptable counterpart
$ man proc_loadavg # the definition that settles the load argument
Naar boven9. Veelgemaakte fouten
9.1 top versus ps, en de %CPU die het er niet mee eens is
Beide programma's drukken een kolom af die %CPU heet. Ze betekenen totaal verschillende dingen, en het verschil is niet klein. Dit proces verbrandde zeven seconden CPU en ging daarna slapen:
$ top -b -n 1 -p 120358 | tail -1
120358 pe7er 20 0 8656 2068 2068 S 0.0 0.0 0:07.04 sleep
$ ps -o pid,%cpu,time,etime,comm -p 120358
PID %CPU TIME ELAPSED COMMAND
120358 67.4 00:00:07 00:10 sleep
# and it is not the first-frame problem from section 6.2 either,
# the second frame agrees:
$ top -b -n 2 -d 1 -p 120358 | tail -1
120358 pe7er 20 0 8656 2068 2068 S 0.0 0.0 0:07.04 sleep
Nul, en 67,4, voor hetzelfde proces op hetzelfde moment. Geen van beide is fout:
topmeldt CPU die sinds de vorige verversing is gebruikt. Het proces slaapt nu, dus 0,0 klopt.psmeldt de totale CPU gedeeld door de totale levensduur. Zeven seconden CPU in tien seconden leven is 67,4 procent, gemiddeld over het hele bestaan.
Daarom kan ps aux een webserverproces op 40 procent tonen dat helemaal niets doet: het was een uur geleden druk bij het opstarten en het gemiddelde herstelt nooit meer. Als je het cijfer van ps binnen top wilt, is het veld %CUU precies dat, en met beide kolommen naast elkaar beantwoord je in een oogopslag de vraag "is dit proces nu druk, of is het altijd druk geweest?".
| Vraag | top | ps |
|---|---|---|
Wat betekent %CPU |
Sinds de vorige verversing | Gemiddelde over de levensduur van het proces |
| Klopt bij een enkele meting | Nee, het eerste beeld is een steekproef van 0,2 s | Ja, het is een eenmalige tool |
| Veilig te ontleden in een script | Alleen met -b -n 2 -d 1 -w 512 |
Ja, daar is hij voor |
| Beïnvloed door een configuratiebestand | Ja, zelfs in de batchmodus | Nee |
| Interactief | Ja, dat is het hele punt | Nee |
| Samenvatting van de machine | Ja, vijf regels ervan | Nee |
De heldere scheiding: top is om te kijken, ps is om te scripten. Als een script een processenlijst nodig heeft, is ps -eo pid,pcpu,rss,comm --sort=-pcpu korter, sneller, stabieler en vrij van elke valkuil uit sectie 6.
9.2 Mythe versus werkelijkheid
| Mythe | Werkelijkheid |
|---|---|
| "Load average 8 betekent dat de CPU 800 procent bezig is." | Het is een telling van taken die willen draaien, geen percentage. Deel het door het aantal kernen, en onthoud dat taken die op de schijf wachten meetellen. |
| "Hoge load betekent altijd een CPU-probleem." | Linux telt toestand D net zo goed als toestand R. Een falende schijf of een dode NFS-koppeling levert een hoge load op met inactieve CPU's. |
"free is bijna nul, dus het geheugen van de server is op." |
buff/cache wordt op verzoek teruggegeven. Lees avail Mem, oftewel MemAvailable uit /proc/meminfo. |
"Een %CPU boven 100 is een fout." |
De Irix-modus telt 100 procent per kern. Vier bezige threads is 400 procent. I schakelt naar de Solaris-modus en deelt door het aantal kernen. |
| "De CPU-regel toont een processor." | Het is het gemiddelde van allemaal. Een kern op 98 procent van de zestien verschijnt als 6 procent. Druk op 1. |
"wa vertelt me dat de schijf bezig is." |
wa is inactieve tijd terwijl iets op I/O wacht, dus het wordt begrensd door het aantal vrije kernen. Gebruik iostat -x of /proc/pressure/io. |
"Als je RES optelt zie je waar het geheugen heen ging." |
Hier gemeten: 23,6 GiB aan RES tegenover 15,0 GiB in gebruik. Gedeelde pagina's worden geteld bij elk proces dat ze koppelt. |
"VIRT is hoeveel geheugen het proces nodig heeft." |
Het is adresruimte. Chrome toont 1448.4g op een machine met 62,5 GiB RAM, en er is niets mis. |
"top -b -n 1 geeft me het CPU-gebruik." |
Het geeft je een steekproef van 0,2 seconde. Gemeten op een proces van 14 procent liepen twaalf metingen van 0,0 tot 60,0. Gebruik -n 2 -d 1. |
| "De batchmodus negeert mijn instellingen." | Hij leest hetzelfde rcfile. Een bewaarde vertraging veranderde top -b -n 2 van 3,21 s naar 1,21 s. Zet elke optie op de commandoregel. |
"top en ps spreken elkaar tegen, dus een van beide is kapot." |
top meet het laatste interval, ps middelt over de levensduur van het proces. Allebei hadden ze gelijk toen ze 0,0 en 67,4 afdrukten voor hetzelfde proces (paragraaf 9.1). |
"TIME+ is hoe lang het proces al draait." |
Het is verbruikte CPU-tijd, geen kloktijd. Een proces dat een week leeft met een TIME+ van 0:02 heeft bijna niets gedaan. ELAPSED is het veld met de kloktijd. |
| "Filteren op gebruiker versmalt het hele scherm." | Alleen de processenlijst. De Tasks:-regel telde nog steeds 515 terwijl er 305 in de lijst stonden. Alleen -p versmalt ook de samenvatting. |
"top in mijn container toont mijn container." |
Processen wel, capaciteit niet. In een container van 256 MiB meldde hij 62,5 GiB totaal en 16 CPU's, want /proc/meminfo is dat van de host. |
"Toestand R betekent dat het proces de CPU gebruikt." |
Het betekent draaiend of klaar om te draaien. Op een machine met een wachtrij wachten de meeste R-processen op hun beurt. |
"Een negatieve PR is een heel beleefd proces." |
Het is een realtimeproces. PR is -1 min de realtimeprioriteit, dus -51 is rt-prioriteit 50, en rt is 99. |
"kill -9 ruimt het wel op." |
Niet bij een proces in toestand D. Dat zit in een kernelaanroep die nog niet is teruggekeerd, en er wordt geen signaal bezorgd tot dat gebeurt. |
"top is standaard, dus de opties werken overal." |
top zit niet in POSIX. Linux, BSD en BusyBox leveren drie ongerelateerde implementaties met verschillende opties en verschillende uitvoer. |
9.3 Andere valkuilen om te vermijden
- Op
ken dan opEnterdrukken. De PID staat standaard op de eerste regel, en dat is het drukste proces op de machine. Dat is zelden het proces dat je bedoelde. -d 0.1laten draaien op een productieserver. Elke verversing leest meerdere bestanden voor elk proces. Op 0,1 seconde wordttopop een machine met duizenden processen onderdeel van de belasting die hij rapporteert.- De uitvoer van
topop kolomnummer ontleden. De kolommen hangen af van het rcfile, de schakelaarsHencen de breedte van de terminal. Gebruik in plaats daarvanps -omet benoemde velden. %MEMvertrouwen binnen een container. De noemer is het RAM van de host, dus een proces op zijn containerlimiet kan 0,4 procent aangeven.- Achter een
kworker-thread aangaan. Een drukkekworkeris de kernel die werk doet voor iets. Zoek het proces dat het veroorzaakt in plaats van een kernelthread te willen renicen. %CPUlezen meteen nadat jetophebt gestart. Het eerste scherm is het onbetrouwbare. Wacht op de tweede verversing voordat je iets gelooft.- Aannemen dat
Tasks: 517 total517 programma's betekent. De helft zijn kernelthreads (paragraaf 7.6), en metHaan schakelt de telling naar threads en springt hij weer omhoog. - Een zombie afbreken. Een
Z-proces is al dood en houdt geen geheugen vast. Het wacht tot zijn ouder de afsluitstatus ophaalt, dus het proces om naar te kijken is de ouder. - Sorteren op
%CPUterwijl je metLzoekt. De handleiding waarschuwt hier rechtstreeks voor: regels verschuiven tussen verversingen, dus een zoekopdracht op een onstabiele sorteerkolom raakt steeds zijn plek kwijt. Sorteer opPIDterwijl je zoekt. - Hem vanuit een script aanroepen zonder
-b.topneemt de terminal over, dus een cron-taak of een CI-stap die de batchmodus vergeet levertfailed tty geten foutcode 1 op, en verder niets. - Swap die gebruikt wordt als probleem melden. Pagina's die inactief naar buiten zijn verplaatst zijn de kernel die zijn werk doet. Swap die actief heen en weer gaat is het probleem, en
vmstat 1laat dat zien in de kolommensienso.
10. Samenvatting
top is ruim veertig jaar oud, staat op elke Linux-server, en wordt vaker verkeerd gelezen dan welk ander commando op de machine ook. Bijna alles daarvan komt neer op een zin: elk veld met een procentteken is het verschil tussen twee metingen, en elk veld zonder is een totaal.
- Vijf samenvattingsregels beschrijven de machine, daarna komt er een regel per proces.
hvoor hulp,qom te stoppen. - De load average is geen CPU-maat. De handleiding van
/proc/loadavgomschrijft hem als taken in de wachtrij of wachtend op schijf-I/O. Deel hem door het aantal kernen, en lees alle drie de cijfers als een richting. - De
%Cpu(s)-regel is het gemiddelde van elke kern. Een kern op 98 procent van de zestien verschijnt als 6 procent, dus druk op1. wais inactieve tijd terwijl iets op I/O wacht, begrensd door het aantal vrije kernen. Het is een aanwijzing, geen schijfmeter.stis je hypervisor die jouw CPU aan iemand anders geeft.- Lees
avail Mem, nietfree.usedisMemTotal - MemAvailableenbuff/cacheisBuffers + Cached + SReclaimable, allebei rechtstreeks uit/proc/meminfo. Cache wordt op verzoek teruggegeven. - Een
%CPUboven 100 is de Irix-modus die een kern als 100 procent telt.Ischakelt naar de Solaris-modus: dezelfde taak toonde 401,0 en daarna 25,1 op een machine met zestien kernen.Hsplitst een proces in zijn threads. - Je kunt
RESniet optellen. Gedeelde pagina's tellen mee bij elk proces dat ze koppelt: 23,6 GiB aanREStegenover 15,0 GiB die werkelijk in gebruik is.PSSenUSSverdelen ze wel netjes, tegen tien keer de kosten. VIRTis adresruimte, geen geheugen. 1448.4g op een machine met 62,5 GiB is normaal en betekent niets.top -b -n 1is een steekproef van 0,2 seconde. Gemeten op een proces dat 14 procent gebruikte liepen twaalf metingen van het eerste beeld van 0,0 tot 60,0; metingen van het tweede beeld zaten tussen 10 en 18. Gebruiktop -b -n 2 -d 1en lees het tweede beeld.- De batchmodus leest je rcfile. Een bewaarde vertraging maakte van
top -b -n 21,21 seconde in plaats van 3,21. Zet-d,-oen-w 512op de commandoregel, of wijsXDG_CONFIG_HOMEergens leegs aan. - Zonder terminal gaat
topuit van 80 kolommen en kapt hijCOMMANDaf. Zonder-bweigert hij helemaal te draaien:failed tty get, foutcode 1. - Filteren met
-u,-Uofoversmalt de lijst maar nooit de samenvatting. Alleen-pversmalt allebei. PRisNI + 20voor gewone processen. Een negatievePRis realtime, niet beleefd:-51is realtimeprioriteit 50, enrtis 99.topberekent bijna niets: hij leest bestanden./proc/loadavg,/proc/meminfoen/proc/statvoor de samenvatting,/proc/PID/staten/proc/PID/statmper proces.stracetelde 2231 openingen voor een beeld, en de 1042stat-metingen over 521 processen bewijzen dat hij alles twee keer bemonstert. Lees in een script liever het bestand.- Een vrije kern helpt een proces niet dat er niet op mag draaien. Een vastzetting door
taskset, een systemd-CPUAffinity=of--cpuset-cpusziet er precies uit als een knelpunt met een enkele thread.taskset -cp PIDhoudt ze uit elkaar, en een nice-waarde is een gewicht en geen plafond. /proc/pressure/{cpu,memory,io}meet hoe lang taken vertraagd werden, niet hoe druk een apparaat was, en het wordt niet verdund door het aantal kernen zoalswadat wel is.- In een container rapporteert
topde host. Een container van 256 MiB toonde 62,5 GiB totaal en zestien CPU's. De PID-namespace wordt gerespecteerd; de cgroups voor geheugen en CPU niet, en een PID van binnen betekent buiten niets: PID 1 daar was 130140 hier. LIBPROC_HIDE_KERNEL=1haalde 256 kernelthreads weg uit een lijst van 518 taken.topenpsdrukten 0,0 en 67,4 af voor hetzelfde proces op hetzelfde moment.topmeet het laatste interval;psmiddelt over een levensduur.topis om te kijken,psis om te scripten.htopenbtopzijn prettiger om voor te zitten, en geen van beide is standaard geïnstalleerd:procpsis een required-pakket, zij zijn optional. Ze lezen ook dezelfde/proc-bestanden, dushtoptoonde in een container van 256 MiB eveneens15.1G/62.5G. Enhtop -d 5betekent een halve seconde, geen vijf.topbewaart geen geschiedenis. Voor de tien minuten voordat de server omviel heb jeatopofsarnodig, en die moeten vooraf geïnstalleerd zijn.
Dit is het overzicht dat de moeite waard is om te bewaren:
THE ESSENTIALS
top start it; q quits, h helps
top -b -n 2 -d 1 batch, two frames <- read the SECOND one
top -b -n 1 -o +%MEM -w 512 one frame, sorted by memory, full width
top -u www-data one user only
top -p 1234,5678 specific PIDs (up to 20)
top -H threads instead of processes
top -c full command lines
top -V version, before trusting any flag
KEYS THAT EARN THEIR PLACE
1 one line per CPU core <- the single-core bottleneck
M P T sort by MEM / CPU / TIME+ x highlight the sort column
c program name or full command line V forest view (v collapses)
i hide tasks that used no CPU since the last refresh
H threads mode I Irix (per core) or Solaris (per machine)
u filter by user o filter any field: RES>500000, !USER=root
f choose columns e/E memory units, KiB through EiB
L / & search / search again = clear all filters and limits
A four windows at once W save the layout (and change your scripts)
k kill r renice d change the interval
WHERE THE NUMBERS LIVE (read these directly in scripts)
/proc/loadavg the three load figures
/proc/meminfo every value on both memory lines
/proc/stat the %Cpu(s) line
/proc/PID/stat PID, state, PR, NI, cpu times, last used CPU
/proc/PID/statm VIRT, RES, SHR
/proc/pressure/{cpu,memory,io} how long tasks were DELAYED, not how busy
CORES A PROCESS CANNOT USE
taskset -cp PID show the affinity mask of a running process
taskset -c 0,1 CMD start something on CPUs 0 and 1 only
CPUAffinity= / --cpuset-cpus systemd units and docker set pins too
nice -n 10 CMD / renice a WEIGHT, not a cap; CPUQuota= is the cap
READING THE SUMMARY
load average tasks wanting to run, R *and* D. Divide by nproc.
%Cpu(s) the average of every core. Press 1 before concluding anything.
us your code sy kernel ni niced id doing nothing
wa IDLE, waiting on I/O hi/si interrupts
st the hypervisor gave your CPU away
used = MemTotal - MemAvailable
buff/cache = Buffers + Cached + SReclaimable, given back on demand
avail Mem READ THIS ONE. It is MemAvailable, and it is physical RAM.
READING A PROCESS ROW
%CPU since the LAST REFRESH, not a lifetime average (ps differs)
TIME+ CPU time consumed, not wall clock (ELAPSED is wall clock)
RES real memory - compare processes with it, never sum it
VIRT address space - ignore it
SHR how much of RES may be double counted
S R ready S sleeping D uninterruptible (unkillable) Z zombie
PR NI + 20; negative means REAL TIME, rt means priority 99
DO NOT
top -b -n 1 for a CPU number it is a 0.2s sample
parse top by column number the rcfile and toggles move them
add up the RES column shared pages counted many times
size a container from inside it reports the host's RAM and cores
press k then Enter the default PID is the busiest process
run it without -b in a script "failed tty get", exit 1
WHEN top RUNS OUT (none of these ship by default; procps does)
atop / sar history: what happened before you logged in
htop per-core meters, tree, F9 to kill, scrolling
btop graphs and mouse, needs a modern terminal
pidstat -d 1 per-process disk I/O
pidstat -t -p PID per thread, indented under the process total
ps -L -p PID threads with the core each last ran on
systemd-cgtop top for cgroups: which service or container
strace -c -p PID which system call is behind a high %sy
iostat -x 2 is the disk the bottleneck
vmstat 1 rates: swap in/out, context switches, faults
docker stats the real numbers for a container
ps -eo ... anything scripted
htop -d 5 = 0.5s, it counts in TENTHS (top -d 5 = 5 seconds)
htop piped = raw escape codes, exit 0 (top without -b = exit 1)
htop, btop = the same /proc files, so the same misreadings
every % is a DELTA between two refreshes; everything else is a TOTAL
the load average counts disk waiters, so it is not a CPU number
the CPU line is an average: press 1
avail Mem, not free
Begin met de load average en de CPU-regel samen, want dat tweetal scheidt in ongeveer vijf seconden een processorprobleem van een opslagprobleem. En als een server kruipt, de load average op 30 staat en elke kern niets doet, was de CPU nooit het probleem: er zit iets vast in toestand D, wachtend op een schijf die geen antwoord meer geeft.


Peter is Joomla specialist en Linux admin voor snelle, veilige en schaalbare websites.












