Faili descriptor Linuxis koos näidistega

Kord, ühes intervjuus küsiti minult, mida sa teeksid, kui avastad, et teenus ei tööta, kuna kettaruumi on vähe?

Muidugi vastasin, et vaataksin, mis seda ruumi täidab ja vajadusel teeksin seal korda.
Siis küsis intervjueerija, aga mis siis, kui partitsioonil pole vaba ruumi, aga ei näe ka faile, mis kogu ruumi võtaksid?

Sellele vastasin, et alati saab vaadata avatud failide deskriptorite nimekirja, näiteks käsuga lsof, et mõista, milline rakendus on kogu saadavaloleva ruumi ära kasutanud, ja seejärel toimetada olenevalt asjaoludest, sõltuvalt sellest, kas andmed on vajalikud.

Intervjueerija katkestas mind viimase sõnaga, lisades oma küsimusele: „Oletame, et andmed ei ole meile vajalikud, see on lihtsalt tõrke logi, aga rakendus ei tööta, kuna ei saa tõrke logi kirjutada“?

„Okei,“ vastasin ma, „me saame välja lülitada tõrke logimise rakenduse konfigurasioonis ja seejärel taaskäivitada.“
Intervjueerija vastas: „Ei, rakendust me taaskäivitada ei saa, meil on mälus endiselt olulised andmed ja teenusele on ühendatud olulised kliendid, keda me ei saa sundida uuesti ühendama.“

«No hästi», ütlesin ma, «kui me ei saa rakendust taaskäivitada ja andmed pole meile olulised, siis võime lihtsalt selle avatud faili descriptor kaudu puhastada, isegi kui me ei näe seda ls käskluses failisüsteemis.»

Intervjueerija jäi rahule, kuid mina mitte.

Siis mõtlesin, miks inimene, kes kontrollib minu teadmisi, ei kaeva sügavamale? Ent mis siis, kui andmed on ikkagi olulised? Mis siis, kui me ei saa protsessi taaskäivitada ja see protsess kirjutab failisüsteemi sektsiooni, kus pole vaba ruumi? Mis siis, kui me ei saa kaotada mitte ainult juba kirjutatud andmeid, vaid ka neid andmeid, mida see protsess kirjutab või püüab kirjutada?

Tuzik

Oma karjääri alguses püüdsin luua väikest rakendust, kus tuli salvestada teavet kasutajate kohta. Siis mõtlesin, kuidas siduda kasutaja tema andmetega. Näiteks mul on Ivanov Ivan Ivanovich ning tal on mingid andmed, kuid kuidas neid omavahel seostada? Ma võin otse märkida, et koer nimega "Tuzik" kuulub just sellele Ivanile. Aga mis juhtub, kui ta vahetab nime ja muutub näiteks Olgaks? Selgub, et meie Olya Ivanovna Ivanova ei saa enam koera omanikuks, kuid meie Tuzik kuulub endiselt mitteeksisteerivale Ivanile. Selle probleemi lahendas andmebaas, mis andis igale kasutajale unikaalse identifikaatori (ID), ja minu Tuzik seostus selle ID-ga, mis oli tegelikult lihtsalt järjestusnumber. Seega oli Tuziku omanikul ID numbriga 2, ja mingil hetkel sellel ID-l oli Ivan, aga hiljem sai sellest samast ID-st Olya. Inimkonna ja loomakasvatuse probleem oli praktiliselt lahendatud.

Faili deskriptor

Faili ja programmi probleem, mis sellega tegeleb, on umbes sama nagu meie koer ja inimene. Oletame, et avasin faili nimega ivan.txt ja hakkasin sellesse kirjutama sõna tuzik, kuid jõudsin kirjutada vaid esimese tähe «t» faili ning see fail nimetati keegi näiteks ümber olya.txt-ks. Fail jäi aga samaks ja ma tahan endiselt oma tuziki sinna kirjutada. Iga kord, kui avatakse fail süsteemikutsumise teel. open Igas programmeerimiskeeles saan ma unikaalse ID, mis viitab mulle failile, see ID ongi faili deskriptor. Täiesti ükskõik, mida ja kes selle failiga hiljem teeb, seda võidakse kustutada, selle võidakse ümber nimetada, selle omanikku võidakse muuta või õigusi lugemiseks ja kirjutamiseks röövida, mul on sellegipoolest juurdepääs, sest fail avamise hetkel olid mul õigused selle lugemiseks ja/või kirjutamiseks ning ma olen juba alustanud sellega töötamist, seega pean ma seda jätkama.

Linuxis avab libc iga jooksva rakenduse (protsessi) jaoks 3 faili deskriptorit, numbritega 0, 1, 2. Rohkem teavet leiate linkidelt. man stdio ja man stdout

  • Faili descriptor 0 nimetatakse STDIN-iks ja see on seotud rakenduse andmete sisendi vastuvõtmisega.
  • Faili descriptor 1 nimetatakse STDOUT-iks ja rakendused kasutavad seda andmete väljastamiseks, näiteks printimis käsud.
  • Faili descriptor 2 nimetatakse STDERR-iks ja seda kasutatakse rakendustes, mis väljastavad vigadega seotud andmeid.

Kui avate oma programmis mõne faili lugemiseks või kirjutamiseks, siis tõenäoliselt saate esimese vabade ID ja see on number 3.

Faili deskriptorite loendit saab vaadata igas protsessis, kui teate selle PID-d.

Näiteks avame bashiga konsooli ja vaatame meie protsessi PID-d.

[user@localhost ]$ echo $$
15771

Teises konsoolis käivitame

[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

Faili descriptor numbriga 255 võite selle artikli ulatuses rahule jätta, see avati juba bash'i enda vajaduste jaoks, mitte seotud teegi poolest.

Praegu on kõik 3 faili deskriptorit seotud pseudoterminaali seadmega. /dev/pts, kuid me saame neid ikkagi käsitleda, näiteks käivitame teises konsoolis

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

Ja esimeses konsoolis näeme

[user@localhost ]$ hello world

Redirect ja Pipe

Sa saad neid 3 failide descriptorit igas protsessis, sealhulgas bash-is, lihtsalt ümber määrata, näiteks läbi toru (pipe), mis ühendab kahte protsessi, vaatame

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

Sa saad selle käsu ise käivitada strace -f ja näha, mis toimub sees, aga ma räägin sellest lühidalt.

Meie vanem protsess bash PID 15771 tõlgendab meie käsku ja mõistab, kui palju käsklusi me soovime käivitada, meie puhul on neid kaks: cat ja sleep. Bash teab, et peab looma kaks alammenetlust ja ühendama need ühe toruga. Kokku vajab bash 2 alammenetlust ja ühte pipe.

Enne alammenetluste loomist käivitab bash süsteemikõne pipe ja saab uued failide descriptorid ajutisele pipe-bufrile, kuid see buffer ei seo meie kahte alammenetlust.

Vanem protsessile näib, et pipe on juba olemas, kuid alammenetlusi pole veel:

PID    käsk
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

Seejärel kasutab süsteemikõne clone bash loob kaks lapsprotsessi ja meie kolm protsessi näevad välja sellised:

PID    käsk
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    käsk
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    käsk
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

Ärge unustage, et clone kopeerib protsessi koos kõikide failideskirjeldajatega, seetõttu on need vanemas ja alamprotsessides samad. Vanema protsessi ülesanne PID 15771 on jälgida alamprotsesse, seetõttu ootab ta lihtsalt vastust alamprotsessidelt.

Seetõttu pole pipe talle vajalik ja ta sulgeb failideskirjeldajad numbritega 3 ja 4.

Esimeses alamprotsessis bash PID 9004, süsteemi kutsumisega dup2, muudab meie STDOUT failideskirjeldaja numbri 1 faili kirjeldaja numbriks, mis osutab pipe'ile, meie juhul on see number 3. Nõnda kõik, mida esimene alamprotsess PID 9004 kirjutab STDOUT-i, suunatakse automaatselt pipe'i puhvrisse.

Teises alamprotsessis PID 9005 bash muudab dup2 abil STDIN failideskirjeldaja numbri 0. Nüüd loeb kõik, mida meie teine bash PID 9005 loeb, pipe'ist.

Pärast seda sulgevad alamprotsessides samuti failideskirjeldajad numbritega 3 ja 4, kuna neid enam ei kasutata.

Failideskirjeldajat 255 ignoreerin ma teadlikult, seda kasutatakse bashi enda sisemiste vajaduste jaoks ja alamprotsessides suletakse see samuti.

See esimese alamprotsessi PID 9004 korral bash käivitab meie käskude reas määratud täitmisfaili, meie juhul on see /usr/bin/cat. exec Teises alamprotsessis PID 9005 korral bash käivitab teise täitmisfaili, mille oleme määranud, meie juhul on see /usr/bin/sleep.

Süsteemi kutse exec ei sulge faili deskripteerijaid, kui need ei ole avatud O_CLOEXEC lipu all open kutse käivitamise ajal. Meie puhul pärast täitmisfailide käivitamist jäävad kõik praegused faili deskripteerijad alles.

Kontrollime konsoolis:

Kasutame konsolis:

[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

Nagu näete, on meie pipe'i unikaalne number mõlemas protsessis sama. Seega on meil seos kahe erineva protsessi vahel, millel on üks ja sama vanem.

Neile, kes ei tunne bashis kasutatavaid süsteemikutseid, soovitan tungivalt käivitada käsud strace'i abil ja vaadata, mis tegelikult toimub, näiteks nii:

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

Naaseme meie probleemi juurde, mis on seotud kettaruumi puudusega ja andmete salvestamisega ilma protsessi taaskäivitamiseta. Kirjutame väikese programmi, mis salvestab kettale umbes 1 megabaidi sekundis. Sel juhul, kui mingil põhjusel ei õnnestu andmeid kettale salvestada, ignoreerime seda lihtsalt ja püüame andmeid uuesti salvestada sekundi pärast. Näites kasutan Pythonit, kuid võite kasutada mõnda muud programmeerimiskeelt.

[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

Käivitage programm ja vaadake failide deskriptorite seisu

[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

Nagu näha, on meil kolm tavalist failideskriptorit ja veel üks, mille me avasime. Kontrollime faili suurust:

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

Andmed kirjutatakse, proovime muuta faili õigusi:

[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

Näeme, et andmed kirjutatakse endiselt, kuigi meie kasutajal pole faili kirjutamise õigusi. Proovime selle kustutada:

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

Kuhu andmed kirjutatakse? Ja kas need kirjutatakse üldse? Kontrollime:

[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)

Jah, meie failikirjeldaja eksisteerib endiselt ning me saame sellega töötada nagu meie vana failiga, saame seda lugeda, puhastada ja kopeerida.

Vaadates faili suurust:

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

Faili suurus on 19923457. Proovime faili puhastada:

[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

Kuidas näha, et faili suurus ainult suureneb ja meie truncate ei töötanud. Vaatame süsteemi kõne dokumentatsiooni. open. Kui avame faili O_APPEND lipuga, siis iga kirjutamise korral kontrollib operatsioonisüsteem faili suurust ja kirjutab andmed faili lõppu, tehes seda atomaarselt. See võimaldab mitmel tõsil või protsessil kirjutada ühte ja sama faili. Kuid meie koodis me seda lippu ei kasuta. Saame lsof-is faili erineva suuruse näha pärast truncate'i alles siis, kui avame faili lisamiseks, seega meie koodis peaks olema

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

peame panema

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

Kontrollime „w“ lipuga

[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

ja "a" lipuga

[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

Programmeerime juba käivitatud protsessi

Sageli kasutavad programmeerijad programmi loomisel ja testimisel silurite (nt GDB) või rakenduse erinevate logimisastmete abi. Linux pakub tegelikult võimalust kirjutada ja muuta juba käivitatud programmi, näiteks muuta muutujate väärtusi, seada peatuspunkt ja muud sellised asjad.

Naaseme algse küsimuse juurde, mis puudutab failikirjutamiseks kettaruumi puudumist, ja proovime simuleerida probleemi.

Loome faili meie jao jaoks, mille me installime eraldi kettana:

[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 ~]$

Loome failisüsteemi:

[user@localhost ~]$ mkfs.ext4 ~/tempfile_for_article.dd
mke2fs 1.42.9 (28-Dec-2013)
/home/user/tempfile_for_article.dd ei ole blokeeritud eriseade.
Kas jätkata? (y,n) y
...
Kirjutamine superblokid ja failisüsteemi arvestuse teave: tehtud
[user@localhost ~]$

Installime failisüsteemi:

[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

Loome kausta meie kasutaja jaoks:

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

Avame faili ainult kirjutamiseks meie programmis:

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

Käivitage

[user@localhost ]$ python openforwrite.py 

Ootame paar sekundi.

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

Nii, me saime probleemi, millest artikli alguses rääkisime. Vaba ruumi 0, kasutatud 100%.

Peame meeles, et ülesande tingimuste kohaselt püüame salvestada väga olulisi andmeid, mida ei saa kaotada. Ja samal ajal peame teenuse parandama, ilma et protsessi taaskäivitama peaksime.

Oletame, et meil on siiski ruumi kettal, aga teises partitsioonis, näiteks /home.

Proovime oma koodi 'lennult' ümber programmeerida.

Vaadakem meie protsessi PID-d, mis on kogu ruumi ära kulutanud:

[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

Ühendume protsessiga läbi gdb

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

Vaadake avatud faili deskriptorid:

(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

Vaadake faili descriptorite teavet numbriga 3, mis meid huvitab

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

Meeles pidades, millist süsteemikutsungit Python teeb (vt ülal, kus me jooksime strace'i ja leidsime open-kutsungi), töötades meie koodi, et avada fail, teeme sama ise meie protsessi nimel, kuid O_WRONLY|O_CREAT|O_TRUNC bitid tuleb asendada numbrilise väärtusega. Selleks avame tuuma allikakoodid, näiteks siin ja vaatame, millised lipud millega vastutavad

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

Kombineerime kõik väärtused ühte, saame 00001101

Käivitame meie kutse gdb-s

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

Nii saime uue faili deskriptoriga numbriga 4 ja uue avatud faili teisel partitsioonil, kontrollime:

(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

Meename no näiteks pipe - kuidas bash muudab failidesse viitavad deskriptorid ning oleme juba õppinud süsteemikõnet dup2.

Proovime asendada üks failidesse viitav deskriptor teisega

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

Kontrollin:

(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

Sulgeme failidesse viitava deskriptor 4, kuna me ei vaja seda:

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

Ja lahkume gdb-st

(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

Kontrollime uut faili:

[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

Nagu näeme, kirjutatakse andmed uude faili, kontrollime vana:

[user@localhost ~]$ ls -lah /mnt/logs/123.txt 
-rw-rw-r-- 1 user user 7.9M Oct  8 11:08 /mnt/logs/123.txt

Andmed ei ole kadunud, rakendus töötab ja logid kirjutatakse uude kohta.

Teeme ülesande veidi keerulisemaks

Oletame, et andmed on meile olulised, kuid meil ei ole üheski jaotises kettaruumi ning me ei saa ketast ühendada.

Saame teha nii, et suuname meie andmed kuskile, näiteks pipe'i, ja andmed pipe'ist omakorda suunata võrku läbi mõne programmi, näiteks netcat'i.
Saame luua nimelise pipe'i käsuga mkfifo. See loob failisüsteemis pseudo-faili, isegi kui seal ei ole vaba ruumi.

Taaskäivitame rakenduse ja kontrollime:

[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

Kettaruumi pole, kuid me loome seal edukalt nimelise pipe'i:

[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

Nüüd peame kuidagi kapseldama kõik andmed, mis jõuavad sellesse pipe'i teisele serverile üle võrgu, selleks sobib endiselt netcat.

Käivitame serveris remote-server.example.com

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

Käivitame meie probleemserveris eraldi terminalis

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

Nüüd jõuavad kõik andmed, mis sisenevad pipe'i, automaatselt netcat'i stdin'i, mis saadab need võrku pordile 7777.

Kõik, mis meil veel üle jääb, on hakata kirjutama meie andmeid sellesse nimetatud pipe'i.

Meil on juba käivitatud rakendus:

[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

Kõigist lipukestest vajame ainult O_WRONLY, kuna fail juba eksisteerib ja me ei pea seda tühjendama.

[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

Kontrollime eemalserverit remote-server.example.com

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

Andmed liiguvad, kontrollime probleemset serverit

[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

Andmed on salvestatud, probleem on lahendatud.

Kasutades võimalust, edastan tervitused Degiro ettevõtte kolleegidele.
Kuulake Raadio-T taskuhäälinguid.

Kõigile head päeva.

Koduülesandena soovitan mõelda, mis toimub protsessi failides cat ja sleep, kui käivitada järgmine käsk:

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

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster