Një herë, në një intervistë, më pyetën se çfarë do të bënit nëse gjeni një shërbim të prishur për faktin se disku i ka mbaruar hapësira?
Natyrisht, u përgjigja se do të shihja se çfarë po bën ky vend dhe, nëse ishte e mundur, do ta pastroja vendin.
Pastaj intervistuesi pyeti, po sikur të mos ketë hapësirë të lirë në ndarje, por gjithashtu nuk i shihni skedarët që do të zënë të gjithë hapësirën?
Për këtë, thashë që gjithmonë mund të shikoni përshkruesit e skedarëve të hapur, për shembull, me komandën lsof dhe të kuptoni se cili aplikacion ka zënë të gjithë hapësirën në dispozicion, dhe më pas mund të veproni sipas rrethanave, varësisht nëse nevojiten të dhënat .
Intervistuesi më ndërpreu në fjalën e fundit, duke i shtuar pyetjes së tij: "Të supozojmë se nuk na duhen të dhënat, është thjesht një regjistër i korrigjimit, por aplikacioni nuk funksionon sepse nuk mund të shkruajë korrigjimin"?
"OK," u përgjigja, "mund të çaktivizojmë korrigjimin në konfigurimin e aplikacionit dhe ta rifillojmë atë."
Intervistuesi kundërshtoi: "Jo, ne nuk mund ta rinisim aplikacionin, ne kemi ende të dhëna të rëndësishme në memorie dhe klientët e rëndësishëm janë të lidhur me vetë shërbimin, të cilin nuk mund ta detyrojmë ta rilidhëm."
"Në rregull," thashë, "nëse nuk mund ta rinisim aplikacionin dhe nuk na interesojnë të dhënat, atëherë mund ta pastrojmë këtë skedar të hapur nëpërmjet përshkruesit të skedarit, edhe nëse nuk e shohim atë në ls komandë në sistemin e skedarëve."
Intervistuesi ishte i kënaqur, por unë jo.
Pastaj mendova, pse personi që teston njohuritë e mia nuk gërmon më thellë? Por, çka nëse të dhënat janë të rëndësishme në fund të fundit? Po sikur të mos mund ta rifillojmë procesin, dhe në të njëjtën kohë ky proces shkruan në sistemin e skedarëve në një ndarje që nuk ka hapësirë të lirë? Po sikur të mos humbasim jo vetëm të dhënat e shkruara tashmë, por edhe të dhënat që ky proces po shkruan ose po përpiqet të shkruajë?
Tuzik
Në fillim të karrierës sime, po përpiqesha të krijoja një aplikacion të vogël që duhej të ruante informacione rreth përdoruesve. Dhe pastaj mendova, si mund ta përputh përdoruesin me të dhënat e tij. Për shembull, unë kam Ivanov Ivan Ivanovich, dhe ai ka disa të dhëna, por si të miqësohemi me ta? Mund të theksoj drejtpërdrejt se qeni me emrin "Tuzik" i përket të njëjtit Ivan. Por, çka nëse ai ndryshon emrin e tij dhe në vend të Ivan bëhet, për shembull, Olya? Atëherë do të rezultojë se Olya Ivanovna Ivanova jonë nuk do të ketë më qen, dhe Tuzik ynë do t'i përkasë akoma Ivanit inekzistent. Baza e të dhënave ndihmoi në zgjidhjen e këtij problemi, i cili i dha çdo përdoruesi një identifikues unik (ID), dhe Tuzik im ishte i lidhur me këtë ID, i cili, në fakt, ishte vetëm një numër serial. Kështu, pronari i tuzik ishte me numër ID 2, dhe në një moment në kohë Ivan ishte nën këtë ID, dhe më pas Olya u bë nën të njëjtën ID. Problemi i njerëzimit dhe blegtorisë u zgjidh praktikisht.
Përshkruesi i skedarit
Problemi i një skedari dhe një programi që funksionon me këtë skedar është pothuajse i njëjtë me qenin dhe njeriun tonë. Supozoni se hapa një skedar të quajtur ivan.txt dhe fillova të shkruaj fjalën tuzik në të, por arrita të shkruaj vetëm shkronjën e parë "t" në skedar, dhe ky skedar u riemërua nga dikush, për shembull, në olya.txt. Por skedari është i njëjtë dhe unë ende dua t'i shkruaj asin tim. Sa herë që hapni një skedar me një thirrje sistemi
Në Linux, biblioteka libc hap 3 skedarë përshkrues për çdo aplikacion (proces) në ekzekutim, me numrat 0,1,2. Më shumë informacion mund të gjeni në lidhjet
- Përshkruesi i skedarit 0 quhet STDIN dhe shoqërohet me hyrjen e aplikacionit.
- Përshkruesi i skedarit 1 quhet STDOUT dhe përdoret nga aplikacionet e daljes siç janë komandat e printimit.
- Përshkruesi i skedarit 2 quhet STDERR dhe përdoret nga aplikacionet për të nxjerrë mesazhe gabimi.
Nëse në programin tuaj hapni ndonjë skedar për lexim ose shkrim, atëherë me shumë mundësi do të merrni ID-në e parë falas dhe do të jetë numri 3.
Ju mund të shihni listën e përshkruesve të skedarëve për çdo proces nëse e dini PID-në e tij.
Për shembull, le të hapim një tastierë me bash dhe të shohim PID-in e procesit tonë
[user@localhost ]$ echo $$
15771
Në tastierën e dytë, ekzekutoni
[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
Ju mund të injoroni me siguri përshkruesin e skedarit me numër 255 në kuadrin e këtij artikulli, ai u hap për nevojat tuaja nga vetë bash, dhe jo nga biblioteka e lidhur.
Tani të 3 skedarët përshkrues janë të lidhur me pajisjen pseudo-terminale
[user@localhost ]$ echo "hello world" > /proc/15771/fd/0
Dhe në tastierën e parë do të shohim
[user@localhost ]$ hello world
Redirect and Pipe
Ju mund t'i anashkaloni lehtësisht këta 3 skedarë përshkrues në çdo proces, duke përfshirë në bash, për shembull, përmes një tubi (tub) që lidh dy procese, shih
[user@localhost ]$ cat /dev/zero | sleep 10000
Ju mund ta ekzekutoni vetë këtë komandë strace -f dhe shikoni se çfarë po ndodh brenda, por unë do ta bëj shkurt.
Procesi ynë bash prind me PID 15771 analizon komandën tonë dhe kupton saktësisht se sa komanda duam të ekzekutojmë, në rastin tonë ka dy prej tyre: cat dhe gjumë. Bash e di se duhet të krijojë dy procese fëmijësh dhe t'i bashkojë ato në një tub. Në total, bash do të ketë nevojë për 2 procese fëmijësh dhe një tub.
Përpara krijimit të proceseve të fëmijëve, bash kryen një thirrje sistemi
Për procesin prind, duket sikur tubi është tashmë aty, por nuk ka ende procese fëmijë:
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 përdorni thirrjen e sistemit
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 harroni se kloni klonon procesin së bashku me të gjithë përshkruesit e skedarëve, kështu që ata do të jenë të njëjtë në procesin prind dhe në ato fëmijë. Detyra e procesit prind me PID 15771 është të monitorojë proceset e fëmijës, kështu që ai vetëm pret një përgjigje nga fëmijët.
Prandaj, ai nuk ka nevojë për një tub, dhe ai mbyll përshkruesit e skedarëve me numrat 3 dhe 4.
Në procesin e parë bash fëmijë me PID 9004, thirrja e sistemit
Në procesin e dytë të fëmijës me PID 9005, bash dup2s skedarin në përshkruesin STDIN numër 0. Tani gjithçka që bash-i ynë i dytë me PID 9005 do të lexojë do të lexohet nga tubacioni.
Pas kësaj, përshkruesit e skedarëve me numrat 3 dhe 4 mbyllen gjithashtu në proceset fëmijë, pasi ato nuk përdoren më.
Unë e shpërfill qëllimisht përshkruesin e skedarit 255, ai përdoret nga brenda nga vetë bash dhe gjithashtu do të mbyllet në proceset e fëmijëve.
Më pas, në procesin e parë të fëmijës me PID 9004, bash fillon me një thirrje sistemi
Në procesin e dytë fëmijë me PID 9005, bash ekzekuton ekzekutuesin e dytë që kemi specifikuar, në rastin tonë /usr/bin/sleep.
Thirrja e sistemit exec nuk i mbyll përshkruesit e skedarëve nëse nuk janë hapur me flamurin O_CLOEXEC në kohën kur u ekzekutua thirrja e hapur. Në rastin tonë, pas ekzekutimit të skedarëve të ekzekutueshëm, të gjithë përshkruesit aktual të skedarëve do të ruhen.
Ne kontrollojmë në tastierë:
[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ç mund ta shihni, numri unik i tubit tonë është i njëjtë në të dy proceset. Kështu, ne kemi një lidhje midis dy proceseve të ndryshme me të njëjtin prind.
Për ata që nuk janë të njohur me thirrjet e sistemit që përdor bash, unë rekomandoj shumë të ekzekutoni komandat përmes strace dhe të shihni se çfarë ndodh brenda, për shembull, si kjo:
strace -s 1024 -f bash -c "ls | grep hello"
Le të kthehemi te problemi ynë me mbarimin e hapësirës në disk dhe përpjekjen për të ruajtur të dhënat pa rifilluar procesin. Le të shkruajmë një program të vogël që do të shkruajë në disk rreth 1 megabajt për sekondë. Për më tepër, nëse për ndonjë arsye nuk mund të shkruanim të dhëna në disk, thjesht do ta injorojmë këtë dhe do të përpiqemi t'i shkruajmë të dhënat përsëri në një sekondë. Në shembullin që unë jam duke përdorur Python, ju 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
Drejtoni programin dhe shikoni përshkruesit e 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ç mund ta shihni, ne kemi 3 përshkruesit tanë standardë të skedarëve dhe një tjetër që kemi hapur. 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 janë shkruar, ne përpiqemi të ndryshojmë të drejtat për skedarin:
[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
Ne shohim që të dhënat janë ende duke u shkruar, megjithëse përdoruesi ynë nuk ka të drejtë të shkruajë në skedar. Le të përpiqemi ta heqim atë:
[user@localhost ]$ sudo rm 123.txt
[user@localhost ]$ ls 123.txt
ls: cannot access 123.txt: No such file or directory
Ku shkruhen të dhënat? Dhe a janë shkruar fare? Ne 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 (deleted)
Po, përshkruesi ynë i skedarit ekziston ende, dhe ne mund të punojmë me këtë përshkrues skedari si skedari ynë i vjetër, mund ta lexojmë, ta pastrojmë dhe ta kopjojmë.
Shikoni 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 është 19923457. Përpjekja për të pastruar 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ç mund ta shihni, madhësia e skedarit rritet vetëm dhe trungu ynë nuk funksionoi. Le të kthehemi te dokumentacioni për thirrjen e sistemit
with open("123.txt", "w") as f:
duhet të vendosim
with open("123.txt", "a") as f:
Kontrollimi 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
Programimi i një procesi tashmë të ekzekutuar
Shpesh, kur krijojnë dhe testojnë një program, programuesit përdorin korrigjues (për shembull, GDB) ose nivele të ndryshme të regjistrimit në aplikacion. Linux ofron mundësinë për të shkruar dhe ndryshuar në të vërtetë një program tashmë të ekzekutuar, të tilla si ndryshimi i vlerave të variablave, vendosja e një pikë ndërprerjeje, e kështu me radhë e kështu me radhë.
Duke iu rikthyer pyetjes origjinale në lidhje me mungesën e hapësirës në disk për të shkruar një skedar, le të përpiqemi të simulojmë problemin.
Le të krijojmë një skedar për ndarjen tonë, të cilën do ta montojmë 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 ~]$
Le 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 ~]$
Le të montojmë 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
Krijoni një drejtori me pronarin tonë:
[user@localhost ~]$ sudo mkdir /mnt/logs
[user@localhost ~]$ sudo chown user: /mnt/logs
Le të hapim skedarin për të shkruar vetëm në programin tonë:
with open("/mnt/logs/123.txt", "w") as f:
Nisja
[user@localhost ]$ python openforwrite.py
Në pritje për disa sekonda
[user@localhost ~]$ df -h | grep mnt
/dev/loop0 8.7M 8.0M 0 100% /mnt
Pra, morëm problemin e përshkruar në fillim të këtij artikulli. Hapësirë e lirë 0, e zënë 100%.
Kujtojmë se sipas kushteve të problemit po mundohemi të regjistrojmë të dhëna shumë të rëndësishme që nuk mund të humbasin. Dhe duke e bërë këtë, ne duhet të rregullojmë shërbimin pa rifilluar procesin.
Le të themi se kemi ende hapësirë në disk, por në një ndarje tjetër, për shembull, në / shtëpi.
Le të përpiqemi të "riprogramojmë në fluturim" kodin tonë.
Ne shikojmë PID-in e procesit tonë, i cili hante të gjithë hapësirën e diskut:
[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
Lidhja me një proces me gdb
[user@localhost ~]$ gdb -p 10078
...
(gdb)
Ne shikojmë përshkruesit e skedarëve të hapur:
(gdb) shell ls -lah /proc/10078/fd/
total 0
dr-x------ 2 user user 0 Oct 8 11:06 .
dr-xr-xr-x 9 user user 0 Oct 8 11:06 ..
lrwx------ 1 user user 64 Oct 8 11:09 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:09 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct 8 11:06 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct 8 11:09 3 -> /mnt/logs/123.txt
Ne shikojmë informacionin për përshkruesin 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ë bën thirrja e sistemit Python (shih më lart se ku kemi drejtuar strace dhe kemi gjetur një thirrje të hapur), ndërsa përpunojmë kodin tonë për të hapur një skedar, ne bëjmë të njëjtën gjë vetë në emër të procesit tonë, por na duhet O_WRONLY|O_CREAT| Bitët O_TRUNC zëvendësohen me një vlerë numerike. Për ta bërë këtë, për shembull, hapni burimet e kernelit
#define O_WRONLY 00000001
#define O_CREAT 00000100
#define O_TRUNC 00001000
Ne kombinojmë të gjitha vlerat në një, marrim 00001101
Kryerja e thirrjes sonë nga gdb
(gdb) call open("/home/user/123.txt", 00001101,0666)
$1 = 4
Pra, ne morëm një përshkrues të ri skedari me numrin 4 dhe një skedar të ri të hapur në një ndarje tjetër, kontrolloni:
(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 e mbajmë mend shembullin me tub - se si bash ndryshon përshkruesit e skedarëve dhe kemi mësuar tashmë thirrjen e sistemit dup2.
Përpjekja për të zëvendësuar një përshkrues skedari me një tjetër
(gdb) call dup2(4,3)
$2 = 3
kontrolloni:
(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
Mbyllni përshkruesin e skedarit 4, pasi nuk na nevojitet:
(gdb) call close (4)
$1 = 0
Dhe dilni 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
Kontrollimi i skedarit të 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ç mund ta shihni, të dhënat janë shkruar në një skedar të ri, ne kontrollojmë atë të vjetër:
[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 humbasin, aplikacioni funksionon, regjistrat shkruhen në një vend të ri.
Le t'i bëjmë gjërat pak më të vështira
Imagjinoni që të dhënat janë të rëndësishme për ne, por ne nuk kemi hapësirë në disk në asnjë nga ndarjet dhe nuk mund ta lidhim diskun.
Ajo që mund të bëjmë është të ridrejtojmë të dhënat tona diku, për shembull, në një tub, dhe t'i ridrejtojmë të dhënat nga tubacioni në rrjet përmes ndonjë programi, siç është netcat.
Mund të krijojmë një tub me emër me komandën mkfifo. Do të krijojë një pseudo skedar në sistemin e skedarëve, edhe nëse nuk ka hapësirë të lirë në të.
Rinisni aplikacionin dhe kontrolloni:
[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ë tub me emër 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 duhet të mbështjellim disi të gjitha të dhënat që futen në këtë tub në një server tjetër përmes rrjetit, i njëjti netcat është i përshtatshëm për këtë.
Në serverin remote-server.example.com, ekzekutoni
[user@localhost ~]$ nc -l 7777 > 123.txt
Në serverin tonë të problemeve, ekzekutoni në një terminal të veçantë
[user@localhost ~]$ nc remote-server.example.com 7777 < /mnt/logs/megapipe
Tani të gjitha të dhënat që futen në tub do të shkojnë automatikisht në stdin në netcat, i cili do t'i dërgojë ato në rrjet në portin 7777.
Gjithçka që duhet të bëjmë është të fillojmë të shkruajmë të dhënat tona në këtë tub me emër.
Ne tashmë kemi një aplikacion që funksionon:
[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ë gjithë flamujt, na duhen vetëm O_WRONLY pasi skedari tashmë ekziston dhe nuk kemi nevojë ta pastrojmë atë
[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 kontrollon serverin në distancë 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 vijnë, ne kontrollojmë serverin e problemit
[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 ruhen, problemi zgjidhet.
Gjej rastin të përshëndes kolegët nga Degiro.
Dëgjoni podkastet e Radio T.
Mirë për të gjithë.
Si detyrë shtëpie, unë propozoj të mendoni se çfarë do të jenë në përshkruesit e skedarëve të procesit të maceve dhe gjumit nëse ekzekutoni komandën e mëposhtme:
[user@localhost ~]$ cat /dev/zero 2>/dev/null| sleep 10000
Burimi: www.habr.com