Ühel intervjuhil küsiti minult, mida teeksin, kui avastaksin, et teenus ei tööta, kuna kettal on ruum otsas?
Muidugi vastasin, et vaataksin, milles see ruum on kinni ja kui võimalik, siis puhastaksin ruumi.
Siis küsis intervjueerija, aga mis siis, kui jaotises ei ole vaba ruumi, kuid ka faile, mis kogu ruumi peaksid kasutama, sa ei näe?
Sellele vastasin, et alati saab vaadata avatud failide kirjeldajaid, näiteks käsuga lsof, ja mõista, milline rakendus on kogu saadaval oleva ruumi ära kasutanud, ning seejärel toimetada vastavalt olukorrale, sõltuvalt sellest, kas andmed on vajalikud.
Intervjueerija katkestas mind viimases sõnas, lisades oma küsimusele: "Kujutame ette, et andmed ei ole meile vajalikud, see on lihtsalt tõrke logi, kuid rakendus ei tööta, kuna ei saa kirjutada tõrke logi"?
"Okei," vastasin, "me võime rakenduse konfis tõrke logimise välja lülitada ja selle uuesti käivitada."
Intervjueerija vaidlustas: "Ei, rakendust me ei saa uuesti käivitada, meil on mälus endiselt olulised andmed ja sellele teenusele on ühendatud olulised kliendid, keda me ei saa sundida uuesti ühendust looma."
"Noh, hea," ütlesin, "kui me ei saa rakendust käivitada ja andmed ei ole meile olulised, siis saame lihtsalt selle avatud faili läbi failide kirjeldaja puhastada, isegi kui me ei näe seda käsus ls failisüsteemis."
Intervjueerija jäi rahule, aga mina mitte.
Siis mõtlesin, miks inimene, kes kontrollib minu teadmisi, ei kaeva sügavamale? Mis siis, kui andmed on ikkagi olulised? Mis siis, kui me ei saa protsessi taaskäivitada, ja samal ajal kirjutab see protsess failisüsteemi jaotisse, kus ei ole 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 oli vaja hoida teavet kasutajate kohta. Siis mõtlesin, kuidas siduda kasutajat tema andmetega. Näiteks on mul Ivan Ivanov, kellel on mingid andmed, kuid kuidas neid omavahel siduda? Võin otse näidata, et koer nimega „Tuzik” kuulub just sellele Ivanile. Kuid mis siis, kui ta vahetab nime ja temast saab näiteks Olya? Siis selgub, et meie Olya Ivanovna Ivanova ei omagi enam koera, kuid meie Tuzik kuulub endiselt olematule Ivanile. Probleemi aitas lahendada andmebaas, mis andis igale kasutajale unikaalse identifikaatori (ID), ja mu Tuzik oli seotud selle ID-ga, mis oli sisuliselt lihtsalt järjestusnumber. Nii oli Tuziku omanikul ID number 2, ja mingil ajahetkel oli selle all Ivan, hiljem aga Olya. Inimkonna ja loomade probleemi saime praktiliselt lahendada.
Faili deskriptor
Faili ja selle faili avava programmi probleem on umbes sama, mis meie koera ja inimese probleem. Oletame, et avasin faili nimega ivan.txt ja hakkasin sinna kirjutama sõna tuzik, kuid suutsin kirjutada vaid esimese tähe „t” ja see fail nimetati kellegi poolt ümber, näiteks olya.txt. Kuid fail jäi endiseks ja ma tahan endiselt kirjutada oma Tuzik-ist. Iga kord, kui avatud failiga süsteemi kutsumi, kõigis programmeerimiskeeltes saan unikaalse ID, mis näitab mulle faili, see ID ongi faili deskriptor. Ja täiesti pole oluline, mida ja kes selle failiga hiljem teeb, seda võidakse kustutada, seda võidakse ümber nimetada, selle omanikku võidakse vahetada või lugemis- ja kirjutamisõigusi võidakse võtta, mul on ikkagi sellele juurdepääs, sest faili avamise hetkel olid mul vahemälu selle lugemiseks ja/või kirjutamiseks ning ma suutsin alustada selle kasutamist, seega pean seda edasi tegema.
Linuxis avab libc igale käimasolevale rakendusele (protsessile) 3 faili deskriptorit numbritega 0, 1, 2. Lisainfot leiate linkidelt. ja
- Faili deskriptor 0 nimetatakse STDIN-iks ja on seotud rakenduse andmete sisestamisega.
- Faili deskriptor 1 nimega STDOUT, mida rakendused kasutavad andmete väljundiks, näiteks print käsud.
- Faili deskriptor 2 nimega STDERR, mida rakendused kasutavad andmete, mis teatavad vigadest, väljundiks.
Kui avate oma programmis mõne faili lugemiseks või kirjutamiseks, siis tõenäoliselt saate esimese vaba ID ja see on number 3.
Failide deskriptorite loendit saab vaadata igas protsessis, kui te küsite selle PID-d.
Näiteks avame bash konsooli ja vaatame oma 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 deskriptorit nummer 255 võite selles artiklis julgelt ignoreerida, see oli avatud bash enda vajaduste jaoks, mitte linkitud teegi jaoks.
Praegu on kõik 3 faili deskriptorit seotud pseudo-terminali seadmega. , kuid me saame siiski nendega manipuleerida, näiteks käivitame teises konsoolis
[user@localhost ]$ echo "hello world" > /proc/15771/fd/0
Ja esimeses konsoolis näeme me
[user@localhost ]$ hello world
Redirect ja Pipe
Saate need 3 faili deskriptorit igas protsessis, sealhulgas bash, hõlpsasti ümber määrata, näiteks läbi toru (pipe), mis ühendab kahte protsessi, vaatame
[user@localhost ]$ cat /dev/zero | sleep 10000
Võite ise selle käsu käivitada ja näha, mis juhtub, kuid räägin teile lühidalt. strace -f Meie vanem protsess bash, mille PID on 15771, analüüsib meie käsku ja mõistab, kui palju käske me soovime käivitada, meie juhul on neid kaks: cat ja sleep. Bash teab, et tal on vaja luua kaks tütart protsessi ja need ühte torusse ühendada. Kokku vajab bash 2 tütart protsessi ja ühte pipe'i.
Enne tütarprotsesside loomist käivitab bash süsteemi kutse
ja saab uusi faili deskriptoreid ajutisele toru puhvri jaoks, kuid see puhver ei seo praegu meie kaht tütarprotsessi. Vanema protsessi jaoks näeb see välja nagu pipe oleks juba olemas, kuid tütarprotsessid pole veel loodud:
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 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
Seejärel kasutab süsteemikõnet bash loob kaks tütart protsessi ja meie kolm protsessi näevad välja nii:
PID käsk
15771 bash
lrwx------ 1 user user 64 Okt 7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Okt 7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Okt 7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Okt 7 15:42 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Okt 7 15:42 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Okt 7 15:42 255 -> /dev/pts/21
PID käsk
9004 bash
lrwx------ 1 user user 64 Okt 7 15:57 0 -> /dev/pts/21
lrwx------ 1 user user 64 Okt 7 15:57 1 -> /dev/pts/21
lrwx------ 1 user user 64 Okt 7 15:57 2 -> /dev/pts/21
lrwx------ 1 user user 64 Okt 7 15:57 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Okt 7 15:57 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Okt 7 15:57 255 -> /dev/pts/21
PID käsk
9005 bash
lrwx------ 1 user user 64 Okt 7 15:57 0 -> /dev/pts/21
lrwx------ 1 user user 64 Okt 7 15:57 1 -> /dev/pts/21
lrwx------ 1 user user 64 Okt 7 15:57 2 -> /dev/pts/21
lrwx------ 1 user user 64 Okt 7 15:57 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Okt 7 15:57 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Okt 7 15:57 255 -> /dev/pts/21
Ärge unustage, et clone kloonib protsessi koos kõigi failikuvanditega, seega on need vanem- ja tütart protsessides ühesugused. Vanemprotsessi, mille PID on 15771, ülesanne on jälgida tütart protsesse, seega ootab see lihtsalt vastust tütartelt.
Seega ei ole tal pipe'i vaja ja ta sulgeb failikuvandid numbritega 3 ja 4.
Esimeses tütart protsessis bashiga, mille PID on 9004, süsteemikõne , muudab meie STDOUT failikuvandi numbriga 1 failikuvandiks, mis näitab pipe'i, meie juhul on see number 3. Nii et kõik, mis esimene tütart protsess PID 9004 kirjutab STDOUT-i, jõuab automaatselt pipe'i puhvri.
Teises tütart protsessis PID 9005 bash muudab failikuvandi STDIN numbriga 0, kasutades dup2. Nüüd loeb kõik, mida meie teine bash PID 9005 loeb, pipe'ist.
Pärast seda suletakse tütart protsessides samuti failikuvandid numbritega 3 ja 4, kuna neid ei kasutata enam.
Failikuvand 255 ignoreerin ma tahtlikult, seda kasutatakse bash-i enda sisemiste vajaduste jaoks ja tütart protsessides sulgetakse see samuti.
Seejärel käivitab esimeses tütart protsessis PID 9004 bash süsteemikõne abil täidetava faili, mille oleme määranud käsureas, meie puhul on see /usr/bin/cat.
Teises tütart protsessis PID 9005 käivitab bash teise täidetava faili, mille oleme määranud, meie puhul on see /usr/bin/sleep.
Süsteemikõne exec ei sulge faili deskriptereid, kui need ei olnud avatud O_CLOEXEC lipuga open'i kutsumise ajal. Meie puhul jäävad pärast täidetavate failide käivitamist kõik praegused faili deskripterid alles.
Kontrollime konsoolis:
[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 toru ainulaadne number mõlemas protsessis sama. Seega on meil seos kahe erineva protsessi vahel, millel on üks vanem.
Neile, kes ei ole tuttavad bash'i kasutatavate süsteemikõnedega, soovitan tungivalt käivitada käsud strace'i kaudu ja vaadata, mis toimub seespool, näiteks nii:
strace -s 1024 -f bash -c "ls | grep hello"
Naaseme meie probleemiga ketta ruumi puudumise ja andmete salvestamise katse juurde ilma protsessi taaskäivitamata. Kirjutame väikese programmi, mis kirjutab diskile umbes 1 megabait sekundis. Samas, kui me mingil põhjusel ei suuda andmeid diskile kirjutada, ignoreerime seda lihtsalt ja proovime jälle järgmise sekundi jooksul andmeid kirjutada. Näites kasutan ma Pythonit, aga 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äivitame programmi ja vaatame faili deskriptereid
[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äeme, on meil olemas meie 3 standardset failikirjeldajat ja veel üks, mille 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 faili õigusi muuta:
[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ägime, et andmeid jätkuvalt kirjutatakse, kuigi meie kasutajal ei ole õigust faili kirjutada. 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? Kas nad 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 on endiselt olemas ja me saame seda failikirjeldajat kasutada nagu meie vana faili, me saame seda lugeda, tühjendada ja kopeerida.
Vaadake 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 tühjendada:
[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
Nagu näeme, suureneb faili suurus ainult ja meie tühjendamine ei töötanud. Viitame süsteemikõne dokumentatsioonile . Kui me avame faili O_APPEND lipuga, siis igal kirjutamisel kontrollib operatsioonisüsteem faili suurust ja kirjutab andmed faili lõppu, tehes seda aatomiliselt. See võimaldab mitmetel lõimedel või protsessidel kirjutada ühte ja sama faili. Kuid meie koodis me seda lippu ei kasuta. Saame 'lsof' väljundis pärast tühjendamist näha teist faili suurust ainult juhul, kui avame faili lisamiseks, seega peaksime meie koodis
with open("123.txt", "w") as f:
asendama
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" lipukesega
[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äimasolevat protsessi
Sageli kasutavad arendajad programmide loomisel ja testimisel debugeerijaid (nt GDB) või rakenduses mitmeid logimise tasemeid. Linux annab võimaluse tegelikult kirjutada ja muuta juba käimasolevat programmi, näiteks muuta muutujate väärtusi, seada peatuspunkte jne.
Naaseme algse küsimuse juurde, kus oli probleem diskiruumi puudumise tõttu faili kirjutamiseks, ja proovime emuleerida probleemi.
Loome faili meie partitsiooni jaoks, mille me monteerime kui eraldi ketas:
[user@localhost ~]$ dd if=/dev/zero of=~/tempfile_for_article.dd bs=1M count=10
10+0 salvestust sisse
10+0 salvestust välja
10485760 baiti (10 MB) kopeeritud, 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 plokk-spetsiaalne seade.
Jätkata ikka? (y,n) y
...
Kirjutamine superblokidesse ja failisüsteemi arvestusteabe: valmis
[user@localhost ~]$
Monteerime failisüsteemi:
[user@localhost ~]$ sudo mount ~/tempfile_for_article.dd /mnt/
[sudo] parool kasutajale:
[user@localhost ~]$ df -h | grep mnt
/dev/loop0 8.7M 172K 7.9M 3% /mnt
Loome kaust meie omanikuga:
[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äivitame
[user@localhost ]$ python openforwrite.py
Ootame paar sekundit
[user@localhost ~]$ df -h | grep mnt
/dev/loop0 8.7M 8.0M 0 100% /mnt
Nii oleme saanud probleemi, millest käesoleva artikli alguses rääkisime. Vaba ruumi 0, kasutatud 100%.
Me mäletame, et ülesande tingimustest tulenevalt üritame salvestada väga olulisi andmeid, mida ei saa kaotada. Ja samas peame teenuse parandama ilma protsessi taaskäivitamata.
Oletame, et meil on siiski ketas vaba ruumi, kuid teises osas, näiteks /home.
Proovime meie koodi "reprogrammeerida" reaalajas.
Vaadake meie protsessi PID, mis on kogu diskiruumi ära kasutanud:
[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
Ühendame gdb kaudu protsessiga
[user@localhost ~]$ gdb -p 10078
...
(gdb)
Vaadake avatud failide descriptor-e:
(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 descriptori teavet, mille number on 3, mis meid huvitab
(gdb) shell cat /proc/10078/fdinfo/3
pos: 8189952
flags: 0100001
mnt_id: 482
Mäletades, millist süsteemi kutsungit Python kutsub (vt ülal, kus käivitasime strace ja leidsime kutsungi open), töötades meie koodi faili avamiseks, teeme me seda ise oma protsessi nimel, kuid O_WRONLY|O_CREAT|O_TRUNC bitid peame asendama numbrilise väärtusega. Selleks avame tuuma lähtekoodid, näiteks ja vaatame, millised lipud millegi eest vastutavad
#define O_WRONLY 00000001
#define O_CREAT 00000100
#define O_TRUNC 00001000
Kombineerime kõik väärtused ühte, saame 00001101
Käivitame meie kutsungi gdb-st
(gdb) call open("/home/user/123.txt", 00001101,0666)
$1 = 4
Nii saime uue failideskripti numbriga 4 ja uue avatud faili teisel jaol, 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
Mäletame pipe'i näidet - kuidas bash muudab faili deskriptiive ja oleme juba õppinud süsteemi kutsungi dup2.
Proovime asendada ühte faili deskriptiivi teisega
(gdb) call dup2(4,3)
$2 = 3
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 -> /home/user/123.txt
l-wx------ 1 user user 64 Oct 8 11:15 4 -> /home/user/123.txt
Sulgeme faili deskriptiiv 4, kuna see ei ole meile vajalik:
(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
Kuidas näeme, andmed kirjutatakse 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, logid kirjutatakse uude kohta.
Veidi keerukam ülesanne
Kujutame ette, et andmed on meile olulised, kuid meil ei ole mingisugust ketasruumi ühelgi jaol ja me ei saa ketast ühenduda.
Mida saame teha, on suunata meie andmed kuhugi mujale, näiteks pipe'i, ning andmed pipe'ist omakorda suunata võrku mõne programmi kaudu, näiteks netcat.
Saame luua nimelise pipe'i käsuga mkfifo. See loob pseudofaili failisüsteemis, isegi kui seal pole 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
Diskil ruumi pole, kuid me õnnestusime seal luua nimelise pipe:
[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 leidma viisi, kuidas kõik andmed, mis satuvad sellesse pipe'i, suunata teisele serverile üle võrgu, selleks sobib sama netcat.
Serveris remote-server.example.com käivitame
[user@localhost ~]$ nc -l 7777 > 123.txt
Meie probleemse serveri peal, avame teises terminalis
[user@localhost ~]$ nc remote-server.example.com 7777 < /mnt/logs/megapipe
Nüüd suunatakse kõik andmed, mis satuvad pipe'i, automaatselt stdin'i netcat'is, mis saadab need võrku sadamasse 7777.
Meie ülesanne on alustada andmete kirjutamist sellesse nimelisse pipe'i.
Meil on juba rakenduse töö:
[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 lippudest vajame ainult O_WRONLY, kuna fail juba eksisteerib ja me ei pea seda kustutama.
[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 Okt 8 11:27 .
dr-xr-xr-x 9 user user 0 Okt 8 11:27 ..
lrwx------ 1 user user 64 Okt 8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Okt 8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Okt 8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Okt 8 11:28 3 -> /mnt/logs/123.txt
l-wx------ 1 user user 64 Okt 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 Okt 8 11:27 .
dr-xr-xr-x 9 user user 0 Okt 8 11:27 ..
lrwx------ 1 user user 64 Okt 8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Okt 8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Okt 8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Okt 8 11:28 3 -> /mnt/logs/megapipe
l-wx------ 1 user user 64 Okt 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 Okt 8 11:27 .
dr-xr-xr-x 9 user user 0 Okt 8 11:27 ..
lrwx------ 1 user user 64 Okt 8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Okt 8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Okt 8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Okt 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 kaugserverit remote-server.example.com
[user@localhost ~]$ ls -lah 123.txt
-rw-rw-r-- 1 user user 38M Okt 8 14:21 123.txt
Andmed tulevad, kontrollime probleemse serveri staatust
[user@localhost ~]$ ls -lah /mnt/logs/
total 7.9M
drwxr-xr-x 2 user user 1.0K Okt 8 11:28 .
drwxr-xr-x 4 root root 1.0K Okt 8 10:55 ..
-rw-rw-r-- 1 user user 7.9M Okt 8 14:17 123.txt
prw-rw-r-- 1 user user 0 Okt 8 14:22 megapipe
Andmed on säilinud, probleem on lahendatud.
Kasutan juhust ja saadan tervitused Degiro kolleegidele.
Kuulake Raadio-T podcaste.
Kõigile head soovid.
Koduseks ülesandeks pakun mõelda, mis on protsessi cat ja sleep failides deskriptorites, kui käivitada selline käsk:
[user@localhost ~]$ cat /dev/zero 2>/dev/null| sleep 10000
Allikas: habr.com
