Procesy a signály

ps, top, kill, signály, jobs, nohup.

Co se naučíš: Uvidíš, co na stroji běží, a budeš umět proces korektně ukončit místo mlácení do vypínače.

17 min čtení + cvičeníNavazuje na:📁 Základní příkazy

Proces je běžící instance programu, program je jen soubor na disku (třeba /usr/bin/firefox), proces je ten samý program naživu v paměti, s vlastním číslem, právy a otevřenými soubory. Jeden program můžeš spustit desetkrát a máš deset procesů. Když se naučíš procesy vidět, řadit do popředí a pozadí a posílat jim signály, přestaneš být bezmocný ve chvíli, kdy něco „zamrzne" nebo drží port a nebudeš první ranou střílet kill -9.


Pár pojmů na úvod

  • Proces = běžící instance programu (program v paměti, ne soubor na disku).
  • PID (Process ID) = jedinečné číslo procesu. Podle něj ho oslovuješ (kill 1234).
  • PPID (Parent PID) = číslo rodiče, procesu, který tenhle proces spustil. Každý proces má rodiče.
  • PID 1 = úplně první proces, který jádro nastartuje. Dnes je to skoro vždy systemd (dřív „init"). Je to praotec všech ostatních procesů a nikdy neumírá, dokud běží systém.
  • Signál = malá zpráva poslaná procesu („skonči", „pozastav se", „znovu načti config"). Způsob, jak s procesy mluvíš zvenčí.
  • Job (úloha) = proces spuštěný z tvého shellu, který shell sleduje (kvůli fg/bg/jobs).

Co je proces a jak vzniká strom

Když spustíš program, jádro vytvoří proces: přidělí mu PID, kus paměti a zapamatuje si, kdo ho spustil (PPID). A tady je klíč: v Linuxu každý proces vzniká z jiného procesu. Tvůj shell (bash) spustí firefox → firefox je dítě shellu, shell je jeho rodič. Shell zase spustil terminál, terminál spustil systemd… až úplně nahoře je PID 1 (systemd), praotec všeho.

Vznikne tak strom procesů, jeden kořen, z něj větve. Podívat se na něj můžeš takhle:

pstree -p          # strom procesů s PID v závorkách
ps -ef --forest    # totéž ve sloupcích, odsazené podle rodičů

💡 Proč to tak je? Když nějaký proces spadne, jádro to oznámí jeho rodiči (ten „sebere výsledek"). A když rodič zemře dřív než dítě, dítě nezůstane bez dozoru, adoptuje ho PID 1. Strom drží pořádek v tom, kdo za koho odpovídá.


Jak si procesy prohlédnout

ps, snímek v jednu chvíli

ps vypíše procesy tak, jak vypadají teď (statický snímek, ne živě). Dvě nejčastější kouzelné formule, které si prostě zapamatuj:

ps aux             # všechny procesy všech uživatelů, „user-friendly" sloupce
ps -ef             # totéž, jiný (klasický Unix) formát: často s PPID

Nenech se zmást tím, že aux je bez pomlčky a -ef s pomlčkou, jsou to dva historické styly ps, které dělají skoro totéž. Sloupce u ps aux, které tě zajímají:

SloupecCo znamená
PIDčíslo procesu
%CPUkolik procesoru zrovna žere
%MEMkolik paměti žere
STATstav procesu (viz níže)
COMMANDčím byl spuštěn

STAT ti řekne, co proces dělá: R = běží/připraven, S = spí (čeká na něco, úplně normální), D = nepřerušitelný spánek (typicky čeká na disk), T = zastavený (uspaný přes Ctrl+Z), Z = zombie (o tom níž). Písmena se kombinují se symboly (S+ = spí a je v popředí).

top / htop, živě a interaktivně

ps je fotka, top je video. Ukazuje procesy živě, řazené podle vytížení, a čísla se aktualizují.

top                # skoro všude, ovládá se klávesami (q = konec)
htop               # hezčí, barevné, myš, F-klávesy: nutno doinstalovat

V htop vidíš zátěž jader nahoře, seznam procesů dole, a přímo v něm můžeš proces zabít (F9) nebo změnit prioritu (F7/F8). Pro rychlé „co mi žere procesor" je htop nejlepší kamarád.

pgrep, najdi PID podle jména

Když chceš jen zjistit PID něčeho konkrétního, nemusíš očima prohledávat ps aux:

pgrep firefox      # vypíše PID(y) všech procesů „firefox"
pgrep -a sshd      # -a přidá i celý příkaz, ať víš, že jsi trefil to pravé

Popředí, pozadí a úlohy

Když spustíš příkaz normálně, běží v popředí, drží ti terminál a ty čekáš, než doběhne. Často to ale nechceš (dlouhý build, server, kopírování). Řešení je pustit ho do pozadí.

sleep 300 &        # & na konci = spusť rovnou na pozadí, terminál máš hned volný

A když už příkaz běží v popředí a až teď si to rozmyslíš?

# 1) stiskni Ctrl+Z  → proces se USPÍ (zastaví) a vrátí ti prompt
# 2) rozjeď ho na pozadí:
bg                 # „background": pokračuj, ale na pozadí
# nebo ho vrať zpět do popředí:
fg                 # „foreground": zase drží terminál

Přehled toho, co ti v shellu běží, dá jobs:

jobs               # seznam úloh tohoto shellu s čísly [1], [2]…
fg %2              # vrať do popředí úlohu číslo 2
kill %1            # zabij úlohu číslo 1 (přes číslo úlohy, ne PID)

⚠️ Pozor na rozdíl Ctrl+Z vs Ctrl+C. Ctrl+C proces ukončí (pošle SIGINT). Ctrl+Z ho jen uspí (pošle SIGTSTP), pořád existuje, jen stojí. Když na uspaný proces zapomeneš a zavřeš terminál, může viset dál.

Ať to přežije zavření terminálu

Normálně platí: zavřeš terminál → jeho procesy dostanou SIGHUP a umřou. To u dlouhých úloh na serveru nechceš. Dvě cesty:

nohup ./dlouhy-skript.sh &     # „no hangup": ignoruje SIGHUP, výstup jde do nohup.out
./dlouhy-skript.sh &           # spustíš na pozadí
disown                         # odpojíš úlohu od shellu → SIGHUP ji nezasáhne

💡 Pro serióznější „ať to běží i po odhlášení" se používají tmux nebo screen (celé sezení, do kterého se dá vrátit). Ale na jeden příkaz nohup/disown bohatě stačí.


Signály, jak s procesy mluvíš

Signál je krátká zpráva, kterou jádro doručí procesu. Neposíláš text, posíláš číslo/jméno a proces na něj (většinou) nějak reaguje. Ty nejdůležitější:

SignálČísloCo říkáDá se odchytit?
SIGTERM15„slušně skonči, ukliď po sobě" (výchozí u kill)ano
SIGKILL9„umři teď, hned, bez řečí"NE
SIGINT2„přeruš se", to posílá Ctrl+Cano
SIGHUP1„zavřel se terminál" / u démonů často „znovu načti config"ano
SIGTSTP20„uspi se", to posílá Ctrl+Z (jde odchytit)ano
SIGSTOP19vynucený stop (ručně kill -STOP)NE
SIGCONT18„pokračuj" (probudí zastavený proces)ano

Klíčový rozdíl: SIGTERM vs SIGKILL. SIGTERM je zdvořilá žádost, proces ji může odchytit, uložit rozdělanou práci, zavřít soubory, uvolnit zámky a pak skončit. SIGKILL nejde odchytit ani odmítnout; jádro proces zabije v tu ránu. To je mocné, ale proto i nebezpečné (viz Failure modes).

Nástroje na posílání signálů

kill 1234          # pošle SIGTERM (15) procesu s PID 1234: slušné ukončení
kill -9 1234       # pošle SIGKILL (9): tvrdé zabití (až když TERM nezabral)
kill -HUP 1234     # pošle SIGHUP: třeba „přenačti config" u serveru

killall firefox    # zabij VŠECHNY procesy jménem „firefox“
pkill -f skript.sh # zabij procesy, jejichž příkazová řádka odpovídá vzoru

kill mluví přes PID, killall přes přesné jméno, pkill přes vzor (regulární výraz nad jménem/ příkazem). pkill -f hledá i v celé příkazové řádce, užitečné, ale snadno tím trefíš víc, než chceš.

Proč NEsahat rovnou po kill -9

Nejčastější začátečnický reflex je „nefunguje to → kill -9". Nedělej to jako první. SIGKILL proces zabije uprostřed čehokoli dělá:

  • rozepsaná data se neuloží (databáze, editor),
  • zámky a dočasné soubory zůstanou (proces se nedostal k úklidu → příště „už to běží / soubor zamčen", i když nic neběží),
  • u databází může zůstat poškozený stav.

Správný postup je odstupňovaný: nejdřív kill (SIGTERM), dej procesu pár vteřin, ať se v klidu vypne, a teprve když nereaguje, přitvrď na kill -9. SIGKILL je poslední pojistka, ne první volba.


Zombie a orphan, dva strašáci (a proč se jich nebát)

Znějí děsivě, ale jsou to jen dva normální stavy ve stromu procesů:

  • Zombie (Z ve STAT) = proces, který doběhl, ale jeho rodič si ještě nesebral výsledek (návratový kód). Zombie už nic nedělá a nežere paměť, je to jen řádek v tabulce, „vzkaz čeká na vyzvednutí". Zmizí, jakmile ho rodič sebere (wait). Zabít zombii nejde (už je „mrtvá"), když jich je moc, problém je rodič, který si výsledky nesbírá; řešíš tedy rodiče (restart / kill rodiče).
  • Orphan (sirotek) = proces, jehož rodič umřel dřív než on. Takový proces nezůstane bez dozoru: automaticky ho adoptuje PID 1 (systemd), který za něj pak převezme roli rodiče (a sebere po něm výsledek). Sirotci jsou úplně v pořádku a běžní.

💡 Pointa: hromada zombie = signál, že nějaký program má bug v úklidu po dětech, ne že „musíš honit zombie". Restartuj (nebo zabij) jejich rodiče a zombie zmizí s ním.


Priorita procesů, nice a renice (krátce)

Když víc procesů chce procesor, jádro je řadí podle priority. Tu ovlivníš hodnotou nice: rozsah −20 (nejvyšší priorita, „nejsem hodný, chci CPU") až +19 (nejnižší, „jsem hodný, ostatní mají přednost"). Běžný uživatel smí prioritu jen snižovat (zvyšovat = jen root).

nice -n 10 ./narocny-vypocet.sh   # spusť rovnou s nižší prioritou (hodnota +10)
renice -n 5 -p 1234               # změň prioritu už běžícího procesu (PID 1234)

Typické použití: pustíš dlouhý build nebo kompresi s nice, ať ti neseká zbytek systému.


Failure modes, časté omyly začátečníků

  • kill -9 jako první volba. Zabiješ proces bez úklidu → ztráta rozdělaných dat, viselé zámky. Vždycky nejdřív kill (SIGTERM), pauza, a -9 až když nereaguje.
  • Zabití špatného PID. Zkopíruješ číslo z rychlíku a odstřelíš nevinný (nebo důležitý systémový) proces. Než pošleš signál, ověř si, co PID je: ps -p 1234 -o pid,cmd nebo pgrep -a.
  • Proces zůstal v pozadí a drží port. Server jsi „ukončil" přes Ctrl+Z (jen uspal!) nebo přes & a zapomněl na něj → „port už je obsazený". Najdi viníka: ss -tlnp | grep :3000 (nebo lsof -i :3000) a teprve pak ho cíleně ukonči.
  • Honění zombie. Snažíš se zombii zabít přímo, nejde to, je už mrtvá. Řeš rodiče, ne zombii.
  • pkill -f odstřelí víc, než chceš. Vzor sedí i na jiné procesy (třeba na tvůj grep nebo editor s tím jménem v cestě). Nejdřív pgrep -af vzor a zkontroluj seznam, pak teprve pkill.

🛠️ Cvičení

  1. Najdi rodiče. Spusť sleep 500 &, pak zjisti jeho PID a PPID. Kdo je jeho rodič? Ověř, že PPID odpovídá tvému shellu. (Nápověda: pgrep, ps -o pid,ppid,cmd -p <PID>.)
  2. Popředí ↔ pozadí. Spusť sleep 300 v popředí, uspi ho přes Ctrl+Z, mrkni na jobs, pošli ho na pozadí přes bg a pak zase zpět do popředí přes fg. Popiš, co se v každém kroku stalo.
  3. Slušně vs tvrdě. Spusť si nějaký vlastní sleep 999 &. Ukonči ho nejdřív obyčejným kill <PID> (SIGTERM). Pak druhý spusť znovu a zkus kill -9 <PID>. Jaký je rozdíl a kdy který použít?
  4. Kdo drží port. Najdi příkaz, kterým zjistíš, jaký proces poslouchá na konkrétním TCP portu (třeba 22). (Nápověda: ss -tlnp.) Proč je lepší vědět PID, než střílet killall naslepo?
  5. Priorita. Spusť nice -n 15 sleep 200 & a v ps/htop si najdi jeho nice hodnotu (NI). Proč bys náročný výpočet pustil s vyšší nice hodnotou?
Náčrt řešení: rozbal, až si cvičení zkusíš sám
  1. Najdi rodiče, PID=$(pgrep -n sleep) chytne PID naposledy spuštěného sleep, pak ps -o pid,ppid,cmd -p $PID ukáže PID i PPID. PPID se rovná PID tvého shellu (ověř echo $$, to je PID aktuálního bashe). Rodičem je shell, protože on ten sleep spustil. Pozor: & proces neodpojí od rodiče, jen ho pustí na pozadí, rodičem zůstává shell (odpojí ho až disown / smrt shellu → adopce PID 1).
  2. Popředí ↔ pozadí, v popředí sleep drží terminál (nemáš prompt). Ctrl+Z pošle SIGTSTP → proces se zastaví (STAT T) a vrátí se prompt; jobs ho ukáže jako Stopped. bg pošle SIGCONT a nechá ho běžet na pozadí (Running), terminál máš volný. fg ho vytáhne zpět do popředí. Pozor: Ctrl+Z neukončuje, jen uspává, zapomenutý uspaný job pořád existuje.
  3. Slušně vs tvrdě, kill <PID> pošle SIGTERM (15): proces dostane šanci se v klidu vypnout (uložit, uklidit). kill -9 <PID> pošle SIGKILL: jádro ho zabije okamžitě, bez možnosti úklidu. U sleep je výsledek stejný (nemá co ukládat), ale u reálného programu (databáze, editor) může -9 znamenat ztrátu dat a viselé zámky. Pozor: -9 je poslední možnost, ne první, nejdřív vždy SIGTERM a chvíli počkej.
  4. Kdo drží port, ss -tlnp (nebo sudo ss -tlnp sport = :22) vypíše naslouchající TCP porty i s PID/jménem procesu; alternativa lsof -i :22. Bez sudo někdy neuvidíš cizí PID. Znát konkrétní PID je lepší, protože killall <jméno> odstřelí všechny procesy toho jména (i ty, co s portem nesouvisí), cílený kill <PID> zasáhne jen viníka. Pozor: nezaměň naslouchající proces (LISTEN) za pouhé připojení.
  5. Priorita, NI sloupec v ps -o pid,ni,cmd / htop ukáže nice 15. Vyšší nice = „hodnější" proces = nižší priorita → jádro dá přednost ostatním, takže náročný výpočet ti neseká interaktivní práci (myš, terminál). Pozor: běžný uživatel smí nice jen zvyšovat (snižovat prioritu); zpět dolů (−) smí jen root.

🧠 Otázky & odpovědi

Jaký je rozdíl mezi programem a procesem?

Program je nehybný soubor na disku (např. /usr/bin/firefox), recept. Proces je ten program spuštěný a živý v paměti: má vlastní PID, přidělenou paměť, práva a otevřené soubory. Z jednoho programu můžeš mít víc procesů současně (deset otevřených terminálů = deset procesů z jednoho binárního souboru). Jakmile proces doběhne nebo ho ukončíš, zmizí z paměti, program na disku zůstává.

Co je PID 1 a proč je důležitý?

PID 1 je úplně první proces, který jádro nastartuje při bootu, dnes skoro vždy systemd (dřív „init"). Je to praotec všech ostatních procesů: přímo nebo přes potomky z něj vzejde všechno ostatní. Má dvě zvláštní role: spouští a hlídá systémové služby (démony) a adoptuje sirotky, procesy, jimž umřel rodič, aby nezůstaly bez dozoru a někdo po nich sebral výsledek. PID 1 běží, dokud běží systém; když spadne, padá celý stroj.

Kdy použít SIGTERM a kdy SIGKILL?

Vždycky nejdřív SIGTERM (kill <PID>, signál 15). Je to zdvořilá žádost „skonči", proces ji může odchytit, uložit rozdělanou práci, zavřít soubory a uvolnit zámky, a pak v klidu skončit. Teprve když nereaguje (visí, ignoruje TERM), sáhni po SIGKILL (kill -9, signál 9), ten nejde odchytit ani odmítnout, jádro proces zabije okamžitě. Cena za to je, že se proces nestihne uklidit: hrozí ztráta dat a viselé zámky. Proto je -9 poslední pojistka, ne první reflex.

Co je zombie proces a jak se ho zbavit?

Zombie (Z ve STAT) je proces, který už doběhl, ale jeho rodič si ještě nesebral výsledek (návratový kód). Zombie nic nedělá a nežere paměť, je to jen záznam v tabulce, „vzkaz čeká na vyzvednutí". Zabít ji nejde (už je mrtvá). SIGKILL na ni nezabere. Zmizí sama, jakmile si ji rodič vyzvedne. Když se hromadí, viník je rodičovský proces, který si výsledky po dětech nesbírá (bug), řešíš tedy jeho: restartuješ ho, nebo ho ukončíš (pak zombie zdědí a uklidí PID 1).

Proč mi proces po zavření terminálu umřel a jak tomu zabránit?

Protože zavřením terminálu jádro pošle jeho procesům SIGHUP („hang up") a výchozí reakce na SIGHUP je skončit. U dlouhých úloh to nechceš. Řešení: spusť příkaz přes nohup ./skript & (ignoruje SIGHUP, výstup jde do nohup.out), nebo ho pusť na pozadí a hned disown (odpojí úlohu od shellu, takže ji SIGHUP nezasáhne). Pro delší práci na serveru je nejlepší tmux nebo screen, celé sezení, do kterého se po opětovném přihlášení vrátíš přesně tam, kde jsi skončil.