Skedari i përshkrimit në Linux me shembuj

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 open 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 man stdio dhe man stdout

  • 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. /dev/pts, 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 pipe 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 clone 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 dup2, 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 exec 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 open. 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 këtu 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

Bli njĂ« hosting tĂ« besueshĂ«m pĂ«r faqet me mbrojtje DDoS, VPS VDS serverĂ« đŸ”„ Bli njĂ« hosting tĂ« besueshĂ«m pĂ«r faqet me mbrojtje DDoS, VPS VDS serverĂ« | ProHoster