Procesy a signály

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

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čePID=$(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ží portss -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. PrioritaNI 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.