Ciele
- Pozri sa, čo na tvojom systéme beží a čo ho zaťažuje
- Vyrob si zombie proces a zbav sa ho
- Over rozdiel medzi slušným a tvrdým ukončením
- Napíš si vlastnú službu
- Nechaj ju spadnúť a sleduj, čo urobí systém
- Naplánuj úlohu klasickým plánovačom
- Naplánuj tú istú úlohu časovačom a porovnaj ich
Úvod
Na prednáške sme hovorili o procesoch, o tom, ako sa z programu stane služba, a o dvoch spôsoboch plánovania. Dnes si to postavíš.
Na konci budeš mať vlastnú službu, ktorá sa po páde sama zdvihne, a tú istú úlohu naplánovanú dvoma spôsobmi — takže uvidíš rozdiel na vlastných výstupoch, nie z tabuľky.
Všetko robíš vo svojej virtuálke. Celé to trvá zhruba hodinu a pol. Nie je to za body, mentori to s tebou preberú.
Postup
Krok 1: Pozri sa, čo na tvojom systéme beží a čo ho zaťažuje
ps aux --sort=-%cpu | head -10
ps aux --sort=-%rss | head -10
uptime
nproc
Úloha 1.1
Zapíš si tri procesy s najväčšou spotrebou procesora a tri s najväčšou spotrebou pamäte. Potom si zapíš tri čísla priemerného zaťaženia a počet jadier.
Úloha 1.2
Odpovedz si na otázku, či je tvoj systém zaťažený. Nestačí sa pozrieť na číslo — porovnaj ho s počtom jadier a porovnaj aj tie tri hodnoty navzájom.
Teraz si pozri strom procesov:
pstree -p | head -30
Úloha 1.3
Nájdi v strome svoje vlastné prihlásenie. Zapíš si reťaz od procesu číslo jeden až po shell, v ktorom práve píšeš.
Poznámka: Pekne v ňom vidno, prečo program spustený z terminálu skončí spolu s terminálom. Je jeho dieťaťom.
Krok 2: Vyrob si zombie proces a zbav sa ho
Zombie sa dá vyrobiť zámerne. Stačí program, ktorý si neprevezme výsledok svojho dieťaťa.
# zombie.py
import os, time
if os.fork() == 0:
os._exit(0) # dieta hned skonci
else:
print("rodic ma PID", os.getpid())
time.sleep(120) # rodic si vysledok neprevezme
python3 zombie.py &
ps aux | grep -w Z
ps -eo pid,ppid,stat,comm | awk '$3 ~ /^Z/'
Úloha 2.1
Nájdi zombie proces vo výpise. Zapíš si jeho číslo, číslo jeho rodiča a písmeno, ktorým je označený jeho stav.
Úloha 2.2
Teraz skús zombie ukončiť a zapíš si, čo sa stalo:
kill -9 <cislo zombie procesu>
ps -eo pid,ppid,stat,comm | awk '$3 ~ /^Z/'
Úloha 2.3
Zombie je stále tam. Ukonči teraz rodiča a pozri sa znova.
kill <cislo rodica>
ps -eo pid,ppid,stat,comm | awk '$3 ~ /^Z/'
Poznámka: Zombie zmizol až s rodičom. Nedá sa ukončiť, lebo už skončil — drží len svoje číslo, kým si rodič neprevezme výsledok. A keď rodič skončí, dieťa si adoptuje proces číslo jeden, ktorý výsledky preberá spoľahlivo.
Krok 3: Over rozdiel medzi slušným a tvrdým ukončením
Napíš si program, ktorý na slušnú žiadosť zareaguje a po sebe uprace.
# slusny.py
import signal, sys, time
def uprac(signum, frame):
with open("uprataneb.txt", "w") as f:
f.write("stihol som po sebe upratat\n")
sys.exit(0)
signal.signal(signal.SIGTERM, uprac)
print("bezim, PID", __import__("os").getpid())
while True:
time.sleep(1)
rm -f uprataneb.txt
python3 slusny.py &
kill %1
cat uprataneb.txt
Úloha 3.1
Zapíš si, či súbor vznikol.
Teraz to isté natvrdo:
rm -f uprataneb.txt
python3 slusny.py &
kill -9 %1
cat uprataneb.txt
Úloha 3.2
Zapíš si, či súbor vznikol tentoraz, a vysvetli si, prečo je ten rozdiel taký, aký je.
Poznámka: Predstav si, že namiesto zápisu do súboru ide o databázu, ktorá má dokončiť rozpísanú transakciu. Práve preto sa vždy začína slušnou žiadosťou.
Krok 4: Napíš si vlastnú službu
Najprv skript, ktorý bude služba spúšťať. Nech robí niečo, čo je vidieť.
# /usr/local/bin/hliadka.sh
#!/bin/bash
while true; do
echo "hliadka: $(date '+%H:%M:%S'), volna pamat $(free -m | awk '/^Mem:/{print $7}') MB"
sleep 10
done
sudo nano /usr/local/bin/hliadka.sh
sudo chmod +x /usr/local/bin/hliadka.sh
/usr/local/bin/hliadka.sh # over, ze funguje, potom ukonci cez Ctrl a C
Teraz definícia služby:
sudo nano /etc/systemd/system/hliadka.service
[Unit]
Description=Hliadka volnej pamate
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/hliadka.sh
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now hliadka
systemctl status hliadka
journalctl -u hliadka -f # ukonci cez Ctrl a C
Úloha 4.1
Zapíš si z výpisu stavu dva riadky — ten, ktorý hovorí o autoštarte, a ten, ktorý hovorí o aktuálnom stave.
Úloha 4.2
Odpovedz si, prečo sa výstup skriptu objavuje v systémovom denníku, hoci ho skript len vypisuje na obrazovku.
Krok 5: Nechaj ju spadnúť a sleduj, čo urobí systém
systemctl show hliadka -p MainPID
sudo kill -9 <to cislo>
sleep 8
systemctl status hliadka
Úloha 5.1
Zapíš si, či sa služba znova spustila a za ako dlho. Porovnaj to s hodnotou, ktorú si nastavil v definícii.
Teraz si vyskúšaj, čo sa stane pri trvalej chybe:
sudo sed -i 's|^ExecStart=.*|ExecStart=/usr/local/bin/neexistuje.sh|' /etc/systemd/system/hliadka.service
sudo systemctl daemon-reload
sudo systemctl restart hliadka
sleep 30
systemctl status hliadka
journalctl -u hliadka -n 20
Úloha 5.2
Zapíš si, do akého stavu sa služba dostala a čo o tom hovorí denník.
Poznámka: Systém to po niekoľkých neúspešných pokusoch vzdá a nechá službu v stave zlyhania. Je to zámer — nekonečné pokúšanie by len zaťažovalo stroj a nikto by si chyby nevšimol.
Úloha 5.3
Vráť pôvodnú cestu späť a over, že služba znova beží.
Krok 6: Naplánuj úlohu klasickým plánovačom
# /usr/local/bin/zapis.sh
#!/bin/bash
echo "$(date '+%F %T') spustene ako $(whoami), PATH=$PATH" >> /tmp/plan.log
sudo nano /usr/local/bin/zapis.sh
sudo chmod +x /usr/local/bin/zapis.sh
crontab -e
Pridaj riadok, ktorý úlohu spustí každú minútu:
* * * * * /usr/local/bin/zapis.sh
sleep 130
cat /tmp/plan.log
Úloha 6.1
Zapíš si, čo je v tom súbore. Všimni si najmä hodnotu posledného údaja
a porovnaj ju s tým, čo ti vypíše echo $PATH v termináli.
Poznámka: Toto je ten rozdiel v prostredí, o ktorom bola reč na prednáške. Preto sa v naplánovaných úlohách vždy používajú úplné cesty.
Úloha 6.2
Odstráň riadok z plánovača, aby ti úloha nebežala do konca semestra.
Krok 7: Naplánuj tú istú úlohu časovačom a porovnaj ich
Potrebuješ dva súbory — službu a časovač s rovnakým menom.
sudo nano /etc/systemd/system/zapis.service
[Unit]
Description=Zapis riadku do denniku
[Service]
Type=oneshot
ExecStart=/usr/local/bin/zapis.sh
sudo nano /etc/systemd/system/zapis.timer
[Unit]
Description=Spusti zapis kazdu minutu
[Timer]
OnCalendar=*:0/1
Persistent=true
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now zapis.timer
systemctl list-timers zapis.timer
sleep 130
journalctl -u zapis.service -n 10
Úloha 7.1
Zapíš si, čo ukáže výpis časovačov — kedy naposledy bežal a kedy pobeží nabudúce.
Úloha 7.2
Porovnaj, kde si hľadal výstup pri klasickom plánovači a kde pri časovači. Zapíš si ten rozdiel, je to hlavný dôvod, prečo časovače vznikli.
Teraz si over dobehnutie zameškanej úlohy:
sudo systemctl stop zapis.timer
# pockaj aspon tri minuty
sudo systemctl start zapis.timer
journalctl -u zapis.service -n 5
Úloha 7.3
Zapíš si, či sa úloha po zapnutí spustila okamžite, alebo až v najbližšom plánovanom čase. Potom si odpovedz, čo by sa stalo pri klasickom plánovači.
Úloha 7.4
Nakoniec časovač aj službu vypni, aby ti nebežali ďalej:
sudo systemctl disable --now zapis.timer
Záver
Čo budeš mať po tomto cvičení pripravené pre mentora:
- Tri najnáročnejšie procesy podľa procesora a podľa pamäte, tri hodnoty priemerného zaťaženia a počet jadier.
- Reťaz procesov od čísla jeden po svoj shell.
- Číslo zombie procesu, číslo jeho rodiča a to, čo sa stalo pri pokuse ukončiť ho.
- Výsledky oboch pokusov o ukončenie programu — či súbor vznikol a prečo.
- Definíciu svojej služby a dva riadky z výpisu jej stavu.
- Čas, za ktorý sa služba po páde sama zdvihla, a stav, do ktorého sa dostala pri trvalej chybe.
- Obsah denníka z klasického plánovača a hodnotu prostredia, ktorú tam našiel.
- Výpis časovača a výsledok pokusu o dobehnutie zameškanej úlohy.
A odpovede na tieto otázky:
- Prečo sa zombie proces nedá ukončiť a čo s ním teda urobiť?
- Prečo je rozdiel medzi slušným a tvrdým ukončením pri databáze vážnejší než pri tvojom skripte?
- Prečo sa výstup tvojho skriptu objavil v systémovom denníku, hoci ho skript len vypisuje?
- Čím sa líšilo prostredie naplánovanej úlohy od tvojho terminálu a čo z toho plynie pre písanie takých úloh?
Doplňujúce úlohy
Úloha A.1
Pridaj do svojej služby nastavenie, aby bežala pod vlastným účtom bez prihlasovacieho shellu namiesto roota. Over, že stále funguje, a zamysli sa, ktorý princíp z minulého týždňa si tým naplnil.
Úloha A.2
Doplň časovaču nastavenie náhodného odstupu a over vo výpise časovačov, že sa plánovaný čas posunul. Vysvetli, prečo sa to na jednom stroji neoplatí a na stovke strojov áno.