Odată, în timpul unui interviu, am fost întrebat, ce veți face dacă găsiți un serviciu care nu funcționează din cauza faptului că discul a rămas fără spațiu?
Bineînțeles, i-am răspuns că o să văd ce este ocupat de acest loc și, dacă se poate, o să curăț locul.
Apoi intervievatorul a întrebat, ce se întâmplă dacă nu există spațiu liber pe partiție, dar nici nu vedeți niciun fișier care ar ocupa tot spațiul?
La aceasta am spus că poți oricând să te uiți la descriptori de fișiere deschise, de exemplu cu comanda lsof, și să înțelegi care aplicație a ocupat tot spațiul disponibil, iar apoi poți acționa în funcție de circumstanțe, în funcție de necesitatea datelor. .
Intervievatorul m-a întrerupt la ultimul cuvânt, adăugând la întrebarea lui: „Să presupunem că nu avem nevoie de date, este doar un jurnal de depanare, dar aplicația nu funcționează pentru că nu poate scrie o depanare”?
„Bine”, am răspuns, „putem dezactiva depanarea în configurația aplicației și o putem reporni.”
Intervievatorul a obiectat: „Nu, nu putem reporni aplicația, avem încă date importante stocate în memorie, iar clienții importanți sunt conectați la serviciul în sine, pe care nu îl putem forța să-l reconectam din nou”.
„bine”, am spus, „dacă nu putem reporni aplicația și datele nu sunt importante pentru noi, atunci pur și simplu putem șterge acest fișier deschis prin descriptorul de fișier, chiar dacă nu îl vedem în comanda ls. pe sistemul de fișiere.”
Intervievatorul a fost mulțumit, dar eu nu.
Apoi m-am gândit, de ce persoana care îmi testează cunoștințele nu sapă mai adânc? Dar dacă datele sunt importante până la urmă? Ce se întâmplă dacă nu putem reporni un proces, iar procesul scrie în sistemul de fișiere pe o partiție care nu are spațiu liber? Ce se întâmplă dacă nu putem pierde nu numai datele care au fost deja scrise, ci și datele pe care acest proces le scrie sau încearcă să le scrie?
Pălărie mare
La începutul carierei mele, am încercat să creez o mică aplicație care trebuia să stocheze informații despre utilizator. Și apoi m-am gândit, cum pot potrivi utilizatorul cu datele lui. De exemplu, îl am pe Ivanov Ivan Ivanovici și el are câteva informații, dar cum pot să mă împrietenesc cu ei? Pot să subliniez direct că câinele numit „Tuzik” îi aparține tocmai acestui Ivan. Dar dacă își schimbă numele și în loc de Ivan devine, de exemplu, Olya? Atunci se va dovedi că Olya Ivanovna Ivanova a noastră nu va mai avea un câine, iar Tuzik-ul nostru va aparține în continuare Ivan inexistentului. O bază de date care a oferit fiecărui utilizator un identificator unic (ID) a ajutat la rezolvarea acestei probleme, iar Tuzik-ul meu a fost legat de acest ID, care, de fapt, era doar un număr de serie. Astfel, proprietarul asului avea numărul de identitate 2, iar la un moment dat Ivan a fost sub acest ID, iar apoi Olya a devenit sub același ID. Problema umanității și a creșterii animalelor a fost practic rezolvată.
Descriptor de fișier
Problema fișierului și a programului care funcționează cu acest fișier este aproximativ aceeași cu cea a câinelui și a omului nostru. Să presupunem că am deschis un fișier numit ivan.txt și am început să scriu cuvântul tuzik în el, dar am reușit să scriu doar prima literă „t” în fișier, iar acest fișier a fost redenumit de cineva, de exemplu, în olya.txt. Dar dosarul rămâne același și tot vreau să-mi înregistrez asul în el. De fiecare dată când un fișier este deschis printr-un apel de sistem
În Linux, biblioteca libc deschide 3 fișiere descriptor pentru fiecare aplicație (proces) care rulează, numerotate 0,1,2. Mai multe informații pot fi găsite pe link-uri
- Descriptorul de fișier 0 se numește STDIN și este asociat cu introducerea aplicației
- Descriptorul de fișier 1 se numește STDOUT și este folosit de aplicații pentru a scoate date, cum ar fi comenzile de imprimare
- Descriptorul de fișier 2 se numește STDERR și este folosit de aplicații pentru a afișa mesaje de eroare.
Dacă în programul dvs. deschideți orice fișier pentru citire sau scriere, atunci cel mai probabil veți obține primul ID gratuit și va fi numărul 3.
Lista descriptorilor de fișiere poate fi vizualizată pentru orice proces dacă îi cunoașteți PID.
De exemplu, să deschidem consola bash și să ne uităm la PID-ul procesului nostru
[user@localhost ]$ echo $$
15771
În a doua consolă să alergăm
[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
Puteți ignora în siguranță descriptorul de fișier numărul 255 în scopul acestui articol; acesta a fost deschis pentru nevoile sale de bash în sine, și nu de biblioteca conectată.
Acum toate cele 3 fișiere descriptor sunt asociate cu dispozitivul pseudo terminal
[user@localhost ]$ echo "hello world" > /proc/15771/fd/0
Și în prima consolă vom vedea
[user@localhost ]$ hello world
Redirecționare și țeavă
Puteți suprascrie cu ușurință aceste 3 fișiere descriptor în orice proces, inclusiv în bash, de exemplu printr-o conductă care conectează două procese, vezi
[user@localhost ]$ cat /dev/zero | sleep 10000
Puteți rula singur această comandă strace -f și vezi ce se întâmplă înăuntru, dar îți voi spune pe scurt.
Procesul nostru părinte bash cu PID 15771 analizează comanda noastră și înțelege exact câte comenzi vrem să rulăm, în cazul nostru există două dintre ele: cat și sleep. Bash știe că trebuie să creeze două procese copil și să le îmbine într-o singură conductă. În total, bash va avea nevoie de 2 procese copil și o conductă.
Bash rulează un apel de sistem înainte de a crea procese copil
Pentru procesul părinte, se pare că există deja o conductă, dar încă nu există procese secundare:
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
Apoi, folosind apelul de sistem
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
Nu uitați că clonarea clonează procesul împreună cu toți descriptorii de fișiere, astfel încât aceștia vor fi la fel în procesul părinte și în cel copil. Sarcina procesului părinte cu PID 15771 este de a monitoriza procesele copil, așa că așteaptă pur și simplu un răspuns din partea copiilor.
Prin urmare, nu are nevoie de pipe și închide descriptorii de fișier numerotați 3 și 4.
În primul proces de bash copil cu PID 9004, apelul de sistem
În al doilea proces copil cu PID 9005, bash folosește dup2 pentru a schimba descriptorul de fișier STDIN numărul 0. Acum tot ceea ce va citi al doilea bash cu PID 9005 va fi citit din conductă.
După aceasta, descriptorii de fișiere numerotați 3 și 4 sunt, de asemenea, închise în procesele copil, deoarece nu mai sunt utilizați.
Ignorez în mod deliberat descriptorul de fișier 255; este folosit în scopuri interne de bash în sine și va fi, de asemenea, închis în procesele copil.
Apoi, în primul proces copil cu PID 9004, bash începe să folosească un apel de sistem
În al doilea proces copil cu PID 9005, bash rulează al doilea executabil pe care l-am specificat, în cazul nostru /usr/bin/sleep.
Apelul de sistem exec nu închide mânerele fișierelor decât dacă acestea au fost deschise cu indicatorul O_CLOEXEC în momentul în care a fost efectuat apelul deschis. În cazul nostru, după lansarea fișierelor executabile, toți descriptorii de fișiere actuali vor fi salvați.
Verificați în consolă:
[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
După cum puteți vedea, numărul unic al țevii noastre este același în ambele procese. Astfel avem o legătură între două procese diferite cu același părinte.
Pentru cei care nu sunt familiarizați cu apelurile de sistem pe care le folosește bash, recomand să rulați comenzile prin strace și să vedeți ce se întâmplă în interior, de exemplu, astfel:
strace -s 1024 -f bash -c "ls | grep hello"
Să revenim la problema noastră cu spațiul redus pe disc și încercarea de a salva date fără a reporni procesul. Să scriem un mic program care va scrie aproximativ 1 megaoctet pe secundă pe disc. Mai mult, dacă din anumite motive nu am reușit să scriem date pe disc, pur și simplu vom ignora acest lucru și vom încerca să scriem din nou datele într-o secundă. În exemplul în care folosesc Python, puteți folosi orice alt limbaj de programare.
[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
Să rulăm programul și să ne uităm la descriptorii fișierelor
[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
După cum puteți vedea, avem cei 3 descriptori standard de fișiere și încă unul pe care l-am deschis. Să verificăm dimensiunea fișierului:
[user@localhost ]$ ls -lah 123.txt
-rw-rw-r-- 1 user user 117M Oct 7 16:30 123.txt
Datele sunt scrise, încercăm să schimbăm permisiunile pe fișier:
[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
Vedem că datele sunt încă în curs de scris, deși utilizatorul nostru nu are permisiunea de a scrie în fișier. Să încercăm să-l eliminăm:
[user@localhost ]$ sudo rm 123.txt
[user@localhost ]$ ls 123.txt
ls: cannot access 123.txt: No such file or directory
Unde sunt scrise datele? Și sunt scrise deloc? Verificăm:
[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)
Da, descriptorul nostru de fișier încă există și putem trata acest descriptor de fișier ca fișierul nostru vechi, îl putem citi, șterge și copia.
Să ne uităm la dimensiunea fișierului:
[user@localhost ]$ lsof | grep 123.txt
python 31083 user 3w REG 8,5 19923457 2621522 /home/user/123.txt
Dimensiunea fișierului este 19923457. Să încercăm să ștergem fișierul:
[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
După cum puteți vedea, dimensiunea fișierului crește doar și trunchiul nostru nu a funcționat. Să ne uităm la documentația apelului de sistem
with open("123.txt", "w") as f:
trebuie să punem
with open("123.txt", "a") as f:
Verificarea cu steagul „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
și cu steagul „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
Programarea unui proces care rulează deja
Adesea, programatorii, atunci când creează și testează programe, folosesc depanatoare (de exemplu GDB) sau diferite niveluri de logare în aplicație. Linux oferă posibilitatea de a scrie și schimba efectiv un program care rulează deja, de exemplu, de a schimba valorile variabilelor, de a seta un punct de întrerupere etc., etc.
Revenind la întrebarea inițială despre spațiul pe disc insuficient pentru a scrie un fișier, să încercăm să simulăm problema.
Să creăm un fișier pentru partiția noastră, pe care îl vom monta ca un disc separat:
[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 ~]$
Să creăm un sistem de fișiere:
[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 ~]$
Montați sistemul de fișiere:
[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
Creăm un director cu proprietarul nostru:
[user@localhost ~]$ sudo mkdir /mnt/logs
[user@localhost ~]$ sudo chown user: /mnt/logs
Să deschidem fișierul pentru scriere doar în programul nostru:
with open("/mnt/logs/123.txt", "w") as f:
Lansa
[user@localhost ]$ python openforwrite.py
Așteptăm câteva secunde
[user@localhost ~]$ df -h | grep mnt
/dev/loop0 8.7M 8.0M 0 100% /mnt
Deci, avem problema descrisă la începutul acestui articol. Spatiu liber 0, 100% ocupat.
Ne amintim că, în funcție de condițiile sarcinii, încercăm să înregistrăm date foarte importante care nu pot fi pierdute. Și, în același timp, trebuie să reparăm serviciul fără a reporni procesul.
Să presupunem că mai avem spațiu pe disc, dar într-o altă partiție, de exemplu în /home.
Să încercăm să „reprogramăm din mers” codul nostru.
Să ne uităm la PID-ul procesului nostru, care a consumat tot spațiul pe disc:
[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
Conectați-vă la proces prin gdb
[user@localhost ~]$ gdb -p 10078
...
(gdb)
Să ne uităm la descriptorii fișierelor deschise:
(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
Ne uităm la informațiile despre descriptorul de fișier numărul 3, care ne interesează
(gdb) shell cat /proc/10078/fdinfo/3
pos: 8189952
flags: 0100001
mnt_id: 482
Ținând cont de ce apel de sistem face Python (vezi mai sus unde am rulat Strace și am găsit apelul deschis), atunci când procesăm codul nostru pentru a deschide un fișier, facem același lucru în numele procesului nostru, dar avem nevoie de O_WRONLY|O_CREAT| Biții O_TRUNC se înlocuiesc cu o valoare numerică. Pentru a face acest lucru, deschideți sursele nucleului, de exemplu
#define O_WRONLY 00000001
#define O_CREAT 00000100
#define O_TRUNC 00001000
Combinăm toate valorile într-una singură, obținem 00001101
Ne rulăm apelul de la gdb
(gdb) call open("/home/user/123.txt", 00001101,0666)
$1 = 4
Deci avem un nou descriptor de fișier cu numărul 4 și un nou fișier deschis pe o altă partiție, verificăm:
(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
Ne amintim exemplul cu pipe - cum bash modifică descriptorii fișierelor și am învățat deja apelul de sistem dup2.
Încercăm să înlocuim un descriptor de fișier cu altul
(gdb) call dup2(4,3)
$2 = 3
Verificăm:
(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
Închidem descriptorul de fișier 4, deoarece nu avem nevoie de el:
(gdb) call close (4)
$1 = 0
Și ieșiți din 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
Verificarea noului fișier:
[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
După cum puteți vedea, datele sunt scrise într-un fișier nou, să-l verificăm pe cel vechi:
[user@localhost ~]$ ls -lah /mnt/logs/123.txt
-rw-rw-r-- 1 user user 7.9M Oct 8 11:08 /mnt/logs/123.txt
Nu se pierd date, aplicația funcționează, jurnalele sunt scrise într-o nouă locație.
Să complicăm puțin sarcina
Să ne imaginăm că datele sunt importante pentru noi, dar nu avem spațiu pe disc în niciuna dintre partiții și nu putem conecta discul.
Ceea ce putem face este să ne redirecționăm datele undeva, de exemplu către pipe și, la rândul său, să redirecționăm datele de la pipe către rețea printr-un program, de exemplu netcat.
Putem crea o conductă numită cu comanda mkfifo. Va crea un pseudo fișier pe sistemul de fișiere chiar dacă nu există spațiu liber pe el.
Reporniți aplicația și verificați:
[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
Nu există spațiu pe disc, dar creăm cu succes o conductă numită acolo:
[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
Acum trebuie să împachetăm cumva toate datele care intră în această conductă către un alt server prin intermediul rețelei; același netcat este potrivit pentru asta.
Pe serverul remote-server.example.com lansăm
[user@localhost ~]$ nc -l 7777 > 123.txt
Pe serverul nostru problematic lansăm într-un terminal separat
[user@localhost ~]$ nc remote-server.example.com 7777 < /mnt/logs/megapipe
Acum toate datele care ajung în conductă vor merge automat la stdin în netcat, care le va trimite în rețea pe portul 7777.
Tot ce trebuie să facem este să începem să scriem datele noastre în această conductă numită.
Avem deja aplicația care rulează:
[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
Dintre toate steagurile, avem nevoie doar de O_WRONLY, deoarece fișierul există deja și nu trebuie să-l ștergem
[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
Verificarea serverului la distanță remote-server.example.com
[user@localhost ~]$ ls -lah 123.txt
-rw-rw-r-- 1 user user 38M Oct 8 14:21 123.txt
Vin datele, verificăm serverul cu probleme
[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
Datele sunt salvate, problema este rezolvată.
Profit de această ocazie pentru a saluta colegii mei din Degiro.
Ascultați podcasturi Radio-T.
Bine tuturor.
Ca teme, vă sugerez să vă gândiți la ceea ce va fi în descriptorii fișierului de proces cat și somn dacă rulați următoarea comandă:
[user@localhost ~]$ cat /dev/zero 2>/dev/null| sleep 10000
Sursa: www.habr.com