Deskriptor datoteke u Linuxu s primjerima

Jednom, tokom intervjua, upitali su me šta ćete učiniti ako ustanovite da servis ne radi zbog činjenice da je na disku ponestalo prostora?

Naravno, odgovorio sam da ću vidjeti šta je zauzelo ovo mjesto i, ako je moguće, očistiti mjesto.
Zatim je anketar upitao, šta ako na particiji nema slobodnog prostora, ali ne vidite ni fajlove koji bi zauzeli sav prostor?

Na ovo sam rekao da uvijek možete pogledati deskriptore otvorenih datoteka, na primjer naredbom lsof, i shvatiti koja je aplikacija zauzela sav raspoloživi prostor, a zatim možete djelovati u skladu sa okolnostima, ovisno o tome da li su podaci potrebni .

Intervjuer me prekinuo na posljednjoj riječi, dodajući na svoje pitanje: „Pretpostavimo da nam ne trebaju podaci, to je samo dnevnik otklanjanja grešaka, ali aplikacija ne radi jer ne može napisati otklanjanje grešaka“?

“U redu,” odgovorio sam, “možemo isključiti otklanjanje grešaka u konfiguraciji aplikacije i ponovo je pokrenuti.”
Anketar je prigovorio: “Ne, ne možemo ponovo pokrenuti aplikaciju, još uvijek imamo važne podatke pohranjene u memoriji, a važni klijenti su povezani na samu uslugu, koju ne možemo prisiliti da se ponovo poveže.”

„u redu“, rekao sam, „ako ne možemo ponovo pokrenuti aplikaciju i podaci nam nisu važni, onda možemo jednostavno obrisati ovu otvorenu datoteku kroz deskriptor datoteke, čak i ako je ne vidimo u komandi ls na sistemu datoteka.”

Anketar je bio zadovoljan, ali ja nisam.

Onda sam pomislio, zašto osoba koja testira moje znanje ne kopa dublje? Ali šta ako su podaci ipak važni? Šta ako ne možemo ponovo pokrenuti proces, a proces upisuje u sistem datoteka na particiji koja nema slobodnog prostora? Šta ako ne možemo izgubiti ne samo podatke koji su već upisani, već i podatke koje ovaj proces zapisuje ili pokušava da zapiše?

Tuzik

Na početku svoje karijere, pokušao sam da napravim malu aplikaciju koja je trebala da pohranjuje korisničke podatke. A onda sam pomislio kako da povežem korisnika sa njegovim podacima. Na primjer, imam Ivanova Ivana Ivanoviča, i on ima neke informacije, ali kako da se sprijateljim s njima? Mogu direktno da istaknem da pas po imenu Tuzik pripada upravo ovom Ivanu. Ali što ako promijeni ime i umjesto Ivan postane, na primjer, Olya? Tada će se ispostaviti da naša Olya Ivanovna Ivanova više neće imati psa, a naš Tuzik će i dalje pripadati nepostojećem Ivanu. Baza koja je svakom korisniku davala jedinstveni identifikator (ID) pomogla je u rješavanju ovog problema, a moj Tuzik je bio vezan za ovaj ID, koji je, u stvari, bio samo serijski broj. Dakle, vlasnik tuzika je imao matični broj 2, a u jednom trenutku pod tom ličnom kartom je bio i Ivan, a onda je Olja postala pod istom. Problem čovječanstva i stočarstva je praktično riješen.

Deskriptor fajla

Problem fajla i programa koji radi sa ovim fajlom je približno isti kao kod našeg psa i čoveka. Pretpostavimo da sam otvorio fajl pod nazivom ivan.txt i počeo da upisujem reč tuzik u njega, ali sam uspeo da upišem samo prvo slovo "t" u fajl, a ovaj fajl je neko preimenovao, na primer, u olya.txt. Ali fajl ostaje isti, a ja i dalje želim da upišem svog keca u njega. Svaki put kada se fajl otvori sistemskim pozivom otvoreno u bilo kom programskom jeziku dobijam jedinstveni ID koji me upućuje na datoteku, ovaj ID je deskriptor datoteke. I uopšte nije bitno šta i ko dalje radi sa ovim fajlom, može se obrisati, može se preimenovati, promeniti vlasnik ili oduzeti prava čitanja i pisanja, ja ću i dalje imati pristup na njega, jer sam u trenutku otvaranja fajla imao prava da ga čitam i/ili upisujem i uspeo sam da počnem da radim sa njim, što znači da moram da nastavim sa tim.

U Linuxu, libc biblioteka otvara 3 datoteke deskriptora za svaku pokrenutu aplikaciju (proces), označene brojevima 0,1,2. Više informacija možete pronaći na linkovima man stdio и man stdout

  • Deskriptor datoteke 0 naziva se STDIN i povezan je sa unosom aplikacije
  • Deskriptor datoteke 1 naziva se STDOUT i koriste ga aplikacije za izlaz podataka, kao što su naredbe za ispis
  • Deskriptor datoteke 2 naziva se STDERR i koriste ga aplikacije za izlaz poruka o grešci.

Ako u svom programu otvorite bilo koju datoteku za čitanje ili pisanje, tada ćete najvjerovatnije dobiti prvi besplatni ID i on će biti broj 3.

Lista deskriptora datoteka može se vidjeti za bilo koji proces ako znate njegov PID.

Na primjer, otvorimo bash konzolu i pogledamo PID našeg procesa

[user@localhost ]$ echo $$
15771

U drugoj konzoli krenimo

[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

Možete bezbedno da zanemarite deskriptor datoteke broj 255 za potrebe ovog članka; otvorio ga je za svoje potrebe sam bash, a ne povezana biblioteka.

Sada su sva 3 deskriptorska fajla povezana sa pseudo terminalnim uređajem /dev/pts, ali još uvijek možemo manipulirati njima, na primjer, pokrenuti ih u drugoj konzoli

[user@localhost ]$ echo "hello world" > /proc/15771/fd/0

A u prvoj konzoli ćemo vidjeti

[user@localhost ]$ hello world

Preusmjeravanje i cijevi

Ova 3 fajla deskriptora možete lako zaobići u bilo kojem procesu, uključujući i bash, na primjer kroz cijev koja povezuje dva procesa, vidi

[user@localhost ]$ cat /dev/zero | sleep 10000

Možete sami pokrenuti ovu naredbu strace -f i vidi šta se unutra dešava, ali ću ti ukratko reći.

Naš roditelj bash proces sa PID 15771 analizira našu naredbu i razumije tačno koliko komandi želimo da pokrenemo, u našem slučaju postoje dvije: cat i sleep. Bash zna da treba kreirati dva podređena procesa i spojiti ih u jednu cijevi. Ukupno, bash će trebati 2 podređena procesa i jedan pipe.

Bash pokreće sistemski poziv prije kreiranja podređenih procesa cijev i prima nove deskriptore datoteke na privremenom baferu cijevi, ali ovaj bafer još ne povezuje naša dva podređena procesa.

Za roditeljski proces, izgleda da već postoji cijev, ali još nema podređenih procesa:

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 koristite sistemski poziv Klon bash kreira dva podređena procesa, a naša tri procesa će izgledati ovako:

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

Ne zaboravite da klon klonira proces zajedno sa svim deskriptorima fajlova, tako da će oni biti isti u nadređenom procesu i u podređenim procesima. Posao roditeljskog procesa sa PID-om 15771 je da nadgleda podređene procese, tako da jednostavno čeka odgovor dece.

Stoga mu nije potrebna cev i zatvara deskriptore fajlova pod brojem 3 i 4.

U prvom bash procesu sa PID 9004, sistemski poziv dup2, mijenja naš deskriptor STDOUT datoteke broj 1 u deskriptor datoteke koji pokazuje na cev, u našem slučaju je to broj 3. Dakle, sve što prvi podređeni proces sa PID 9004 zapiše u STDOUT automatski će završiti u baferu cijevi.

U drugom podređenom procesu sa PID 9005, bash koristi dup2 da promijeni deskriptor datoteke STDIN broj 0. Sada će sve što će naš drugi bash sa PID 9005 pročitati biti pročitano iz cijevi.

Nakon toga, deskriptori datoteka pod brojevima 3 i 4 se također zatvaraju u podređenim procesima, jer se više ne koriste.

Namerno ignorišem deskriptor datoteke 255; koristi ga za interne svrhe sam bash i takođe će biti zatvoren u podređenim procesima.

Zatim, u prvom podređenom procesu sa PID-om 9004, bash počinje koristiti sistemski poziv exec izvršna datoteka koju smo naveli u komandnoj liniji, u našem slučaju je to /usr/bin/cat.

U drugom podređenom procesu sa PID 9005, bash pokreće drugi izvršni fajl koji smo naveli, u našem slučaju /usr/bin/sleep.

Exec sistemski poziv ne zatvara ručke datoteka osim ako nisu otvoreni sa O_CLOEXEC zastavicom u vrijeme kada je otvoren poziv napravljen. U našem slučaju, nakon pokretanja izvršnih datoteka, svi trenutni deskriptori datoteka će biti sačuvani.

Provjerite 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še cijevi je isti u oba procesa. Tako imamo vezu između dva različita procesa sa istim roditeljem.

Za one koji nisu upoznati sa sistemskim pozivima koje bash koristi, toplo preporučujem da pokrenete komande kroz strace i vidite šta se dešava interno, na primjer ovako:

strace -s 1024 -f bash -c "ls | grep hello"

Vratimo se na naš problem sa malim prostorom na disku i pokušajem da sačuvamo podatke bez ponovnog pokretanja procesa. Napišimo mali program koji će pisati približno 1 megabajt u sekundi na disk. Štaviše, ako iz nekog razloga nismo bili u mogućnosti da zapišemo podatke na disk, jednostavno ćemo to zanemariti i pokušati ponovo zapisati podatke u sekundi. U primjeru koji 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 deskriptore datoteka

[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 vidite, imamo naša 3 standardna deskriptora datoteka i još jedan koji smo otvorili. Provjerimo veličinu fajla:

[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 za fajl:

[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 to ukloniti:

[user@localhost ]$ sudo rm 123.txt 
[user@localhost ]$ ls 123.txt
ls: cannot access 123.txt: No such file or directory

Gdje su podaci upisani? I da li su uopšte napisane? 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 ga tretirati kao naš stari fajl, možemo ga čitati, brisati i kopirati.

Pogledajmo veličinu fajla:

[user@localhost ]$ lsof | grep 123.txt
python    31083             user    3w      REG                8,5   19923457   2621522 /home/user/123.txt

Veličina fajla je 19923457. Pokušajmo da obrišemo fajl:

[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 vidite, veličina datoteke se samo povećava i naš trunk nije radio. Pogledajmo dokumentaciju sistemskog poziva otvoreno. Ako koristimo oznaku O_APPEND prilikom otvaranja datoteke, tada sa svakim pisanjem, operativni sistem provjerava veličinu datoteke i upisuje podatke na sam kraj datoteke, i to atomski. Ovo omogućava višestrukim nitima ili procesima da upisuju u istu datoteku. Ali u našem kodu ne koristimo ovu zastavu. Možemo vidjeti drugačiju veličinu datoteke u lsof nakon debla samo ako otvorimo datoteku za dodatno pisanje, što znači u našem kodu umjesto

with open("123.txt", "w") as f:

moramo staviti

with open("123.txt", "a") as f:

Provjera sa "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 sa zastavom "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

Programiranje već pokrenutog procesa

Često programeri, prilikom kreiranja i testiranja programa, koriste debuggere (na primjer GDB) ili različite razine prijavljivanja u aplikaciju. Linux pruža mogućnost stvarnog pisanja i promjene programa koji je već pokrenut, na primjer, mijenjanje vrijednosti varijabli, postavljanje točke prekida, itd., itd.

Vraćajući se na prvobitno pitanje o nedovoljno prostora na disku za pisanje datoteke, pokušajmo simulirati problem.

Kreirajmo datoteku za našu particiju, koju ćemo montirati kao poseban disk:

[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 ~]$

Kreirajmo sistem datoteka:

[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 ~]$

Montirajte sistem datoteka:

[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

Kreiramo direktorij sa našim vlasnikom:

[user@localhost ~]$ sudo mkdir /mnt/logs
[user@localhost ~]$ sudo chown user: /mnt/logs

Otvorimo fajl za pisanje samo u našem programu:

with open("/mnt/logs/123.txt", "w") as f:

Pokreni

[user@localhost ]$ python openforwrite.py 

Čekamo nekoliko sekundi

[user@localhost ~]$ df -h | grep mnt
/dev/loop0      8.7M  8.0M     0 100% /mnt

Dakle, imamo problem opisan na početku ovog članka. Slobodni prostor 0, 100% zauzet.

Sjećamo se da se prema uslovima zadatka trudimo da snimimo veoma važne podatke koji se ne mogu izgubiti. A u isto vrijeme, moramo popraviti uslugu bez ponovnog pokretanja procesa.

Recimo da još uvijek imamo prostora na disku, ali na drugoj particiji, na primjer u /home.

Pokušajmo "reprogramirati u hodu" naš kod.

Pogledajmo PID našeg procesa, koji je pojeo sav prostor 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

Povežite se s procesom putem gdb-a

[user@localhost ~]$ gdb -p 10078
...
(gdb) 

Pogledajmo deskriptore otvorenih 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

Gledamo informaciju o deskriptoru datoteke broj 3, koja nas zanima

(gdb) shell cat /proc/10078/fdinfo/3
pos:    8189952
flags:  0100001
mnt_id: 482

Imajući na umu koji sistemski poziv obavlja Python (vidi gore gdje smo pokrenuli strace i pronašli otvoreni poziv), kada obrađujemo naš kod za otvaranje datoteke, mi sami radimo isto u ime našeg procesa, ali nam je potreban O_WRONLY|O_CREAT| O_TRUNC bitovi zamjenjuju se numeričkom vrijednošću. Da biste to učinili, otvorite izvore kernela, na primjer ovdje i pogledajte koje su zastave za šta odgovorne

#define O_WRONLY 00000001
#define O_CREAT 00000100
#define O_TRUNC 00001000

Kombiniramo sve vrijednosti u jednu, dobijemo 00001101

Pokrećemo naš poziv iz gdb-a

(gdb) call open("/home/user/123.txt", 00001101,0666)
$1 = 4

Tako smo dobili novi deskriptor datoteke sa 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 sa cijevi - kako bash mijenja deskriptore fajla, a već smo naučili sistemski poziv dup2.

Pokušavamo zamijeniti jedan deskriptor fajla 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 fajl deskriptor 4, pošto nam ne treba:

(gdb) call close (4)
$1 = 0

I izađite 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

Provjera novog fajla:

[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 vidite, podaci se upisuju u novu datoteku, provjerimo stari:

[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 se ne gube, aplikacija radi, zapisnici se zapisuju na novu lokaciju.

Hajde da malo zakomplikujemo zadatak

Zamislimo da su nam podaci važni, ali nemamo prostora na disku ni na jednoj particiji i ne možemo spojiti disk.

Ono što možemo učiniti je preusmjeriti naše podatke negdje, na primjer na cev, i zauzvrat preusmjeriti podatke iz cijevi u mrežu kroz neki program, na primjer netcat.
Možemo kreirati imenovanu cev sa naredbom mkfifo. Kreiraće pseudo datoteku na sistemu datoteka čak i ako na njoj nema slobodnog prostora.

Ponovo pokrenite aplikaciju i provjerite:

[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 tamo uspješno kreiramo imenovanu cijevi:

[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 moramo nekako umotati sve podatke koji idu u ovu cev na drugi server preko mreže; isti netcat je pogodan za to.

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 

Sada će svi podaci koji završe u cijevi automatski ići na stdin u netcat, koji će ih poslati u mrežu na portu 7777.

Sve što treba da uradimo je da počnemo da upisujemo naše podatke u ovu imenovanu cev.

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, trebamo samo O_WRONLY jer datoteka već postoji i ne moramo 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

Provjera udaljenog servera remote-server.example.com

[user@localhost ~]$ ls -lah 123.txt 
-rw-rw-r-- 1 user user 38M Oct  8 14:21 123.txt

Podaci stižu, provjeravamo problem servera

[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 sačuvani, problem je rešen.

Koristim ovu priliku da pozdravim moje kolege iz Degira.
Slušajte Radio T podcaste.

Dobro svima.

Kao domaći zadatak, predlažem vam da razmislite o tome šta će biti u procesu deskriptori datoteka cat i sleep ako pokrenete sljedeću naredbu:

[user@localhost ~]$ cat /dev/zero 2>/dev/null| sleep 10000

izvor: www.habr.com

Dodajte komentar