Jednou jsem se při rozhovoru zeptal, co uděláte, když zjistíte, že služba nefunguje kvůli tomu, že na disku došlo místo?
Samozřejmě jsem odpověděl, že se podívám, co je na tomto místě obsazeno a pokud to bude možné, místo vyčistím.
Pak se tazatel zeptal, co když na oddílu není volné místo, ale také nevidíte žádné soubory, které by zabíraly všechno místo?
K tomu jsem řekl, že se vždy můžete podívat na otevřené deskriptory souborů, například pomocí příkazu lsof, a pochopit, která aplikace zabrala veškerý dostupný prostor, a pak můžete jednat podle okolností v závislosti na tom, zda jsou data potřebná .
Tazatel mě přerušil na poslední slovo a ke své otázce dodal: „Předpokládejme, že nepotřebujeme data, je to jen protokol ladění, ale aplikace nefunguje, protože neumí napsat ladění“?
"Dobře," odpověděl jsem, "můžeme vypnout ladění v konfiguraci aplikace a restartovat."
Tazatel namítl: „Ne, nemůžeme restartovat aplikaci, stále máme důležitá data uložená v paměti a k samotné službě jsou připojeni důležití klienti, které nemůžeme donutit k opětovnému připojení.“
"Dobře," řekl jsem, "pokud nemůžeme restartovat aplikaci a data pro nás nejsou důležitá, můžeme tento otevřený soubor jednoduše vymazat pomocí deskriptoru souboru, i když jej nevidíme v příkazu ls v souborovém systému."
Tazatel byl potěšen, ale já ne.
Pak jsem si pomyslel, proč člověk, který testuje mé znalosti, nepátrá hlouběji? Ale co když jsou data přece jen důležitá? Co když nemůžeme restartovat proces a proces zapisuje do systému souborů na diskový oddíl, který nemá volné místo? Co když nemůžeme ztratit nejen data, která již byla zapsána, ale ani data, která tento proces zapisuje nebo se pokouší zapsat?
Velký klobouk
Na začátku své kariéry jsem se snažil vytvořit malou aplikaci, která potřebovala ukládat informace o uživatelích. A pak mě napadlo, jak přiřadit uživatele k jeho datům. Například mám Ivanova Ivana Ivanoviče a on má nějaké informace, ale jak se s nimi mohu spřátelit? Mohu přímo podotknout, že pes jménem „Tuzik“ patří právě tomuto Ivanovi. Co když si ale změní jméno a místo Ivana se stane například Olya? Pak se ukáže, že naše Olya Ivanovna Ivanova už nebude mít psa a náš Tuzik bude stále patřit neexistujícímu Ivanovi. Databáze, která každému uživateli poskytla jedinečný identifikátor (ID), pomohla tento problém vyřešit a můj Tuzik byl svázán s tímto ID, což bylo ve skutečnosti jen sériové číslo. Majitel esa měl tedy ID číslo 2 a v určitém okamžiku byl Ivan pod tímto ID, a pak se Olya stala pod stejným ID. Problém lidstva a chovu zvířat byl prakticky vyřešen.
Popisovač souboru
Problém souboru a programu, který s tímto souborem pracuje, je přibližně stejný jako u našeho psa a člověka. Předpokládejme, že jsem otevřel soubor s názvem ivan.txt a začal do něj psát slovo tuzik, ale podařilo se mi do souboru napsat pouze první písmeno „t“ a tento soubor někdo přejmenoval například na olya.txt. Soubor ale zůstává stejný a i tak do něj chci zaznamenat své eso. Pokaždé, když je soubor otevřen systémovým voláním
V Linuxu knihovna libc otevře 3 soubory deskriptorů pro každou spuštěnou aplikaci (proces), očíslované 0,1,2. Více informací naleznete na odkazech
- Deskriptor souboru 0 se nazývá STDIN a je spojen se vstupem aplikace
- Popisovač souboru 1 se nazývá STDOUT a používají ho aplikace k výstupu dat, jako jsou příkazy pro tisk
- Popisovač souboru 2 se nazývá STDERR a používají ho aplikace k výstupu chybových zpráv.
Pokud ve svém programu otevřete jakýkoli soubor pro čtení nebo zápis, pak s největší pravděpodobností získáte první volné ID a bude to číslo 3.
Seznam deskriptorů souborů lze zobrazit pro jakýkoli proces, pokud znáte jeho PID.
Například otevřeme bash konzoli a podívejme se na PID našeho procesu
[user@localhost ]$ echo $$
15771
V druhé konzoli spustíme
[user@localhost ]$ ls -lah /proc/15771/fd/
total 0
dr-x------ 2 user user 0 Oct 7 15:42 .
dr-xr-xr-x 9 user user 0 Oct 7 15:42 ..
lrwx------ 1 user user 64 Oct 7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 255 -> /dev/pts/21
Pro účely tohoto článku můžete klidně ignorovat deskriptor souboru číslo 255; byl otevřen pro své potřeby samotným bashem, nikoli propojenou knihovnou.
Nyní jsou všechny 3 soubory deskriptorů spojeny s pseudoterminálovým zařízením
[user@localhost ]$ echo "hello world" > /proc/15771/fd/0
A v první konzoli uvidíme
[user@localhost ]$ hello world
Přesměrování a potrubí
Tyto 3 soubory deskriptorů můžete snadno přepsat v jakémkoli procesu, včetně bash, například pomocí potrubí spojujícího dva procesy, viz.
[user@localhost ]$ cat /dev/zero | sleep 10000
Tento příkaz můžete spustit sami pomocí strace -f a uvidíte, co se děje uvnitř, ale řeknu vám to krátce.
Náš nadřazený bash proces s PID 15771 analyzuje náš příkaz a přesně chápe, kolik příkazů chceme spustit, v našem případě jsou dva: kočka a spánek. Bash ví, že potřebuje vytvořit dva podřízené procesy a sloučit je do jednoho kanálu. Celkem bude bash potřebovat 2 podřízené procesy a jednu rouru.
Bash před vytvořením podřízených procesů spustí systémové volání
U nadřazeného procesu to vypadá, že již existuje roura, ale zatím neexistují žádné podřízené procesy:
PID command
15771 bash
lrwx------ 1 user user 64 Oct 7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:42 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:42 255 -> /dev/pts/21
Poté pomocí systémového volání
PID command
15771 bash
lrwx------ 1 user user 64 Oct 7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:42 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:42 255 -> /dev/pts/21
PID command
9004 bash
lrwx------ 1 user user 64 Oct 7 15:57 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:57 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:57 255 -> /dev/pts/21
PID command
9005 bash
lrwx------ 1 user user 64 Oct 7 15:57 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:57 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:57 255 -> /dev/pts/21
Nezapomeňte, že klon klonuje proces spolu se všemi deskriptory souborů, takže budou stejné v nadřazeném procesu i v podřízeném procesu. Úkolem nadřazeného procesu s PID 15771 je monitorovat podřízené procesy, takže jednoduše čeká na odpověď od potomků.
Proto nepotřebuje rouru a zavírá deskriptory souborů s čísly 3 a 4.
V prvním podřízeném bash procesu s PID 9004 systémové volání
V druhém podřízeném procesu s PID 9005 bash používá dup2 ke změně deskriptoru souboru STDIN číslo 0. Nyní bude vše, co bude číst náš druhý bash s PID 9005, načteno z roury.
Poté jsou deskriptory souborů s čísly 3 a 4 také uzavřeny v podřízených procesech, protože se již nepoužívají.
Záměrně ignoruji deskriptor souboru 255; používá ho pro interní účely samotný bash a bude také uzavřen v podřízených procesech.
Dále, v prvním podřízeném procesu s PID 9004 začne bash používat systémové volání
V druhém podřízeném procesu s PID 9005 bash spustí druhý spustitelný soubor, který jsme zadali, v našem případě /usr/bin/sleep.
Systémové volání exec neuzavře popisovače souborů, pokud nebyly otevřeny s příznakem O_CLOEXEC v době, kdy bylo provedeno otevřené volání. V našem případě se po spuštění spustitelných souborů uloží všechny aktuální deskriptory souborů.
Zkontrolujte v konzoli:
[user@localhost ]$ pgrep -P 15771
9004
9005
[user@localhost ]$ ls -lah /proc/15771/fd/
total 0
dr-x------ 2 user user 0 Oct 7 15:42 .
dr-xr-xr-x 9 user user 0 Oct 7 15:42 ..
lrwx------ 1 user user 64 Oct 7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:42 255 -> /dev/pts/21
[user@localhost ]$ ls -lah /proc/9004/fd
total 0
dr-x------ 2 user user 0 Oct 7 15:57 .
dr-xr-xr-x 9 user user 0 Oct 7 15:57 ..
lrwx------ 1 user user 64 Oct 7 15:57 0 -> /dev/pts/21
l-wx------ 1 user user 64 Oct 7 15:57 1 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:57 2 -> /dev/pts/21
lr-x------ 1 user user 64 Oct 7 15:57 3 -> /dev/zero
[user@localhost ]$ ls -lah /proc/9005/fd
total 0
dr-x------ 2 user user 0 Oct 7 15:57 .
dr-xr-xr-x 9 user user 0 Oct 7 15:57 ..
lr-x------ 1 user user 64 Oct 7 15:57 0 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct 7 15:57 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct 7 15:57 2 -> /dev/pts/21
[user@localhost ]$ ps -up 9004
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
user 9004 0.0 0.0 107972 620 pts/21 S+ 15:57 0:00 cat /dev/zero
[user@localhost ]$ ps -up 9005
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
user 9005 0.0 0.0 107952 360 pts/21 S+ 15:57 0:00 sleep 10000
Jak vidíte, jedinečné číslo naší dýmky je v obou procesech stejné. Máme tedy spojení mezi dvěma různými procesy se stejným rodičem.
Pro ty, kteří nejsou obeznámeni se systémovými voláními, které bash používá, velmi doporučuji spustit příkazy přes strace a podívat se, co se děje interně, například takto:
strace -s 1024 -f bash -c "ls | grep hello"
Vraťme se k našemu problému s nedostatkem místa na disku a pokusem o záchranu dat bez restartování procesu. Pojďme napsat malý program, který bude zapisovat na disk přibližně 1 megabajt za sekundu. Navíc, pokud se nám z nějakého důvodu nepodařilo zapsat data na disk, jednoduše to ignorujeme a pokusíme se data zapsat za sekundu znovu. V příkladu, který používám Python, můžete použít jakýkoli jiný programovací jazyk.
[user@localhost ]$ cat openforwrite.py
import datetime
import time
mystr="a"*1024*1024+"n"
with open("123.txt", "w") as f:
while True:
try:
f.write(str(datetime.datetime.now()))
f.write(mystr)
f.flush()
time.sleep(1)
except:
pass
Spustíme program a podíváme se na deskriptory souborů
[user@localhost ]$ python openforwrite.py &
[1] 3762
[user@localhost ]$ ps axuf | grep [o]penforwrite
user 3762 0.0 0.0 128600 5744 pts/22 S+ 16:28 0:00 | _ python openforwrite.py
[user@localhost ]$ ls -la /proc/3762/fd
total 0
dr-x------ 2 user user 0 Oct 7 16:29 .
dr-xr-xr-x 9 user user 0 Oct 7 16:29 ..
lrwx------ 1 user user 64 Oct 7 16:29 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 7 16:29 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 7 16:29 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 7 16:29 3 -> /home/user/123.txt
Jak můžete vidět, máme naše 3 standardní deskriptory souborů a jeden další, který jsme otevřeli. Zkontrolujeme velikost souboru:
[user@localhost ]$ ls -lah 123.txt
-rw-rw-r-- 1 user user 117M Oct 7 16:30 123.txt
Data se zapisují, snažíme se změnit oprávnění k souboru:
[user@localhost ]$ sudo chown root: 123.txt
[user@localhost ]$ ls -lah 123.txt
-rw-rw-r-- 1 root root 168M Oct 7 16:31 123.txt
[user@localhost ]$ ls -lah 123.txt
-rw-rw-r-- 1 root root 172M Oct 7 16:31 123.txt
Vidíme, že data se stále zapisují, ačkoli náš uživatel nemá oprávnění k zápisu do souboru. Zkusme to odstranit:
[user@localhost ]$ sudo rm 123.txt
[user@localhost ]$ ls 123.txt
ls: cannot access 123.txt: No such file or directory
Kde jsou zapsány údaje? A jsou vůbec napsané? Kontrolujeme:
[user@localhost ]$ ls -la /proc/3762/fd
total 0
dr-x------ 2 user user 0 Oct 7 16:29 .
dr-xr-xr-x 9 user user 0 Oct 7 16:29 ..
lrwx------ 1 user user 64 Oct 7 16:29 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 7 16:29 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 7 16:29 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 7 16:29 3 -> /home/user/123.txt (deleted)
Ano, náš deskriptor souboru stále existuje a můžeme s ním zacházet jako s naším starým souborem, můžeme jej číst, mazat a kopírovat.
Podívejme se na velikost souboru:
[user@localhost ]$ lsof | grep 123.txt
python 31083 user 3w REG 8,5 19923457 2621522 /home/user/123.txt
Velikost souboru je 19923457. Zkusme soubor vymazat:
[user@localhost ]$ truncate -s 0 /proc/31083/fd/3
[user@localhost ]$ lsof | grep 123.txt
python 31083 user 3w REG 8,5 136318390 2621522 /home/user/123.txt
Jak vidíte, velikost souboru se pouze zvětšuje a náš kufr nefungoval. Podívejme se na dokumentaci systémového volání
with open("123.txt", "w") as f:
musíme dát
with open("123.txt", "a") as f:
Kontrola pomocí příznaku „w“.
[user@localhost ]$ strace -e trace=open python openforwrite.py 2>&1| grep 123.txt
open("123.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
a s vlajkou "a".
[user@localhost ]$ strace -e trace=open python openforwrite.py 2>&1| grep 123.txt
open("123.txt", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
Programování již běžícího procesu
Často programátoři při vytváření a testování programů využívají debuggery (například GDB) nebo různé úrovně logování v aplikaci. Linux poskytuje možnost skutečně psát a měnit již běžící program, například měnit hodnoty proměnných, nastavovat bod přerušení atd. atd.
Vrátíme-li se k původní otázce o nedostatku místa na disku pro zápis souboru, pokusme se problém nasimulovat.
Vytvořme soubor pro náš oddíl, který připojíme jako samostatný disk:
[user@localhost ~]$ dd if=/dev/zero of=~/tempfile_for_article.dd bs=1M count=10
10+0 records in
10+0 records out
10485760 bytes (10 MB) copied, 0.00525929 s, 2.0 GB/s
[user@localhost ~]$
Vytvořme souborový systém:
[user@localhost ~]$ mkfs.ext4 ~/tempfile_for_article.dd
mke2fs 1.42.9 (28-Dec-2013)
/home/user/tempfile_for_article.dd is not a block special device.
Proceed anyway? (y,n) y
...
Writing superblocks and filesystem accounting information: done
[user@localhost ~]$
Připojte souborový systém:
[user@localhost ~]$ sudo mount ~/tempfile_for_article.dd /mnt/
[sudo] password for user:
[user@localhost ~]$ df -h | grep mnt
/dev/loop0 8.7M 172K 7.9M 3% /mnt
Vytvoříme adresář s naším vlastníkem:
[user@localhost ~]$ sudo mkdir /mnt/logs
[user@localhost ~]$ sudo chown user: /mnt/logs
Otevřeme soubor pouze pro zápis v našem programu:
with open("/mnt/logs/123.txt", "w") as f:
Běh
[user@localhost ]$ python openforwrite.py
Počkáme několik sekund
[user@localhost ~]$ df -h | grep mnt
/dev/loop0 8.7M 8.0M 0 100% /mnt
Takže máme problém popsaný na začátku tohoto článku. Volné místo 0, 100 % obsazeno.
Pamatujeme si, že podle podmínek úlohy se snažíme zaznamenat velmi důležitá data, která nelze ztratit. A zároveň musíme opravit službu bez restartování procesu.
Řekněme, že máme stále místo na disku, ale v jiném oddílu, například v /home.
Zkusme „přeprogramovat za běhu“ náš kód.
Podívejme se na PID našeho procesu, který zabral veškerý diskový prostor:
[user@localhost ~]$ ps axuf | grep [o]penfor
user 10078 27.2 0.0 128600 5744 pts/22 R+ 11:06 0:02 | _ python openforwrite.py
Připojte se k procesu přes gdb
[user@localhost ~]$ gdb -p 10078
...
(gdb)
Podívejme se na otevřené popisovače souborů:
(gdb) shell ls -lah /proc/10078/fd/
total 0
dr-x------ 2 user user 0 Oct 8 11:06 .
dr-xr-xr-x 9 user user 0 Oct 8 11:06 ..
lrwx------ 1 user user 64 Oct 8 11:09 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:09 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:06 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:09 3 -> /mnt/logs/123.txt
Podíváme se na informace o deskriptoru souboru číslo 3, které nás zajímají
(gdb) shell cat /proc/10078/fdinfo/3
pos: 8189952
flags: 0100001
mnt_id: 482
S ohledem na to, jaké systémové volání Python provádí (viz výše, kde jsme spustili strace a našli otevřené volání), při zpracovávání našeho kódu k otevření souboru děláme totéž sami jménem našeho procesu, ale potřebujeme O_WRONLY|O_CREAT| Bity O_TRUNC nahrazují číselnou hodnotou. Chcete-li to provést, otevřete například zdrojové kódy jádra
#define O_WRONLY 00000001
#define O_CREAT 00000100
#define O_TRUNC 00001000
Spojíme všechny hodnoty do jedné, dostaneme 00001101
Spouštíme náš hovor z gdb
(gdb) call open("/home/user/123.txt", 00001101,0666)
$1 = 4
Takže máme nový deskriptor souboru s číslem 4 a nový otevřený soubor na jiném oddílu, zkontrolujeme:
(gdb) shell ls -lah /proc/10078/fd/
total 0
dr-x------ 2 user user 0 Oct 8 11:06 .
dr-xr-xr-x 9 user user 0 Oct 8 11:06 ..
lrwx------ 1 user user 64 Oct 8 11:09 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:09 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:06 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:09 3 -> /mnt/logs/123.txt
l-wx------ 1 user user 64 Oct 8 11:15 4 -> /home/user/123.txt
Pamatujeme si příklad s pipe - jak bash mění deskriptory souborů, a už jsme se naučili systémové volání dup2.
Snažíme se nahradit jeden deskriptor souboru jiným
(gdb) call dup2(4,3)
$2 = 3
Zkontrolujeme:
(gdb) shell ls -lah /proc/10078/fd/
total 0
dr-x------ 2 user user 0 Oct 8 11:06 .
dr-xr-xr-x 9 user user 0 Oct 8 11:06 ..
lrwx------ 1 user user 64 Oct 8 11:09 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:09 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:06 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:09 3 -> /home/user/123.txt
l-wx------ 1 user user 64 Oct 8 11:15 4 -> /home/user/123.txt
Zavřeme deskriptor souboru 4, protože jej nepotřebujeme:
(gdb) call close (4)
$1 = 0
A ukončete gdb
(gdb) quit
A debugging session is active.
Inferior 1 [process 10078] will be detached.
Quit anyway? (y or n) y
Detaching from program: /usr/bin/python2.7, process 10078
Kontrola nového souboru:
[user@localhost ~]$ ls -lah /home/user/123.txt
-rw-rw-r-- 1 user user 5.1M Oct 8 11:18 /home/user/123.txt
[user@localhost ~]$ ls -lah /home/user/123.txt
-rw-rw-r-- 1 user user 7.1M Oct 8 11:18 /home/user/123.txt
Jak vidíte, data se zapisují do nového souboru, zkontrolujeme ten starý:
[user@localhost ~]$ ls -lah /mnt/logs/123.txt
-rw-rw-r-- 1 user user 7.9M Oct 8 11:08 /mnt/logs/123.txt
Žádná data se neztratí, aplikace funguje, protokoly se zapisují do nového umístění.
Pojďme si úkol trochu zkomplikovat
Představme si, že jsou pro nás data důležitá, ale v žádném z oddílů nemáme místo na disku a nemůžeme disk připojit.
Co můžeme udělat, je přesměrovat naše data někam, například do potrubí, a naopak přesměrovat data z potrubí do sítě pomocí nějakého programu, například netcat.
Pojmenovanou rouru můžeme vytvořit pomocí příkazu mkfifo. Vytvoří pseudo soubor v systému souborů, i když na něm není volné místo.
Restartujte aplikaci a zkontrolujte:
[user@localhost ]$ python openforwrite.py
[user@localhost ~]$ ps axuf | grep [o]pen
user 5946 72.9 0.0 128600 5744 pts/22 R+ 11:27 0:20 | _ python openforwrite.py
[user@localhost ~]$ ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user 0 Oct 8 11:27 .
dr-xr-xr-x 9 user user 0 Oct 8 11:27 ..
lrwx------ 1 user user 64 Oct 8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:28 3 -> /mnt/logs/123.txt
[user@localhost ~]$ df -h | grep mnt
/dev/loop0 8.7M 8.0M 0 100% /mnt
Na disku není místo, ale úspěšně jsme tam vytvořili pojmenovaný kanál:
[user@localhost ~]$ mkfifo /mnt/logs/megapipe
[user@localhost ~]$ ls -lah /mnt/logs/megapipe
prw-rw-r-- 1 user user 0 Oct 8 11:28 /mnt/logs/megapipe
Nyní musíme nějak zabalit všechna data, která jdou do této roury na jiný server přes síť, k tomu je vhodný stejný netcat.
Na serveru remote-server.example.com běžíme
[user@localhost ~]$ nc -l 7777 > 123.txt
Na našem problematickém serveru spouštíme v samostatném terminálu
[user@localhost ~]$ nc remote-server.example.com 7777 < /mnt/logs/megapipe
Nyní všechna data, která skončí v rouře, půjdou automaticky do stdin v netcat, který je pošle do sítě na portu 7777.
Jediné, co musíme udělat, je začít zapisovat naše data do této pojmenované roury.
Aplikaci již máme spuštěnou:
[user@localhost ~]$ ps axuf | grep [o]pen
user 5946 99.8 0.0 128600 5744 pts/22 R+ 11:27 169:27 | _ python openforwrite.py
[user@localhost ~]$ ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user 0 Oct 8 11:27 .
dr-xr-xr-x 9 user user 0 Oct 8 11:27 ..
lrwx------ 1 user user 64 Oct 8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:28 3 -> /mnt/logs/123.txt
Ze všech příznaků potřebujeme pouze O_WRONLY, protože soubor již existuje a nemusíme jej mazat
[user@localhost ~]$ gdb -p 5946
...
(gdb) call open("/mnt/logs/megapipe", 00000001,0666)
$1 = 4
(gdb) shell ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user 0 Oct 8 11:27 .
dr-xr-xr-x 9 user user 0 Oct 8 11:27 ..
lrwx------ 1 user user 64 Oct 8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:28 3 -> /mnt/logs/123.txt
l-wx------ 1 user user 64 Oct 8 14:20 4 -> /mnt/logs/megapipe
(gdb) call dup2(4,3)
$2 = 3
(gdb) shell ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user 0 Oct 8 11:27 .
dr-xr-xr-x 9 user user 0 Oct 8 11:27 ..
lrwx------ 1 user user 64 Oct 8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:28 3 -> /mnt/logs/megapipe
l-wx------ 1 user user 64 Oct 8 14:20 4 -> /mnt/logs/megapipe
(gdb) call close(4)
$3 = 0
(gdb) shell ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user 0 Oct 8 11:27 .
dr-xr-xr-x 9 user user 0 Oct 8 11:27 ..
lrwx------ 1 user user 64 Oct 8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:28 3 -> /mnt/logs/megapipe
(gdb) quit
A debugging session is active.
Inferior 1 [process 5946] will be detached.
Quit anyway? (y or n) y
Detaching from program: /usr/bin/python2.7, process 5946
Kontrola vzdáleného serveru remote-server.example.com
[user@localhost ~]$ ls -lah 123.txt
-rw-rw-r-- 1 user user 38M Oct 8 14:21 123.txt
Data přicházejí, zkontrolujeme problémový server
[user@localhost ~]$ ls -lah /mnt/logs/
total 7.9M
drwxr-xr-x 2 user user 1.0K Oct 8 11:28 .
drwxr-xr-x 4 root root 1.0K Oct 8 10:55 ..
-rw-rw-r-- 1 user user 7.9M Oct 8 14:17 123.txt
prw-rw-r-- 1 user user 0 Oct 8 14:22 megapipe
Data jsou uložena, problém je vyřešen.
Využívám této příležitosti, abych pozdravil své kolegy z Degira.
Poslouchejte podcasty Radio T.
Dobré pro všechny.
Jako domácí úkol vám doporučuji zamyslet se nad tím, co bude v procesních deskriptorech cat a sleep, pokud spustíte následující příkaz:
[user@localhost ~]$ cat /dev/zero 2>/dev/null| sleep 10000
Zdroj: www.habr.com