Dnes se mi stala zlá, nepěkný věc. V linuxovém terminálu (bash shell), připojeném k serveru přes SSH, běžela úloha, která měla být zpočátku rychle dokončena. Jak tomu ale nezřídka bývá, z původně plánovaných pěti minut se rázem stala hodina, dvě, … Ať už z důvodu možného přerušení spojení či potřeby odpojení a odnesení terminálu je v takovém případě potřeba zajistit úspěšné proběhnutí všech procedur. Pokud s takovým scénářem počítám, používám léty ověřený manažer screen
, po jehož ne/násilném odpojení zůstanou zjednodušeně aktivní úlohy spuštěny na pozadí. Co ale v případě, že takový program není k dispozici nebo jej nespustíme?
Lidé pohybující se v příkazovém řádku operačních systémů unixového typu pravděpodobně dobře znají základní skupinu příkazů umožňující práci s aktivními úlohami běžícími v aktuální relaci.
Základní příkazy pro práci s úlohami/procesy
jobs
– zobrazení seznamu aktivních úloh
1 2 3 4 |
[root@linux ~]# jobs [1]- Running find / > /dev/null [2]+ Running ls -R / > /dev/null [2]+ Running dd if=/dev/sda1 of=/mnt/backup/sda1.dd |
ps
– zobrazení seznamu aktivních procesů
1 2 3 4 5 |
[root@linux ~]# ps PID TTY TIME CMD 1043 pts/5 00:00:00 bash 1190 pts/5 00:00:23 dd 1197 pts/5 00:00:00 ps |
bg
– spuštění úlohy na pozadí
1 2 3 4 5 6 7 8 9 |
[root@linux ~]# dd if=/dev/sda1 of=/mnt/backup/sda1.dd ^Z [1]+ Stopped dd if=/dev/sda1 of=/mnt/backup/sda1.dd [root@linux ~]# jobs [1]+ Stopped dd if=/dev/sda1 of=/mnt/backup/sda1.dd [root@linux ~]# bg %1 [1]+ dd if=/dev/sda1 of=/mnt/backup/sda1.dd & |
fg
– spuštění úlohy v popředí
1 2 3 4 5 6 7 8 9 10 11 12 |
[root@linux ~]# dd if=/dev/sda1 of=/mnt/backup/sda1.dd ^Z [1]+ Stopped dd if=/dev/sda1 of=/mnt/backup/sda1.dd [root@linux ~]# jobs [1]+ Stopped dd if=/dev/sda1 of=/mnt/backup/sda1.dd [root@linux ~]# fg %1 dd if=/dev/sda1 of=/mnt/backup/sda1.dd ^C228553+0 records in 228552+0 records out 117018624 bytes (117 MB) copied, 36.6495 s, 3.2 MB/s |
kill
– ukončení běžící úlohy
1 2 3 4 5 6 7 8 9 10 |
[root@linux ~]# dd if=/dev/sda1 of=/mnt/backup/sda1.dd ^Z [1]+ Stopped dd if=/dev/sda1 of=/mnt/backup/sda1.dd [root@linux ~]# jobs [1]+ Stopped dd if=/dev/sda1 of=/mnt/backup/sda1.dd [root@linux ~]# kill %1 [root@linux ~]# [1]+ Terminated dd if=/dev/sda1 of=/mnt/backup/sda1.dd |
Tyto příkazy (kromě ps
a kill
) lze ale použít pouze pro práci s úlohami v rámci relace, ve které se nacházíme. Při jejím ukončení dojde k ukončení i všech aktivních úloh, které běží jak na pozadí, tak v popředí.
Jak ale zabezpečit, aby byly aktivní úlohy spuštění i po ukončení relace?
V takovém případě můžeme využít právě manažer screen
, jemu podobné, nebo odpojit úlohu od aktuální relace (terminálu).
Odpojení úlohy od aktuální relace
Pokud chceme pouze odpojit příslušný proces od procesu aktivního terminálu, můžeme ve shellu bash
použít příkaz disown
. Ten slouží k odstranění úlohy ze seznamu úloh aktuální relace. Tzn., při jejím ukončení zůstane tato úloha dále aktivní a nebude ukončena.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
[root@linux ~]# dd if=/dev/sda1 of=/mnt/backup/sda1.dd ^Z [1]+ Stopped dd if=/dev/sda1 of=/mnt/backup/sda1.dd [root@linux ~]# bg [1]+ dd if=/dev/sda1 of=/mnt/backup/sda1.dd & [root@linux ~]# jobs [1]+ Running dd if=/dev/sda1 of=/mnt/backup/sda1.dd & [root@linux ~]# disown %1 [root@linux ~]# jobs [root@linux ~]# ps PID TTY TIME CMD 1043 pts/5 00:00:00 bash 1220 pts/5 00:00:13 dd 1222 pts/5 00:00:00 ps |
Jak je možné vidět na příkladu, úloha (proces 1220) je ze seznamu úloh aktuální relace opravdu odstraněna, zůstává ale připojena pod rodičovským procesem (proces 1043). Pravděpodobně se však nepovede tímto způsobem odpojit úlohu programu typu Midnight Commander (mc).
1 2 3 4 5 6 |
1032 ? Ss 0:05 \_ sshd: root@pts/2 1034 pts/2 Ss 0:00 \_ -bash 1040 pts/2 S+ 0:04 \_ mc 1043 pts/5 Ss 0:00 \_ bash -rcfile .bashrc 1220 pts/5 R 0:12 \_ dd if=/dev/sda1 of=/mnt/backup/sda1.dd 1227 pts/5 R+ 0:00 \_ ps afx |
Po ukončení aktuální relace je tento proces odpojen a zůstává v systému samostatný, bez rodičovského procesu a připojené terminálové relace (místo „pts/5“ je ve výpisu „?“).
1 |
1220 ? R 0:37 dd if=/dev/sda1 of=/mnt/backup/sda1.dd |
Nevýhodou je, že tento proces nelze jednoduše opětovně připojit do jiné relace. Pokud je například průběžně vypisován stav na standardní nebo chybový výstup, zůstane nám skryt.
Pokud trváme na nutnosti zachovat tyto výstupy, můžeme použít např. GDB a procesu vnutit jejich přesměrování:
1 2 3 4 5 6 |
[root@linux ~]# touch /tmp/stdout /tmp/stderr [root@linux ~]# gdb –p 1220 p dup2(open("/tmp/stdout", 1), 1) p dup2(open("/tmp/stderr", 1), 2) detach quit |
Připojení úlohy do jiné relace
Pokud, jak bylo v úvodu popsáno, potřebujeme běžící úlohu „přemístit“ pod screen
, můžeme použít program reptyr
. Pokud jej spustíme v jiné relaci, než ve které běží úloha našeho zájmu, provede odpojení této úlohy od rodičovského procesu a pomocí systémového volání ptrace
se na tuto úlohu napojí. Je nutné brát v úvahu, že toto systémové volání má určitou režii a operační systém zatěžuje.
Zdrojová relace:
1 2 3 4 5 6 7 8 9 10 11 12 |
[root@linux tmp]# dd if=/dev/sda1 of=/mnt/backup/sda1.dd ^Z [1]+ Stopped dd if=/dev/sda1 of=/mnt/backup/sda1.dd [root@linux tmp]# bg [1]+ dd if=/dev/sda1 of=/mnt/backup/sda1.dd & [root@linux tmp]# ps PID TTY TIME CMD 1241 pts/1 00:00:00 bash 1274 pts/1 00:00:05 dd 1275 pts/1 00:00:00 ps |
Cílová relace, jako argument slouží číslo procesu úlohy PID (viz předchozí výpis „ps“):
1 2 3 4 |
[root@linux ~]# reptyr 1274 177446+0 records in 177446+0 records out 90852352 bytes (91 MB) copied, 23.8651 s, 3.8 MB/s |
Použití systémového volání ptrace
může být na některých systémech GNU/Linux, např. Ubuntu, z bezpečnostních důvodů zakázáno. Pokud jej tedy chceme využít, musíme jej povolit. (pomůže Google + „/proc/sys/kernel/yama/ptrace_scope“).
Reptyr není samozřejmě jediným svého druhu, ale rozhodně dokáže pomoci v neočekávaných situacích. Jen je potřeba brát v úvahu vlastníky jednotlivých procesů. Nelze tedy přebírat úlohy jiného uživatele.
Další
Před spuštěním dlouhotrvajícího programu můžeme použít kupříkladu známý příkaz nohup
:
1 |
[root@linux ~]# nohup dd if=/dev/sda1 of=/mnt/backup/sda1.dd 1>/tmp/stdout 2>/tmp/stderr |
Zde je vidět přesměrování výstupů do souborů. Příklad s dd
není příliš vhodným, ale pro názornost stačí.