Deskriptor súboru v Linuxe s príkladmi

Raz som sa v rozhovore spýtal, čo by ste urobili, keby ste našli nefunkčnú službu kvôli tomu, že na disku došlo miesto?

Samozrejme som odpovedal, že uvidím, čo toto miesto robí a ak to bude možné, miesto vyčistím.
Potom sa anketár spýtal, čo ak na oddiele nie je voľné miesto, ale nevidíte ani súbory, ktoré by zaberali celý priestor?

Na to som povedal, že vždy sa môžete pozrieť na otvorené deskriptory súborov, napríklad pomocou príkazu lsof, a pochopiť, ktorá aplikácia zabrala všetok dostupný priestor, a potom môžete konať podľa okolností v závislosti od toho, či sú potrebné údaje. .

Anketár ma prerušil pri poslednom slove a dodal na svoju otázku: „Predpokladajme, že nepotrebujeme údaje, je to len denník ladenia, ale aplikácia nefunguje, pretože nemôže zapísať ladenie“?

"OK," odpovedal som, "môžeme vypnúť ladenie v konfigurácii aplikácie a reštartovať ju."
Anketár namietal: „Nie, nemôžeme reštartovať aplikáciu, stále máme dôležité dáta v pamäti a k ​​samotnej službe sú pripojení dôležití klienti, ktorých nemôžeme prinútiť znovu sa pripojiť.“

"Dobre," povedal som, "ak nemôžeme reštartovať aplikáciu a nestaráme sa o dáta, potom môžeme jednoducho vyčistiť tento otvorený súbor cez deskriptor súboru, aj keď ho nevidíme v ls príkaz v súborovom systéme."

Anketár bol spokojný, ale ja nie.

Potom som si pomyslel, prečo človek, ktorý testuje moje vedomosti, nepátra hlbšie? Čo ak sú však údaje predsa len dôležité? Čo ak nemôžeme reštartovať proces a zároveň tento proces zapisuje do súborového systému na oddiel, ktorý nemá voľné miesto? Čo ak nemôžeme stratiť nielen už zapísané dáta, ale ani dáta, ktoré tento proces zapisuje alebo sa pokúša zapísať?

Tuzik

Na začiatku mojej kariéry som sa snažil vytvoriť malú aplikáciu, ktorá potrebovala uchovávať informácie o používateľoch. A potom som si pomyslel, ako môžem priradiť používateľa k jeho údajom. Napríklad mám Ivanova Ivana Ivanoviča a má nejaké údaje, ale ako sa s nimi spriateliť? Môžem priamo podotknúť, že pes menom "Tuzik" patrí tomu istému Ivanovi. Čo ak si však zmení meno a namiesto Ivana sa stane napríklad Olya? Potom sa ukáže, že naša Olya Ivanovna Ivanova už nebude mať psa a náš Tuzik bude stále patriť neexistujúcemu Ivanovi. Tento problém pomohla vyriešiť databáza, ktorá každému používateľovi pridelila jedinečný identifikátor (ID) a môj Tuzik bol naviazaný na toto ID, ktoré bolo v skutočnosti len sériové číslo. Majiteľ tuziku mal teda ID číslo 2 a v určitom čase bol pod týmto ID Ivan a potom sa pod rovnakým ID stala Olya. Problém ľudstva a chovu zvierat bol prakticky vyriešený.

Deskriptor súboru

Problém súboru a programu, ktorý s týmto súborom pracuje, je asi rovnaký ako u nášho psa a človeka. Predpokladajme, že som otvoril súbor s názvom ivan.txt a začal som doň písať slovo tuzik, no podarilo sa mi do súboru napísať len prvé písmeno „t“ a tento súbor niekto premenoval napríklad na olya.txt. Spis je ale ten isty a aj tak do neho chcem napisat svoje eso. Zakaždým, keď otvoríte súbor pomocou systémového volania otvoriť v akomkoľvek programovacom jazyku dostanem jedinečné ID, ktoré ma nasmeruje na súbor, toto ID je deskriptor súboru. A je úplne jedno, čo a kto s týmto súborom ďalej urobí, dá sa zmazať, premenovať, môže zmeniť majiteľa alebo odobrať práva na čítanie a zápis, stále k nemu budem mať prístup, pretože v čase otvorenia súboru som mal práva na jeho čítanie a/alebo zápis a podarilo sa mi s ním začať pracovať, čo znamená, že v tom musím pokračovať.

V systéme Linux knižnica libc otvára 3 súbory deskriptorov pre každú spustenú aplikáciu (proces) s číslami 0,1,2. Viac informácií nájdete na odkazoch man stdio и muž stdout

  • Deskriptor súboru 0 sa nazýva STDIN a je spojený so vstupom aplikácie.
  • Deskriptor súboru 1 sa nazýva STDOUT a používajú ho výstupné aplikácie, ako sú tlačové príkazy.
  • Deskriptor súboru 2 má názov STDERR a používajú ho aplikácie na výstup chybových správ.

Ak vo svojom programe otvoríte akýkoľvek súbor na čítanie alebo zápis, potom s najväčšou pravdepodobnosťou dostanete prvé bezplatné ID a bude to číslo 3.

Môžete vidieť zoznam deskriptorov súborov pre každý proces, ak poznáte jeho PID.

Otvorme napríklad konzolu s bashom a pozrime si PID nášho procesu

[user@localhost ]$ echo $$
15771

V druhej konzole spustite

[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

Deskriptor súboru s číslom 255 môžete v rámci tohto článku pokojne ignorovať, otvoril ho pre vaše potreby samotný bash a nie prepojená knižnica.

Teraz sú všetky 3 súbory deskriptorov spojené s pseudoterminálnym zariadením /dev/pts, no stále s nimi môžeme manipulovať, napríklad spustiť v druhej konzole

[user@localhost ]$ echo "hello world" > /proc/15771/fd/0

A v prvej konzole uvidíme

[user@localhost ]$ hello world

Presmerovanie a Pipe

Tieto 3 súbory deskriptorov môžete jednoducho prepísať v akomkoľvek procese, vrátane bash, napríklad prostredníctvom potrubia (pipe) spájajúceho dva procesy, pozri

[user@localhost ]$ cat /dev/zero | sleep 10000

Tento príkaz môžete spustiť sami strace -f a uvidíte, čo sa deje vo vnútri, ale skrátim to.

Náš nadradený bash proces s PID 15771 analyzuje náš príkaz a presne chápe, koľko príkazov chceme spustiť, v našom prípade sú dva: mačka a spánok. Bash vie, že potrebuje vytvoriť dva podradené procesy a zlúčiť ich do jedného potrubia. Celkovo bude bash potrebovať 2 podradené procesy a jedno potrubie.

Pred vytvorením podriadených procesov bash spustí systémové volanie rúrka a prijíma nové deskriptory súborov do dočasnej vyrovnávacej pamäte, ale táto vyrovnávacia pamäť ešte žiadnym spôsobom nespája naše dva podriadené procesy.

V prípade nadradeného procesu sa zdá, že kanál už existuje, ale zatiaľ neexistujú žiadne podriadené 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

Potom pomocou systémového volania klonovať bash vytvorí dva podradené procesy a naše tri procesy budú vyzerať 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

Nezabudnite, že klon klonuje proces spolu so všetkými deskriptormi súborov, takže budú rovnaké v nadradenom procese aj v podradenom procese. Úlohou nadradeného procesu s PID 15771 je monitorovať podriadené procesy, takže len čaká na odpoveď od potomkov.

Preto nepotrebuje fajku a deskriptory súborov zatvorí číslami 3 a 4.

V prvom bash podriadenom procese s PID 9004 systémové volanie dup2, zmení náš deskriptor súboru STDOUT číslo 1 na deskriptor súboru smerujúci na potrubie, v našom prípade je to číslo 3. Takže všetko, čo prvý podriadený proces s PID 9004 zapíše do STDOUT, automaticky spadne do vyrovnávacej pamäte potrubia.

V druhom podradenom procese s PID 9005 bash dup2s súbor do STDIN deskriptora číslo 0. Teraz všetko, čo bude čítať náš druhý bash s PID 9005, bude čítať z potrubia.

Potom sa deskriptory súborov s číslami 3 a 4 uzavrú aj v podriadených procesoch, pretože sa už nepoužívajú.

Zámerne ignorujem deskriptor súboru 255, používa ho interne samotný bash a bude tiež uzavretý v podriadených procesoch.

Ďalej, v prvom podradenom procese s PID 9004 bash začína systémovým volaním exec spustiteľný súbor, ktorý sme zadali na príkazovom riadku, v našom prípade je to /usr/bin/cat.

V druhom podradenom procese s PID 9005 bash spustí druhý spustiteľný súbor, ktorý sme špecifikovali, v našom prípade /usr/bin/sleep.

Systémové volanie exec nezatvorí deskriptory súborov, pokiaľ neboli otvorené s príznakom O_CLOEXEC v čase, keď bolo otvorené volanie vykonané. V našom prípade sa po spustení spustiteľných súborov uložia všetky aktuálne deskriptory súborov.

Kontrola v konzole:

[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

Ako vidíte, jedinečné číslo našej fajky je v oboch procesoch rovnaké. Máme teda spojenie medzi dvoma rôznymi procesmi s tým istým rodičom.

Pre tých, ktorí nie sú oboznámení so systémovými volaniami, ktoré bash používa, veľmi odporúčam spúšťať príkazy cez strace a zistiť, čo sa deje vo vnútri, napríklad takto:

strace -s 1024 -f bash -c "ls | grep hello"

Vráťme sa k nášmu problému s nedostatkom miesta na disku a pokusom o záchranu dát bez reštartovania procesu. Napíšme malý program, ktorý bude zapisovať na disk rýchlosťou asi 1 megabajt za sekundu. Navyše, ak by sme z nejakého dôvodu nemohli zapisovať dáta na disk, jednoducho to ignorujeme a pokúsime sa dáta zapísať znova za sekundu. V príklade, ktorý používam Python, môžete použiť akýkoľvek iný 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

Spustite program a pozrite sa na deskriptory súborov

[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

Ako môžete vidieť, máme 3 štandardné deskriptory súborov a ďalší, ktorý sme otvorili. Skontrolujeme veľkosť súboru:

[user@localhost ]$ ls -lah 123.txt 
-rw-rw-r-- 1 user user 117M Oct  7 16:30 123.txt

údaje sú zapísané, snažíme sa zmeniť práva k súboru:

[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 údaje sa stále zapisujú, hoci náš používateľ nemá právo zapisovať do súboru. Skúsme to odstrániť:

[user@localhost ]$ sudo rm 123.txt 
[user@localhost ]$ ls 123.txt
ls: cannot access 123.txt: No such file or directory

Kde sú zapísané údaje? A sú vôbec napísané? 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)

Áno, náš deskriptor súboru stále existuje a môžeme s ním pracovať ako s naším starým súborom, môžeme ho čítať, čistiť a kopírovať.

Pozrite sa na veľkosť súboru:

[user@localhost ]$ lsof | grep 123.txt
python    31083             user    3w      REG                8,5   19923457   2621522 /home/user/123.txt

Veľkosť súboru je 19923457. Pokus o vymazanie súboru:

[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

Ako vidíte, veľkosť súboru sa len zväčšuje a náš kmeň nefungoval. Obráťme sa na dokumentáciu k systémovému volaniu otvoriť. Ak pri otváraní súboru použijeme príznak O_APPEND, tak pri každom zápise operačný systém skontroluje veľkosť súboru a zapíše dáta až na úplný koniec súboru, a to atomicky. To umožňuje viacerým vláknam alebo procesom zapisovať do rovnakého súboru. Ale v našom kóde tento príznak nepoužívame. Odlišnú veľkosť súboru v lsof za kmeňom môžeme vidieť iba vtedy, ak súbor otvoríme na zápis, čo znamená, že v našom kóde namiesto

with open("123.txt", "w") as f:

musíme dať

with open("123.txt", "a") as f:

Kontrola s príznakom "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

Programovanie už spusteného procesu

Programátori často pri vytváraní a testovaní programu používajú debuggery (napríklad GDB) alebo rôzne úrovne logovania v aplikácii. Linux poskytuje možnosť skutočne písať a meniť už spustený program, ako je zmena hodnôt premenných, nastavenie bodu prerušenia atď.

Ak sa vrátime k pôvodnej otázke o nedostatku miesta na disku na zápis súboru, skúsme problém nasimulovať.

Vytvorme súbor pre náš oddiel, ktorý pripojíme ako 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 ~]$

Vytvorme súborový 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 ~]$

Pripojíme súborový 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

Vytvorte si adresár s naším vlastníkom:

[user@localhost ~]$ sudo mkdir /mnt/logs
[user@localhost ~]$ sudo chown user: /mnt/logs

Otvorme súbor na zápis iba v našom programe:

with open("/mnt/logs/123.txt", "w") as f:

Spustiť

[user@localhost ]$ python openforwrite.py 

Čaká sa niekoľko sekúnd

[user@localhost ~]$ df -h | grep mnt
/dev/loop0      8.7M  8.0M     0 100% /mnt

Takže sme dostali problém popísaný na začiatku tohto článku. Voľné miesto 0, obsadené na 100 %.

Pamätáme si, že podľa podmienok problému sa snažíme zaznamenať veľmi dôležité údaje, ktoré nemožno stratiť. A pritom musíme službu opraviť bez reštartovania procesu.

Povedzme, že stále máme miesto na disku, ale v inom oddiele, napríklad v / home.

Skúsme "preprogramovať za behu" náš kód.

Pozeráme sa na PID nášho procesu, ktorý zjedol celý priestor na disku:

[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

Pripojenie k procesu s gdb

[user@localhost ~]$ gdb -p 10078
...
(gdb) 

Pozeráme sa na otvorené deskriptory súborov:

(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

Pozeráme sa na informácie o deskriptore súboru s číslom 3, ktorý nás zaujíma

(gdb) shell cat /proc/10078/fdinfo/3
pos:    8189952
flags:  0100001
mnt_id: 482

Majme na pamäti, aké systémové volanie robí Python (pozri vyššie, kde sme spustili strace a našli otvorené volanie), pri spracovaní nášho kódu na otvorenie súboru robíme to isté sami v mene nášho procesu, ale potrebujeme O_WRONLY|O_CREAT| Bity O_TRUNC nahradia číselnou hodnotou. Ak to chcete urobiť, otvorte napríklad zdrojové kódy jadra tu a uvidíte, ktoré vlajky sú za čo zodpovedné

#define O_WRONLY 00000001
#define O_CREAT 00000100
#define O_TRUNC 00001000

Skombinujeme všetky hodnoty do jednej, dostaneme 00001101

Spustenie nášho hovoru z gdb

(gdb) call open("/home/user/123.txt", 00001101,0666)
$1 = 4

Takže máme nový deskriptor súboru s číslom 4 a nový otvorený súbor na inom oddiele, skontrolujte:

(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

Pamätáme si príklad s pipe - ako bash mení deskriptory súborov a už sme sa naučili systémové volanie dup2.

Pokúšame sa nahradiť jeden deskriptor súboru iným

(gdb) call dup2(4,3)
$2 = 3

skontrolujte:

(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

Zatvorte deskriptor súboru 4, pretože ho nepotrebujeme:

(gdb) call close (4)
$1 = 0

A ukončite 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 súboru:

[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

Ako vidíte, údaje sa zapisujú do nového súboru, skontrolujeme 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

Údaje sa nestratia, aplikácia funguje, protokoly sa zapisujú na nové miesto.

Poďme si veci trochu sťažiť

Predstavte si, že dáta sú pre nás dôležité, no v žiadnom z oddielov nemáme miesto na disku a nevieme disk pripojiť.

Čo môžeme urobiť, je presmerovať naše údaje niekam, napríklad do potrubia, a presmerovať údaje z potrubia do siete pomocou nejakého programu, ako je napríklad netcat.
Pomenované potrubie môžeme vytvoriť pomocou príkazu mkfifo. V súborovom systéme vytvorí pseudo súbor, aj keď na ňom nie je voľné miesto.

Reštartujte aplikáciu a skontrolujte:

[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 nie je miesto, ale úspešne sme tam vytvorili pomenované prepojenie:

[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

Teraz musíme nejakým spôsobom zabaliť všetky údaje, ktoré sa dostanú do tohto potrubia, na iný server cez sieť, na to je vhodný rovnaký netcat.

Na serveri remote-server.example.com spustite

[user@localhost ~]$ nc -l 7777 > 123.txt 

Na našom problémovom serveri spustite v samostatnom termináli

[user@localhost ~]$ nc remote-server.example.com 7777 < /mnt/logs/megapipe 

Teraz všetky údaje, ktoré sa dostanú do potrubia, automaticky prejdú na stdin v netcat, ktorý ich odošle do siete na port 7777.

Jediné, čo musíme urobiť, je začať zapisovať naše údaje do tohto pomenovaného potrubia.

Už máme spustenú aplikáciu:

[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

Zo všetkých príznakov potrebujeme iba O_WRONLY, pretože súbor už existuje a nemusíme ho vymazávať

[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 vzdialeného servera remote-server.example.com

[user@localhost ~]$ ls -lah 123.txt 
-rw-rw-r-- 1 user user 38M Oct  8 14:21 123.txt

Prichádzajú dáta, skontrolujeme 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

Dáta sú uložené, problém je vyriešený.

Využívam túto príležitosť, aby som pozdravil svojich kolegov z Degiro.
Počúvajte podcasty Radio-T.

Všetko dobré.

Ako domácu úlohu navrhujem zamyslieť sa nad tým, čo bude v deskriptoroch súborov procesu mačky a spánku, ak spustíte nasledujúci príkaz:

[user@localhost ~]$ cat /dev/zero 2>/dev/null| sleep 10000

Zdroj: hab.com

Pridať komentár