Një herë, gjatë një interviste më pyetën se çfarë do të bësh nëse zbuloje një shërbim të pa funksionueshëm për shkak se disk-u ka mbaruar vendin?
Sigurisht që i thashë se do të shikoja se çfarë po zëvendasohet dhe nëse është e mundur, do ta pastraja atë vend.
Atëherë intervistuesi më pyeti, çfarë nëse në ndarjen nuk ka vend të lirë, por as skedarë që zënë gjithçka, nuk i shikon?
Për këtë i thashë se gjithmonë mund të shikojmë skedarët e hapur, për shembull me komandën lsof dhe të kuptojmë se cila aplikacion ka zënë të gjithë vendin e disponueshëm, pastaj mund të veprojmë sipas rrethanave, në varësi të nevojave për të dhënat.
Intervistuesi më ndërpreu në fjalën e fundit, duke plotësuar pyetjen e tij: "Supozoni se të dhënat nuk na duhen, është vetëm një log debagimi, por aplikacioni nuk funksionon për shkak se nuk mund të shkruajë debagimin"?
"Ok", â pĂ«rgjigja, "ne mund tĂ« çaktivizojmĂ« debagimin nĂ« konfigurimin e aplikacionit dhe ta riaktivejmĂ« atĂ«".
Intervistuesi kundĂ«rshtoi: "Jo, nuk mund ta riaktivizojmĂ« aplikacionin, sepse nĂ« memorie ende ruhen tĂ« dhĂ«na tĂ« rĂ«ndĂ«sishme dhe nĂ« shĂ«rbim janĂ« lidhur klientĂ« tĂ« rĂ«ndĂ«sishĂ«m, tĂ« cilĂ«t nuk mund tâi detyrojmĂ« tĂ« rinovojnĂ« lidhjen".
«Po mirë», thashë, «nëse nuk mund të riçelim aplikacionin dhe të dhënat nuk na interesojnë, atëherë thjesht mund ta pastrojmë këtë skedarin e hapur përmes identifikuesit të skedarit, edhe nëse nuk e shohim në komandën ls në sistemin e skedarëve.»
Intervistuesi ishte i kënaqur, ndërsa unë jo.
AtĂ«herĂ« mendova, pse personi qĂ« po provon njohuritĂ« e mia nuk gĂ«rmon mĂ« thellĂ«? ĂfarĂ« nĂ«se tĂ« dhĂ«nat janĂ« tĂ« rĂ«ndĂ«sishme? ĂfarĂ« nĂ«se nuk mund tĂ« riçelim procesin, dhe ky proces shkruan nĂ« sistemin e skedarĂ«ve nĂ« njĂ« seksion ku nuk ka hapĂ«sirĂ« tĂ« lirĂ«? ĂfarĂ« nĂ«se nuk mund tĂ« humbim vetĂ«m tĂ« dhĂ«nat qĂ« janĂ« already shkruar, por edhe ato qĂ« ky proces Ă«shtĂ« duke shkruar ose pĂ«rpiqet tĂ« shkruajĂ«?
Tuzik
NĂ« fillim tĂ« karrierĂ«s sime, pĂ«rpiqesha tĂ« krijoja njĂ« aplikacion tĂ« vogĂ«l, ku duhej tĂ« ruaja informacionin rreth pĂ«rdoruesve. AtĂ«herĂ« mendoja, si mund ta lidh pĂ«rdoruesin me tĂ« dhĂ«nat e tij? PĂ«r shembull, kam Ivanov Ivan Ivaniçin, dhe ai ka disa tĂ« dhĂ«na, por si tâi lidh ato? Mund ta pĂ«rcaktoj drejtpĂ«rdrejt se qeni me emrin "Tuzik" i pĂ«rket kĂ«tij Ipari. Por çfarĂ« ndodh nĂ«se ai ndĂ«rron emrin dhe bĂ«het, pĂ«r shembull, Olya? AtĂ«herĂ« do tĂ« duket se Olya Ivanovna Ivanova nuk do tĂ« ketĂ« mĂ« qen, ndĂ«rsa Tuzik do tĂ« vazhdojĂ« tĂ« i pĂ«rkasĂ« njĂ« Ivan qĂ« nuk ekziston. Zgjidhja e kĂ«saj problemi erdhi nga njĂ« bazĂ« tĂ« dhĂ«nash, e cila i jepte çdo pĂ«rdoruesi njĂ« identifikues unik (ID), dhe Tuzik lidhej me kĂ«tĂ« ID, i cili, nĂ« thelb, ishte thjesht njĂ« numĂ«r radhor. NĂ« kĂ«tĂ« mĂ«nyrĂ«, pronari i Tuzik ishte me ID numĂ«r 2, dhe nĂ« njĂ« moment tĂ« caktuar, nĂ«n kĂ«tĂ« ID ishte Ivan, e mĂ« pas nĂ«n tĂ« njĂ«jtin ID u bĂ« Olya. Problemi i njerĂ«zimit dhe blegtorisĂ« ishte praktikisht zgjidhur.
Skedari i deskriptorit
Problemi me skedarin dhe programin qĂ« punon me kĂ«tĂ« skedar Ă«shtĂ« pĂ«rafĂ«rsisht i njĂ«jtĂ« me atĂ« tĂ« qenit tonĂ« dhe njeriut. Le tĂ« themi se kam hapur skedarin me emrin ivan.txt dhe kam filluar tĂ« shkruaj fjalĂ«n tuzik, por kam arritur tĂ« shkruaj vetĂ«m shkronjĂ«n e parĂ« 't' nĂ« skedar, dhe ky skedar Ă«shtĂ« rinomuar nga dikush, pĂ«r shembull nĂ« olya.txt. Por skedari mbeti ende i njĂ«jti, dhe unĂ« ende dua tĂ« shkruaj tuzikun tim nĂ« tĂ«. Ădo herĂ« qĂ« hap skedarin pĂ«rmes njĂ« thirrjeje sistemore nĂ« çdo gjuhĂ« programimi unĂ« marr njĂ« ID unike, e cila mĂ« tregon pĂ«r skedarin, kjo ID Ă«shtĂ« gjithashtu descriptor i skedarit. Dhe nuk ka absolutisht asnjĂ« rĂ«ndĂ«si se çfarĂ« dhe kush bĂ«n me kĂ«tĂ« skedar mĂ« pas, ata mund ta fshijnĂ«, ata mund ta rinominojnĂ«, ata mund t'i ndryshojnĂ« pronarĂ«t ose t'i marrin tĂ« drejtat pĂ«r lexim dhe shkruajtur, unĂ« prapĂ« do tĂ« kem qasje nĂ« tĂ«, sepse nĂ« momentin e hapjes sĂ« skedarit kisha tĂ« drejta pĂ«r ta lexuar dhe/ose shkruar dhe kam arritur tĂ« filloj tĂ« punoj me tĂ«, dhe kjo do tĂ« thotĂ« se duhet tĂ« vazhdoj ta bĂ«j kĂ«tĂ«.
Në Linux, biblioteka libc hap për çdo aplikacion të hapur (proces) 3 deskriptorë skedari, me numra 0, 1, 2. Më shumë informacion mund të gjeni në lidhjet dhe
- Descriptori i skedarit 0 quhet STDIN dhe lidhet me hyrjen e të dhënave në aplikacion.
- Descriptori i skedarit 1 quhet STDOUT dhe përdoret nga aplikacionet për daljen e të dhënave, si për shembull komandat print.
- Descriptori i skedarit 2 quhet STDERR dhe përdoret nga aplikacionet për të raportuar të dhënat e gabimeve.
Nëse në programin tuaj hapni ndonjë skedër për të lexuar ose shkruar, atëherë me siguri do të merrni ID-në e parë të lirë dhe kjo do të jetë numri 3.
Lista e descriptorëve të skedarëve mund të shikohet për çdo proces, nëse e dini PID-në e tij.
Për shembull, le të hapim një konsolë me bash dhe të shikojmë PID-në e procesit tonë.
[user@localhost ]$ echo $$
15771
Në konsolën tjetër do të lançojmë
[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
Descriptori i skedarit me numër 255 mund ta injoroni me siguri në kuadër të këtij artikulli, ai u hap për nevojat e tij nga vetë bash, dhe jo nga një bibliotekë e lidhur.
Tani të 3 descriptorët e skedarëve janë të lidhur me pajisjen e pseudoterminalit. , por shkaq se ne mund të manipulojmë me to, për shembull do ta ekzekutojmë në konsolën e dytë
[user@localhost ]$ echo "hello world" > /proc/15771/fd/0
Dhe në konsolën e parë do të shohim
[user@localhost ]$ hello world
Redirect dhe Pipe
Mund ta tejkaloni lehtësisht këto 3 përshkrues skedhesh në çdo proces, duke përfshirë gjithashtu bash, për shembull përmes një pipe që lidh dy procese, shohim
[user@localhost ]$ cat /dev/zero | sleep 10000
Mund ta ekzekutoni vetë këtë komandë me strace -f dhe të shihni se çfarë ndodh brenda, por do t'ua tregoj shkurtimisht.
Procesi ynë prind bash me PID 15771 analizon komandën tonë dhe kupton sesa komanda duam të ekzekutojmë, në rastin tonë janë dy: cat dhe sleep. Bash e di se i nevojiten dy procese të bijshëm dhe të bashkojë ato me një pipe. Pra, për bash do të nevojiten 2 procese të bijshëm dhe një pipe.
Para se të krijojë proceset e bijshëm, bash ekzekuton thirrjen sistemore dhe merr përshkrues të rinj skedesh për një përzierës të përkohshëm pipe, por ky përzierës nuk lidh ende dy proceset tona të bijshëm.
Për procesin prind, kjo duket sikur pipe është atje, ndërsa proceset e bijshëm ende nuk ekzistojnë:
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
Pastaj, me anë të thirrjes së sistemit bash krijon dy procese fëmijë, dhe tre proceset tona do të duken kështu:
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
Mos e harroni se clone klonon procesin së bashku me të gjithë përshkruesit e skedarëve, prandaj në procesin prind dhe në ata të bije do të jenë të njëjtë. Detyra e procesit prind me PID 15771 është të ndjekë proceset e bij, prandaj ai thjesht pret përgjigje nga ato.
Për këtë arsye, pipe-i nuk i nevojitet, dhe ai mbyll përshkruesit e skedarëve me numrat 3 dhe 4.
Në procesin e parë të bij bash me PID 9004, përmes thirrjes sistemike , ndryshon përshkruesin e skedarit tonë STDOUT me numrin 1 në përshkruesin e skedarit që tregon në pipe, në rastin tonë ky është numri 3. Kështu, çdo gjë që procesi i parë i bije me PID 9004 do të shkruajë në STDOUT, do të kalojë automatikisht në buffer-in e pipe-it.
Në procesin e dytë të bij bash me PID 9005, bash ndryshon përmes dup2 përshkruesin e skedarit STDIN me numrin 0. Tani çdo gjë që do të lexojë bash-i ynë i dytë me PID 9005, do të lexojë nga pipe.
Pas kësaj, në proceset e bijeve gjithashtu mbyllen përshkruesit e skedarëve me numrat 3 dhe 4, pasi ata nuk përdoren më.
Përshkruesin e skedarit 255 e injoroj me qëllim, ai përdoret për nevojat e brendshme të bash-it dhe në proceset e bije do të mbyllet gjithashtu.
Pastaj, në procesin e parë dytësor me PID 9004, bash ekzekuton me ndihmën e thirrjes së sistemit skedarin e ekzekutueshëm që kemi specifikuar në komandën e komandës, në këtë rast është /usr/bin/cat.
Në procesin e dytë dytësor me PID 9005, bash ekzekuton skedarin e dytë të ekzekutueshëm që kemi specifikuar, në këtë rast është /usr/bin/sleep.
Thirrja e sistemit exec nuk mbyll descriptorët e skedarëve, nëse ata nuk janë hapur me flagun O_CLOEXEC gjatë thirrjes së open. Në rastin tonë, pas ekzekutimit të skedarëve të ekzekutueshëm, të gjithë descriptorët aktualë të skedarëve do të ruhet.
Kontrolloni në konsolë:
[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
Si e shihni, numri unik i pipe-it në të dy proceset tona përputhet. Kështu, ne kemi një lidhje mes dy proceseve të ndryshme me një prind të vetëm.
Për ata që nuk janë të njohur me thirrjet sistemore që përdor bash, rekomandoj shumë të ekzekutoni komandat përmes strace dhe të shihni se çfarë ndodh brenda, për shembull, kështu:
strace -s 1024 -f bash -c "ls | grep hello"
Le të kthehemi në problemin tonë me mungesën e hapësirës në disk dhe përpjekjen për të ruajtur të dhëna pa e rinisur procesin. Do të shkruaj një program të vogël që do të shkruaj në disk rreth 1 megabajt në sekondë. Nëse për ndonjë arsye ne nuk arrijmë të shkruajmë të dhënat në disk, thjesht do ta injorojmë këtë dhe do të përpiqemi të shkruajmë të dhënat përsëri pas një sekonde. Në këtë shembull, po përdor Python, por mund të përdorni çdo gjuhë tjetër programimi.
[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
Të nisim programin dhe të shikojmë të dhënat përkatëse të skedarëve
[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
Siç po shohim, kemi 3 standardet tona të skedarëve dhe një tjetër që e hapëm. Le të kontrollojmë madhësinë e skedarit:
[user@localhost ]$ ls -lah 123.txt
-rw-rw-r-- 1 user user 117M Oct 7 16:30 123.txt
të dhënat po shkruhen, provoni të ndryshoni të drejtat në skedar:
[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
Shikojmë se të dhënat ende po shkruhen, edhe pse përdoruesi ynë nuk ka të drejta për të shkruar në skedar. Le të provojmë ta fshijmë:
[user@localhost ]$ sudo rm 123.txt
[user@localhost ]$ ls 123.txt
ls: cannot access 123.txt: No such file or directory
Ku po shkruhen të dhënat? Dhe a po shkruhen në të vërtetë? Kontrollojmë:
[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 (fshirë)
Po, skedari ynë ende ekziston, dhe ne mund të punojmë me këtë skedar si me skedarin tonë të vjetër, mund ta lexojmë, ta zbrazim dhe ta kopjojmë.
Shikojmë madhësinë e skedarit:
[user@localhost ]$ lsof | grep 123.txt
python 31083 user 3w REG 8,5 19923457 2621522 /home/user/123.txt
Madhësia e skedarit 19923457. Provoni ta zbrazni skedarin:
[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
Siç e shohim, madhësia e skedarit vetëm po rritet dhe truncat tonë nuk funksionoi. Le të kthehemi te dokumentacioni mbi thirrjen sistemore . Nëse gjatë hapjes së skedarit përdorim flamurin O_APPEND, atëherë për çdo shkruajtje, sistemi operativ kontrollon madhësinë e skedarit dhe shkruan të dhënat në fund të skedarit, dhe e bën këtë në mënyrë atomike. Kjo lejon që disa threads ose procese të shkruajnë në të njëjtin skedar. Por në kodin tonë nuk e përdorim këtë flamur. Mund të shohim një madhësi tjetër skedari në lsof pas truncat vetëm nëse hapim skedarin për shtim, kështu që në kodin tonë në vend të
with open("123.txt", "w") as f:
duhet të vendosim
with open("123.txt", "a") as f:
Kontrollojmë me flamurin "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
dhe me flamurin «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
Programojmë procesin e nisur tashmë
Shpesh programuesit, gjatë krijimit dhe testimit të programit, përdorin debagues (për shembull GDB) ose nivele të ndryshme logimi në aplikacion. Linux ofron mundësinë të shkruash dhe ndryshosh praktikisht një program të nisur, për shembull duke ndryshuar vlerat e variableve, vendosur breakpoint-et, etj.
Duke u kthyer në pyetjen origjinale për mungesën e hapësirës në disk për të shkruar një skedar, le të simulonim problemin.
Të krijojmë një skedar për seksionin tonë, të cilin do ta nënmontojmë si një disk të veçantë:
[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 ~]$
Të krijojmë një sistem skedari:
[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 ~]$
Të nënmontojmë sistemin e skedarëve:
[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
Krijojmë një direktor me pronarin tonë:
[user@localhost ~]$ sudo mkdir /mnt/logs
[user@localhost ~]$ sudo chown user: /mnt/logs
Do të hapim skedarin vetëm për shkrim në programin tonë:
with open("/mnt/logs/123.txt", "w") as f:
Nisni
[user@localhost ]$ python openforwrite.py
Pritni disa sekonda
[user@localhost ~]$ df -h | grep mnt
/dev/loop0 8.7M 8.0M 0 100% /mnt
Kështu, kemi hasur problemin e përshkruar në fillim të këtij artikulli. Hapësira e lirë është 0, e zënë 100%.
Ne e mbajmë mend se sipas kërkesës po përpiqemi të shkruajmë të dhëna shumë të rëndësishme, të cilat nuk duhet të humbin. Dhe për këtë, na duhet të riparojmë shërbimin pa restartimin e procesit.
Le të supozojmë se ndonjëherë kemi hapësirë në disk, por në një ndarjes tjetër, për shembull në /home.
Le të provojmë të "ri-programojmë në flakë" kodin tonë.
Shikojmë PID-në e procesit tonë që ka konsumuar të gjithë hapësirën disk:
[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
Kërkojmë lidhjen me procesin përmes gdb
[user@localhost ~]$ gdb -p 10078
...
(gdb)
Shikojmë descriptorët e hapur të skedarëve:
(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
Shikojmë informacionin mbi deshifruesin e skedarit me numrin 3, i cili na intereson
(gdb) shell cat /proc/10078/fdinfo/3
pos: 8189952
flags: 0100001
mnt_id: 482
Duke pasur parasysh se çfarë thirrjeje sistemike bën Python (shih lart, ku ekzekutuam strace dhe gjetëm thirrjen open), duke përpunuar kodin tonë për hapjen e një skedari, ne bëjmë të njëjtën gjë vetë për emrin tonë të procesit, por bitët O_WRONLY|O_CREAT|O_TRUNC duhet t'i zëvendësojmë me vlerën numerike. Për këtë hapim burimet e bërthamës, për shembull dhe shikojmë se cilët flakët janë për çfarë
#define O_WRONLY 00000001
#define O_CREAT 00000100
#define O_TRUNC 00001000
Kombinojmë të gjitha vlerat në një, përfitojmë 00001101
Shkaktojmë thirrjen tonë nga gdb
(gdb) call open("/home/user/123.txt", 00001101,0666)
$1 = 4
Kështu morëm një deshifrik të ri me numrin 4 dhe një skedar të ri të hapur në një ndarje tjetër, kontrolle:
(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 kujtojmĂ« shembullin me pipe â si e ndryshon bash skedarĂ«t e deshifrimit, dhe tashmĂ« e kemi mĂ«suar thirrjen e sistemit dup2.
Po mundohemi të zëvendësojmë një skedar deshifrimi me një tjetër.
(gdb) call dup2(4,3)
$2 = 3
Po kontrollojmë:
(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
Mbyllim skedarin e deshifrimit 4, pasi nuk na nevojitet:
(gdb) call close (4)
$1 = 0
Dhe dalim nga 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
Kontrollojmë skedarin e ri:
[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
Siç shihet, të dhënat shkruhen në skedarin e ri, kontrolloni të vjetrin:
[user@localhost ~]$ ls -lah /mnt/logs/123.txt
-rw-rw-r-- 1 user user 7.9M Oct 8 11:08 /mnt/logs/123.txt
Të dhënat nuk janë humbur, aplikacioni funksionon, logjet shkruhen në një vend të ri.
Të komplikojmë pak detyrën
Le të supozojmë se të dhënat janë të rëndësishme për ne, por nuk kemi hapësirë në asnjë nga ndarjet dhe nuk mund të lidhim një disk.
Ajo që mund të bëjmë është të ridrejtojmë të dhënat tona diku, për shembull në pipe, dhe të dhënat nga pipe mund t'i ridrejtojmë në rrjet përmes ndonjë programi, për shembull netcat.
Mund të krijojmë një pipe të emëruar me komandën mkfifo. Ajo do të krijojë një skedar të pseudo në sistemin e skedarëve, edhe nëse nuk ka hapësirë të lirë mbi të.
Rivendosim aplikacionin dhe kontrollojmë:
[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
Nuk ka hapësirë në disk, por ne krijojmë me sukses një pipe të emëruar atje:
[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
Tani na duhet ndonjë mënyrë për të kanalizuar të gjitha të dhënat që kalojnë në këtë pipe në një server tjetër përmes rrjetit, për këtë na nevojitet përsëri netcat.
Në serverin remote-server.example.com startojmë
[user@localhost ~]$ nc -l 7777 > 123.txt
Në serverin tonë problematik startojmë në një terminal të veçantë
[user@localhost ~]$ nc remote-server.example.com 7777 < /mnt/logs/megapipe
Tani të gjitha të dhënat që do të kalojnë në pipe automatikisht do të kalojnë në stdin në netcat, e cila do t'i dërgojë në rrjet në portin 7777.
Gjithçka që na mbetet të bëjmë është të fillojmë të shkruajmë të dhënat tona në këtë pipe të emëruar.
Ne tashmë kemi një aplikacion të startuar:
[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
Nga të gjitha flamujt na nevojitet vetëm O_WRONLY pasi që skedari tashmë ekziston dhe nuk na nevojitet ta pastrojmë.
[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
Po kontrollojmë serverin e largët remote-server.example.com
[user@localhost ~]$ ls -lah 123.txt
-rw-rw-r-- 1 user user 38M Oct 8 14:21 123.txt
Të dhënat po shkojnë, kontrollojmë serverin problematik
[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
Të dhënat janë ruajtur, problemi është zgjidhur.
Duke shfrytëzuar këtë rast, përshëndes kolegët nga kompania Degiro.
Dëgjoni podkastet e Radio-T.
Të gjithë mirë.
Si detyrë shtëpie, propozoj të mendoni se çfarë do të jetë në skedarët e përshkruesve për procesin cat dhe sleep nëse ekzekutoni këtë komandë:
[user@localhost ~]$ cat /dev/zero 2>/dev/null| sleep 10000
Burimi: habr.com
