Deskriptor datoteke u Linuxu s primjerima

Jednom su me tijekom intervjua pitali što ćete učiniti ako ustanovite da servis ne radi zbog činjenice da je na disku ponestalo prostora?

Naravno, odgovorio sam da ću vidjeti što je na ovom mjestu i ako bude moguće, očistit ću to mjesto.
Zatim je ispitivač pitao, što ako nema slobodnog prostora na particiji, ali također ne vidite datoteke koje bi zauzele sav prostor?

Na ovo sam rekao da uvijek možete pogledati otvorene deskriptore datoteka, na primjer pomoću naredbe lsof, i shvatiti koja je aplikacija zauzela sav raspoloživi prostor, a zatim možete djelovati prema okolnostima, ovisno o tome jesu li podaci potrebni .

Ispitivač me prekinuo na posljednjoj riječi, dodajući na svoje pitanje: "Pretpostavimo da nam podaci nisu potrebni, to je samo zapisnik o otklanjanju pogrešaka, ali aplikacija ne radi jer ne može napisati ispravljanje pogrešaka"?

"U redu", odgovorio sam, "možemo isključiti debug u konfiguraciji aplikacije i ponovno je pokrenuti."
Ispitivač se usprotivio: "Ne, ne možemo ponovno pokrenuti aplikaciju, još uvijek imamo važne podatke pohranjene u memoriji, a važni klijenti povezani su sa samom uslugom, koje ne možemo prisiliti da se ponovno povežu."

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

Ispitivač je bio zadovoljan, ali ja nisam.

Onda sam pomislio, zašto osoba koja testira moje znanje ne kopa dublje? Ali što ako su podaci ipak važni? Što ako ne možemo ponovno pokrenuti proces, a proces piše u datotečni sustav na particiji na kojoj nema slobodnog prostora? Što ako ne možemo izgubiti ne samo podatke koji su već zapisani, već i podatke koje ovaj proces zapisuje ili pokušava zapisati?

Tuzik

Na početku svoje karijere pokušao sam stvoriti malu aplikaciju koja je trebala pohranjivati ​​korisničke podatke. I onda sam pomislio, kako mogu spojiti korisnika s njegovim podacima. Na primjer, imam Ivanova Ivana Ivanoviča i on ima neke informacije, ali kako se mogu sprijateljiti s njima? Mogu izravno istaknuti da pas po imenu “Tuzik” pripada upravo ovom Ivanu. Ali što ako promijeni ime i umjesto Ivana postane, na primjer, Olya? Tada će se ispostaviti da naša Olya Ivanovna Ivanova više neće imati psa, a naš će Tuzik i dalje pripadati nepostojećem Ivanu. U rješavanju ovog problema pomogla je baza podataka koja je svakom korisniku davala jedinstveni identifikator (ID), a moj Tuzik je bio vezan za taj ID, koji je, zapravo, bio samo serijski broj. Dakle, vlasnik tuzika je imao broj osobne iskaznice 2, te je u nekom trenutku pod ovom osobnom iskaznicom bio Ivan, a zatim je pod tom istom osobnom iskaznicom postala Olya. Problem ljudstva i stočarstva bio je praktički riješen.

Deskriptor datoteke

Problem datoteke i programa koji radi s tom datotekom otprilike je isti kao kod našeg psa i čovjeka. Pretpostavimo da sam otvorio datoteku pod nazivom ivan.txt i počeo u nju pisati riječ tuzik, ali sam uspio napisati samo prvo slovo “t” u datoteku, a tu je datoteku netko preimenovao, na primjer, u olya.txt. Ali datoteka ostaje ista i još uvijek želim u nju zabilježiti svog asa. Svaki put kad se datoteka otvori pozivom sustava otvoriti u bilo kojem programskom jeziku primam jedinstveni ID koji me upućuje na datoteku, ovaj ID je deskriptor datoteke. I uopće nije bitno što i tko će dalje s ovom datotekom, može se obrisati, može se preimenovati, promijeniti vlasnika ili oduzeti prava čitanja i pisanja, ja ću i dalje imati pristup jer sam u trenutku otvaranja datoteke imao prava čitati je i/ili pisati i uspio sam početi raditi s njom, što znači da to moram i nastaviti.

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

  • Deskriptor datoteke 0 naziva se STDIN i povezan je s 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 ispis poruka o pogreškama.

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

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

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

[user@localhost ]$ echo $$
15771

U drugoj konzoli trčimo

[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

Za potrebe ovog članka možete slobodno zanemariti deskriptor datoteke broj 255; otvorio ga je za svoje potrebe sam bash, a ne povezana biblioteka.

Sada su sve 3 datoteke deskriptora pridružene pseudoterminalnom uređaju /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 usmjeravanje

Ove 3 datoteke deskriptora možete lako nadjačati u bilo kojem procesu, uključujući bash, na primjer kroz cijev koja povezuje dva procesa, pogledajte

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

Ovu naredbu možete sami pokrenuti pomoću strace -f i vidjeti što se unutra događa, ali reći ću vam ukratko.

Naš nadređeni bash proces s PID-om 15771 analizira našu naredbu i točno razumije koliko naredbi želimo pokrenuti, u našem slučaju postoje dvije: cat i sleep. Bash zna da treba stvoriti dva procesa djeteta i spojiti ih u jednu cijev. Ukupno će bash trebati 2 podređena procesa i jednu cijev.

Bash pokreće sistemski poziv prije stvaranja podređenih procesa cijev i prima nove deskriptore datoteka na privremenom međuspremniku cijevi, ali ovaj međuspremnik još ne povezuje naša dva podređena procesa.

Za nadređeni proces, izgleda da već postoji kanal, 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 pomoću sistemskog poziva klon bash stvara dva procesa djeteta, 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 clone klonira proces zajedno sa svim deskriptorima datoteka, tako da će oni biti isti u roditeljskom procesu iu podređenim. Posao nadređenog procesa s PID-om 15771 je nadgledanje podređenih procesa, tako da jednostavno čeka odgovor od djece.

Stoga mu ne treba cijev i zatvara deskriptore datoteka pod brojevima 3 i 4.

U prvom podređenom bash procesu s PID-om 9004, sistemski poziv dup2, mijenja naš STDOUT deskriptor datoteke broj 1 u deskriptor datoteke koji pokazuje na cijev, u našem slučaju to je broj 3. Dakle, sve što prvi proces dijete s PID-om 9004 zapiše u STDOUT automatski će završiti u međuspremniku cijevi.

U drugom podređenom procesu s PID-om 9005, bash koristi dup2 za promjenu deskriptora datoteke STDIN broj 0. Sada će sve što će naš drugi bash s PID-om 9005 pročitati biti pročitano iz cijevi.

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

Namjerno zanemarujem deskriptor datoteke 255; koristi ga sam bash za interne potrebe i također će biti zatvoren u podređenim procesima.

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

U drugom podređenom procesu s PID-om 9005, bash pokreće drugu izvršnu datoteku koju smo naveli, u našem slučaju /usr/bin/sleep.

Exec sistemski poziv ne zatvara rukovanje datotekama osim ako nisu bile otvorene s O_CLOEXEC zastavom u vrijeme otvaranja poziva. U našem slučaju, nakon pokretanja izvršnih datoteka, svi trenutni deskriptori datoteka bit će spremljeni.

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

Za one koji nisu upoznati sa sistemskim pozivima koje bash koristi, toplo preporučujem pokretanje naredbi kroz strace i vidjeti što se interno događa, na primjer ovako:

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

Vratimo se našem problemu s malo prostora na disku i pokušaju spremanja podataka bez ponovnog pokretanja procesa. Napišimo mali program koji će pisati otprilike 1 megabajt u sekundi na disk. Štoviše, ako iz nekog razloga nismo uspjeli zapisati podatke na disk, jednostavno ćemo to ignorirati i pokušati ponovno zapisati podatke za sekundu. 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 svoja 3 standardna 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 zapisuju, pokušavamo promijeniti dopuštenja za 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 još uvijek zapisuju, iako naš korisnik nema dozvolu za pisanje u datoteku. Pokušajmo ga ukloniti:

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

Gdje su upisani podaci? I jesu li uopće napisani? 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 tretirati ovaj deskriptor datoteke kao našu staru datoteku, možemo je čitati, brisati 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šajmo izbrisati 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 vidite, veličina datoteke se samo povećava, a naš trunk nije radio. Pogledajmo dokumentaciju o pozivu sustava otvoriti. Ako prilikom otvaranja datoteke koristimo zastavu O_APPEND, tada kod svakog pisanja operativni sustav provjerava veličinu datoteke i upisuje podatke na sam kraj datoteke i to atomski. To omogućuje da više niti ili procesa zapisuju u istu datoteku. Ali u našem kodu ne koristimo ovu zastavu. Možemo vidjeti drugu veličinu datoteke u lsof nakon debla samo ako otvorimo datoteku za dodatno pisanje, š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:

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

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 pri izradi i testiranju programa koriste debuggere (primjerice GDB) ili razne razine logovanja u aplikaciji. Linux pruža mogućnost stvarnog pisanja i mijenjanja već pokrenutog programa, na primjer, mijenjanje vrijednosti varijabli, postavljanje prijelomne točke, itd., itd.

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

Kreirajmo datoteku za našu particiju, koju ćemo montirati kao zaseban 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 datotečni sustav:

[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 datotečni sustav:

[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

S našim vlasnikom kreiramo imenik:

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

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. Slobodnog prostora 0, 100% zauzeto.

Sjećamo se da prema uvjetima zadatka pokušavamo zabilježiti vrlo 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 “u hodu reprogramirati” 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 otvorene datoteke:

(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 informacije o deskriptoru datoteke broj 3, koji nas zanima

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

Imajući na umu koji sistemski poziv upućuje Python (pogledajte gore gdje smo pokrenuli strace i pronašli otvoreni poziv), prilikom obrade našeg koda za otvaranje datoteke, sami činimo isto u ime našeg procesa, ali trebamo O_WRONLY|O_CREAT| O_TRUNC bitovi zamjenjuju se numeričkom vrijednošću. Da biste to učinili, otvorite, na primjer, izvore kernela ovdje i pogledajte koje su zastave za što odgovorne

#define O_POGREŠNO 00000001
#define O_CREAT 00000100
#definiraj O_TRUNC 00001000

Kombiniramo sve vrijednosti u jednu, dobivamo 00001101

Pozivamo 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 - kako bash mijenja deskriptore datoteka, a sistemski poziv dup2 već smo naučili.

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 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 nove datoteke:

[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 su zapisani u novu datoteku, provjerimo 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 se ne gube, aplikacija radi, dnevnici se zapisuju na novu lokaciju.

Zakomplicirajmo malo zadatak

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

Ono što možemo učiniti je preusmjeriti naše podatke negdje, na primjer u pipe, a zatim preusmjeriti podatke iz cijevi u mrežu kroz neki program, na primjer netcat.
Možemo stvoriti imenovanu cijev pomoću naredbe mkfifo. Stvorit će pseudo datoteku na datotečnom sustavu čak i ako na njoj nema slobodnog prostora.

Ponovno 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 smo uspješno stvorili imenovanu cijev tamo:

[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 omotati sve podatke koji ulaze u ovu cijev na drugi poslužitelj putem mreže; za to je prikladan isti netcat.

Na poslužitelju remote-server.example.com pokrećemo

[user@localhost ~]$ nc -l 7777 > 123.txt 

Na našem problematičnom poslužitelju 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 mreži na port 7777.

Sve što trebamo učiniti je početi pisati svoje podatke u ovu imenovanu cijev.

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 poslužitelja 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 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 spremljeni, problem riješen.

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

Sve najbolje.

Kao domaću zadaću, predlažem da razmislite o tome što će biti u deskriptorima datoteka procesa cat i sleep ako pokrenete sljedeću naredbu:

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

Izvor: www.habr.com

Dodajte komentar