Popisovač souborů v Linuxu s příklady

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 otevřít v jakémkoli programovacím jazyce obdržím jedinečné ID, které mě nasměruje na soubor, toto ID je deskriptor souboru. A je úplně jedno, co a kdo s tímto souborem dál udělá, dá se smazat, přejmenovat, změnit vlastníka nebo odebrat práva na čtení a zápis, stále budu mít přístup k němu, protože v době otevření souboru jsem měl práva jej číst a/nebo zapisovat a podařilo se mi s ním začít pracovat, což znamená, že v tom musím pokračovat.

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 muž stdio и muž stdout

  • 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 /dev/pts, ale stále s nimi můžeme manipulovat, například je spouštět v druhé konzoli

[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í trubka a přijímá nové deskriptory souborů v dočasné vyrovnávací paměti potrubí, ale tato vyrovnávací paměť ještě nepropojuje naše dva podřízené procesy.

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í klonovat bash vytvoří dva podřízené procesy a naše tři procesy budou vypadat takto:

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í dup2, změní náš deskriptor souboru STDOUT číslo 1 na deskriptor souboru ukazující na rouru, v našem případě je to číslo 3. Tedy vše, co první podřízený proces s PID 9004 zapíše do STDOUT, automaticky skončí ve vyrovnávací paměti roury.

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í exec spustitelný soubor, který jsme zadali na příkazovém řádku, v našem případě je to /usr/bin/cat.

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í otevřít. Pokud při otevírání souboru použijeme příznak O_APPEND, pak při každém zápisu operační systém zkontroluje velikost souboru a zapíše data až na úplný konec souboru, a to atomicky. To umožňuje více vláknům nebo procesům zapisovat do stejného souboru. Ale v našem kódu tento příznak nepoužíváme. Odlišnou velikost souboru v lsof za kmenem můžeme vidět pouze v případě, že soubor otevřeme pro další zápis, což znamená místo toho v našem kódu

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 zde a podívejte se, které vlajky jsou za co zodpovědné

#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

Přidat komentář