Jednom, na jednom intervjuu su me pitali šta bih učinio ako otkrijem da servis ne radi zbog toga što je na disku nestalo prostora?
Naravno, odgovorio sam da ću pogledati šta zauzima to mesto i ako je moguće, osloboditi prostor.
Tada me intervjuator upitao, a šta ako na particiji nema slobodnog prostora, ali takođe ne vidite datoteke koje bi činile to zauzeto mesto?
Na to sam rekao da uvek možemo pogledati otvorene datotečne deskriptore, na primer komandom lsof i shvatiti koja aplikacija zauzima sve dostupno mesto, a zatim možemo delovati u zavisnosti od situacije, u zavisnosti od toga da li su podaci potrebni.
Intervjuator me prekinuo na poslednjoj reči, dopunjujući svoje pitanje: „Pretpostavimo da nam podaci nisu potrebni, to je samo debag log, ali aplikacija ne radi zbog toga što ne može zapisati debag“?
„U redu“, rekao sam, „možemo isključiti debag u konfiguraciji aplikacije i ponovo je pokrenuti“.
Intervjuator je prigovorio: „Ne, aplikaciju ne možemo ponovo pokrenuti, u našoj memoriji se još uvek čuvaju važni podaci, a važni klijenti su povezani s samim servisom, koje ne možemo naterati da se ponovo povežu“.
„Pa dobro“, rekoh, „ako ne možemo ponovo pokrenuti aplikaciju i podaci nam nisu važni, onda možemo jednostavno očistiti taj otvoreni fajl preko deskriptora fajla, čak i ako ga ne vidimo u komandi ls na datotečnom sistemu“.
Intervjuator je bio zadovoljan, a ja ne.
Tada sam pomislio, zašto osoba koja proverava moje znanje ne kopa dublje? A šta ako su podaci ipak važni? Šta ako ne možemo ponovo pokrenuti proces, a taj proces piše na datotečni sistem u particiju na kojoj nema slobodnog mesta? Šta ako ne možemo izgubiti ne samo već napisane podatke, već i one podatke koje ovaj proces piše ili pokušava da zapiše?
Tuzik
Na početku svoje karijere pokušavao sam stvoriti malu aplikaciju u kojoj je trebalo pohranjivati informacije o korisnicima. Tada sam razmišljao kako ću povezati korisnika s njegovim podacima. Recimo, imam Ivana Ivanovića i ima neke podatke, ali kako ih spojiti? Mogu izravno navesti da pas po imenu „Tuzi” pripada tom Ivanu. No, što ako promijeni ime i umjesto Ivana postane, recimo, Olya? Tada će naša Olya Ivanovna Ivanova više nemati psa, a naš Tuzi će još uvijek pripadati nepostojećem Ivanu. Ovaj problem riješila je baza podataka, koja je svakom korisniku dodijelila jedinstveni identifikator (ID), a moj Tuzi se vezivao uz taj ID, koji je u suštini bio samo redni broj. Tako je vlasnik Tuzea imao ID broj 2, i u nekom trenutku taj ID imao je Ivan, a zatim je pod tim istim ID-jem postala Olya. Problem čovječanstva i stočarstva bio je praktički riješen.
Deskriptor datoteke
Problem datoteke i programa koji radi s tom datotekom otprilike je isti kao problem našeg psa i čovjeka. Pretpostavimo da sam otvorio datoteku pod imenom ivan.txt i počeo u nju zapisivati riječ tuzik, ali sam stigao napisati samo prvo slovo „t” u datoteku, i ova datoteka je od strane nekoga preimenovana, na primjer u olya.txt. No, datoteka je ostala ista, i još uvijek želim zapisati svog tuzika u nju. Svaki put prilikom otvaranja datoteke sistemskim pozivom u bilo kojem programskom jeziku dobivam jedinstveni ID koji mi ukazuje na datoteku, taj ID je deskriptor datoteke. I nije važno što i tko radi s tom datotekom dalje, može je izbrisati, može je preimenovati, može joj promijeniti vlasnika ili oduzeti prava za čitanje i pisanje, ja ću i dalje imati pristup njoj, jer u trenutku otvaranja datoteke imao sam pravo za njezino čitanje i/ili pisanje i uspio sam početi raditi s njom, što znači da moram nastaviti to raditi.
U Linuxu biblioteka libc otvara za svaku pokrenutu aplikaciju (proces) 3 deskriptor datoteka, s brojevima 0, 1, 2. Više informacija možete pronaći na linkovima i
- Deskriptor datoteke 0 naziva se STDIN i povezan je s unosom podataka u aplikaciju
- Deskriptorska datoteka 1 zove se STDOUT i koristi se od aplikacija za ispis podataka, poput komandi print.
- Deskriptorska datoteka 2 zove se STDERR i koristi se od aplikacija za ispis podataka vezanih uz greške.
Ako u vašem programu otvorite neku datoteku za čitanje ili pisanje, vjerojatno ćete dobiti prvi slobodni ID, a to će biti broj 3.
Popis deskriptora datoteka može se vidjeti za bilo koji proces, ako znate njegov PID.
Na primjer, otvorimo konzolu s bashom i pogledajmo PID našeg procesa.
[user@localhost ]$ echo $$
15771
U drugoj konzoli pokrenimo
[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
Deskriptorsku datoteku s brojem 255 možete slobodno ignorirati u ovom članku, otvorio ju je bash za svoje potrebe, a ne biblioteku koja je povezana.
Trenutno su sva 3 deskriptora datoteka povezana s uređajem pseudoterminala. , ali još uvijek njima možemo manipulirati, na primjer, pokrenimo u drugoj konzoli
[user@localhost ]$ echo "hello world" > /proc/15771/fd/0
I u prvoj konzoli ćemo vidjeti
[user@localhost ]$ hello world
Preusmjeri i Cijev
Možete lako prebrisati ova 3 deskriptora datoteka u bilo kojem procesu, uključujući i bash, na primjer, putem cijevi koja spaja dva procesa, gledamo.
[user@localhost ]$ cat /dev/zero | sleep 10000
Možete sami pokrenuti ovu komandu s strace -f i vidjeti što se događa unutar, ali ću ukratko objasniti.
Naš roditeljski proces bash s PID-om 15771 analizuje našu komandu i shvaća koliko zapravo komandi želimo pokrenuti, u našem slučaju dvije: cat i sleep. Bash zna da treba stvoriti dva podprocesa i povezati ih jednom cijevi. Ukupno, bash će trebati 2 podprocesa i jednu cijev.
Prije stvaranja podprocesa, bash pokreće sistemski poziv i dobiva nove deskriptore datoteka na privremeni spremnik cijevi, ali taj spremnik dosad ne povezuje naša dva podprocesa.
Za roditeljski proces izgleda kao da cijev već postoji, a podprocesi još nisu stvoreni:
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
Zatim pomoću sistemskog poziva bash stvara dva dječja procesa, a naših tri procesa izgledat će ovako:
PID naredba
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 naredba
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 naredba
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
Ne zaboravimo da clone klonira proces zajedno sa svim datotečnim descriptorima, tako da će u roditeljskom procesu i u dječjim biti identični. Zadatak roditeljskog procesa s PID 15771 je pratiti dječje procese, pa samo čeka odgovor od dječjih.
Shodno tome, pipe mu nije potreban, i zatvara datotečne deskriptore s brojevima 3 i 4.
U prvom dječjem procesu bash s PID 9004, sistemskim pozivom , mijenja naš STDOUT datotečni deskriptor s brojem 1 na datotečni deskriptor koji pokazuje na pipe, u našem slučaju to je broj 3. Tako će sve što prvi dječji proces s PID 9004 piše u STDOUT, automatski završiti u baferu pipe-a.
U drugom dječjem procesu s PID 9005 bash mijenja pomoću dup2 datotečni deskriptor STDIN s brojem 0. Sada će sve što naš drugi bash s PID 9005 čita, čitati iz pipe-a.
Nakon toga, u dječjim procesima takođe se zatvaraju datotečni deskriptori s brojevima 3 i 4, jer se više ne koriste.
Datotečni deskriptor 255 namjerno zanemarujem, koristi se za unutarnje potrebe samog basha i u dječjim procesima će također biti zatvoren.
Nakon toga, u prvom dječjem procesu s PID 9004 bash pokreće pomoću sistemskog poziva izvršni fajl koji smo naveli u komandnoj liniji, u našem slučaju to je /usr/bin/cat.
U drugom dječjem procesu s PID 9005 bash pokreće drugi izvršni fajl koji smo naveli, u našem slučaju to je /usr/bin/sleep.
Sistemski poziv exec ne zatvara datotečne deskriptore ako nisu otvoreni s oznakom O_CLOEXEC prilikom poziva open. U našem slučaju svi trenutni datotečni deskriptori će ostati nakon pokretanja izvršnih datoteka.
Provjeravamo u konzoli:
[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
Kao što vidite, jedinstveni broj našeg pipe-a u oba procesa se podudara. Tako imamo vezu između dva različita procesa s jednim roditeljem.
Za one koji nisu upoznati sa sistemskim pozivima koje koristi bash, svakako preporučujem pokretanje komandi kroz strace kako biste vidjeli što se događa unutra, na primjer:
strace -s 1024 -f bash -c "ls | grep hello"
Vratimo se na naš problem s nedostatkom prostora na disku i pokušajima da sačuvamo podatke bez ponovnog pokretanja procesa. Napisat ćemo malu program međusobnog pisanja na disk otprilike 1 megabajt u sekundi. Ako iz bilo kojeg razloga ne budemo mogli zapisivati podatke na disk, jednostavno ćemo to ignorirati i pokušati ponovno zapisati podatke nakon sekunde. U primjeru koristim Python, možete koristiti bilo koji drugi programski jezik.
[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
Pokrenimo program i pogledajmo datotečne deskriptore
[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
Kao što vidimo, imamo naš standardni 3 deskriptora datoteka i još jedan koji smo otvorili. Provjerimo veličinu datoteke:
[user@localhost ]$ ls -lah 123.txt
-rw-rw-r-- 1 user user 117M Oct 7 16:30 123.txt
Podaci se pišu, pokušavamo promijeniti dozvole na datoteku:
[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
Vidimo da se podaci i dalje pišu, iako naš korisnik nema dozvolu za pisanje u datoteku. Pokušajmo je izbrisati:
[user@localhost ]$ sudo rm 123.txt
[user@localhost ]$ ls 123.txt
ls: cannot access 123.txt: No such file or directory
Gdje se pišu podaci? I pišu li se uopće? Provjeravamo:
[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)
Da, naš deskriptor datoteke još uvijek postoji, i možemo raditi s tim deskriptorom kao s našom starom datotekom, možemo je čitati, prazniti i kopirati.
Pogledajmo veličinu datoteke:
[user@localhost ]$ lsof | grep 123.txt
python 31083 user 3w REG 8,5 19923457 2621522 /home/user/123.txt
Veličina datoteke je 19923457. Pokušavamo očistiti datoteku:
[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
Kao što vidimo, veličina datoteke se samo povećava i naš truncate nije uspio. Poslat ćemo se na dokumentaciju o sistemskom pozivu . Ako prilikom otvaranja datoteke koristimo zastavicu O_APPEND, tada sistem provjerava veličinu datoteke pri svakom pisanju i piše podatke na kraj datoteke atomarno. To omogućuje više niti ili procesa da pišu u istu datoteku. No, u našem kodu ne koristimo tu zastavicu. Očekujemo da ćemo vidjeti drugačiju veličinu datoteke u lsof nakon truncate samo ako otvorimo datoteku za dodatno pisanje, a to znači da u našem kodu umjesto
with open("123.txt", "w") as f:
moramo staviti
with open("123.txt", "a") as f:
Provjeravamo s "w" zastavicom
[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
i s «a» zastavicom
[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
Programiramo već pokrenuti proces
Često programeri koriste debagere (na primjer GDB) ili različite razine logiranja u aplikaciji prilikom stvaranja i testiranja programa. Linux omogućuje zapravo pisanje i promjenu već pokrenutog programa, na primjer, mijenjanje vrijednosti varijabli, postavljanje breakpointa i sl.
Vraćajući se na izvorno pitanje o nedostatku prostora na disku za zapis datoteke, pokušajmo simulirati problem.
Stvorimo datoteku za našu particiju koju ćemo montirati kao zasebni disk:
[user@localhost ~]$ dd if=/dev/zero of=~/tempfile_for_article.dd bs=1M count=10
10+0 zapisa unesenih
10+0 zapisa izlaznih
10485760 bajtova (10 MB) kopirano, 0.00525929 s, 2.0 GB/s
[user@localhost ~]$
Stvorimo datotečni sustav:
[user@localhost ~]$ mkfs.ext4 ~/tempfile_for_article.dd
mke2fs 1.42.9 (28-Dec-2013)
/home/user/tempfile_for_article.dd nije blok uređaj.
Nastaviti u svakom slučaju? (y,n) y
...
Pisanje superblokova i informacija o računovodstvu datotečnog sustava: gotovo
[user@localhost ~]$
Montiramo datotečni sustav:
[user@localhost ~]$ sudo mount ~/tempfile_for_article.dd /mnt/
[sudo] lozinka za korisnika:
[user@localhost ~]$ df -h | grep mnt
/dev/loop0 8.7M 172K 7.9M 3% /mnt
Stvaramo direktorij s našim vlasnikom:
[user@localhost ~]$ sudo mkdir /mnt/logs
[user@localhost ~]$ sudo chown user: /mnt/logs
Otvorimo datoteku samo za pisanje u našem programu:
with open("/mnt/logs/123.txt", "w") as f:
Pokrenite
[user@localhost ]$ python openforwrite.py
Čekamo nekoliko sekundi
[user@localhost ~]$ df -h | grep mnt
/dev/loop0 8.7M 8.0M 0 100% /mnt
Dakle, dobili smo problem opisan na početku ovog članka. Slobodnog prostora 0, zauzetog 100%.
Sjećamo se da prema uvjetima zadatka pokušavamo zapisati vrlo važne podatke koje ne možemo izgubiti. U isto vrijeme, trebamo popraviti uslugu bez ponovnog pokretanja procesa.
Pretpostavimo da ipak imamo prostora na disku, ali u drugoj particiji, na primjer u /home.
Pokušajmo "preprogramirati u hodu" naš kod.
Pogledajmo PID našeg procesa koji je zauzeo svu memoriju na disku:
[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
Spojimo se na proces putem gdb
[user@localhost ~]$ gdb -p 10078
...
(gdb)
Pogledajmo otvorene deskriptore datoteka:
(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
Pogledajmo informacije o deskriptoru datoteke s brojem 3, koji nas zanima
(gdb) shell cat /proc/10078/fdinfo/3
pos: 8189952
flags: 0100001
mnt_id: 482
Imajući na umu koji sustavni poziv Python izvršava (vidite iznad gdje smo pokrenuli strace i pronašli poziv open), obrađujući naš kod za otvaranje datoteke, radimo isto to sami u ime našeg procesa, ali bitovi O_WRONLY|O_CREAT|O_TRUNC trebaju se zamijeniti brojevnom vrijednošću. Za to otvaramo izvorne kodove jezgre, na primjer i gledamo koji su flagovi za što odgovorni
#define O_WRONLY 00000001
#define O_CREAT 00000100
#define O_TRUNC 00001000
Spajamo sve vrijednosti u jednu, dobijemo 00001101
Pokrećemo naš poziv iz gdb-a
(gdb) call open("/home/user/123.txt", 00001101,0666)
$1 = 4
Dakle, dobili smo novi deskriptor datoteke s brojem 4 i novu otvorenu datoteku na drugoj particiji, provjeravamo:
(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
Sjećamo se primjera s pipe-om — kako bash mijenja deskriptore datoteka, a već smo naučili sustavni poziv dup2.
Pokušavamo zamijeniti jedan deskriptor datoteke drugim
(gdb) call dup2(4,3)
$2 = 3
Provjeravamo:
(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
Zatvaramo deskriptor datoteke 4, jer nam nije potreban:
(gdb) call close (4)
$1 = 0
I izlazimo iz gdb-a
(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
Provjeravamo novu datoteku:
[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
Kao što vidimo, podaci se pišu u novu datoteku, provjeravamo staru:
[user@localhost ~]$ ls -lah /mnt/logs/123.txt
-rw-rw-r-- 1 user user 7.9M Oct 8 11:08 /mnt/logs/123.txt
Podaci nisu izgubljeni, aplikacija radi, logovi se pišu na novo mjesto.
Malo otežajmo zadatak
Zamislimo da su nam podaci važni, ali nemamo prostora na disku ni u jednoj od particija i ne možemo spojiti disk.
Što možemo učiniti jest preusmjeriti naše podatke negdje, na primjer u pipe, a podatke iz pipe-a zauzvrat preusmjeriti u mrežu putem neke aplikacije, poput netcat.
Možemo stvoriti imenovani pipe naredbom mkfifo. Ona će stvoriti pseudo-datoteku u datotečnom sustavu, čak i ako na njemu nema slobodnog mjesta.
Ponovo pokrećemo aplikaciju i provjeravamo:
[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
Nema prostora na disku, ali uspješno kreiramo imenovani 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
Sada trebamo nekako obraditi sve podatke koji idu u ovaj pipe i poslati ih na drugi server preko mreže, za to nam može poslužiti netcat.
Na serveru remote-server.example.com pokrećemo
[user@localhost ~]$ nc -l 7777 > 123.txt
Na našem problematičnom serveru pokrećemo u zasebnom terminalu
[user@localhost ~]$ nc remote-server.example.com 7777 < /mnt/logs/megapipe
Svi podaci koji uđu u pipe automatski će se prenijeti na stdin u netcat, koji će ih poslati u mrežu na port 7777.
Sve što nam preostaje je početi pisati naše podatke u ovaj imenovani pipe.
Već imamo pokrenutu aplikaciju:
[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
Od svih zastavica, potrebna nam je samo O_WRONLY jer datoteka već postoji i ne trebamo je brisati.
[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
Provjeravamo udaljeni poslužitelj remote-server.example.com
[user@localhost ~]$ ls -lah 123.txt
-rw-rw-r-- 1 user user 38M Oct 8 14:21 123.txt
Podaci dolaze, provjeravamo problematični poslužitelj
[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
Podaci su pohranjeni, problem je riješen.
Koristeći priliku, pozdravljam kolege iz tvrtke Degiro.
Slušajte podcaste Radio-T.
Svima dobra volja.
Kao domaću zadaću predlažem da razmislite što će biti u datotekama deskriptora procesa cat i sleep ako pokrenete ovu naredbu:
[user@localhost ~]$ cat /dev/zero 2>/dev/null| sleep 10000
Izvor: habr.com
