Deskriptor datoteke v Linuxu s primeri

Nekoč so me med intervjujem vprašali, kaj boste storili, če ugotovite, da storitev ne deluje zaradi dejstva, da je na disku zmanjkalo prostora?

Seveda sem odgovoril, da bom videl, kaj je na tem mestu, in če bo možno, bom prostor očistil.
Nato je anketar vprašal, kaj če na particiji ni prostega prostora, vendar tudi ne vidite datotek, ki bi zavzele ves prostor?

Na to sem rekel, da lahko vedno pogledate deskriptorje odprtih datotek, na primer z ukazom lsof, in razumete, katera aplikacija je zavzela ves razpoložljivi prostor, nato pa lahko ukrepate glede na okoliščine, odvisno od tega, ali so podatki potrebni .

Anketar me je prekinil pri zadnji besedi in k svojemu vprašanju dodal: »Recimo, da ne potrebujemo podatkov, to je samo dnevnik odpravljanja napak, vendar aplikacija ne deluje, ker ne more napisati odpravljanja napak«?

"V redu," sem odgovoril, "lahko izklopimo odpravljanje napak v konfiguraciji aplikacije in jo znova zaženemo."
Anketar je nasprotoval: "Ne, aplikacije ne moremo znova zagnati, še vedno imamo pomembne podatke shranjene v pomnilniku, pomembni odjemalci pa so povezani s samo storitvijo, ki je ne moremo prisiliti, da se ponovno poveže."

»V redu,« sem rekel, »če ne moremo znova zagnati aplikacije in podatki za nas niso pomembni, potem lahko preprosto počistimo to odprto datoteko prek deskriptorja datoteke, tudi če je ne vidimo v ukazu ls v datotečnem sistemu."

Anketar je bil zadovoljen, jaz pa ne.

Potem sem pomislil, zakaj se oseba, ki preverja moje znanje, ne poglobi? Kaj pa, če so podatki kljub vsemu pomembni? Kaj pa, če procesa ne moremo znova zagnati in proces piše v datotečni sistem na particijo, ki nima prostega prostora? Kaj pa, če ne moremo izgubiti ne le že zapisanih podatkov, ampak tudi podatkov, ki jih ta proces zapiše ali poskuša zapisati?

Tuzik

Na začetku svoje kariere sem poskušal ustvariti majhno aplikacijo, ki je morala shranjevati uporabniške podatke. In potem sem pomislil, kako lahko povežem uporabnika z njegovimi podatki. Na primer, imam Ivanova Ivana Ivanoviča in ima nekaj informacij, toda kako naj se spoprijateljim z njimi? Lahko neposredno poudarim, da pes Tuzik pripada prav temu Ivanu. Kaj pa, če spremeni ime in namesto Ivana postane na primer Olya? Potem se bo izkazalo, da naša Olya Ivanovna Ivanova ne bo imela več psa, naš Tuzik pa bo še vedno pripadal neobstoječemu Ivanu. To težavo je pomagala rešiti baza podatkov, ki je vsakemu uporabniku dala edinstven identifikator (ID), moj Tuzik pa je bil vezan na ta ID, ki je bil pravzaprav le serijska številka. Tako je imel lastnik asa identifikacijsko številko 2 in v nekem trenutku je bil Ivan pod to identifikacijo, nato pa je Olya postala pod isto identifikacijo. Problem človeštva in živinoreje je bil praktično rešen.

Deskriptor datoteke

Problem datoteke in programa, ki dela s to datoteko je približno enak kot pri našem psu in človeku. Recimo, da sem odprl datoteko z imenom ivan.txt in vanjo začel pisati besedo tuzik, vendar sem uspel v datoteko napisati samo prvo črko “t”, to datoteko pa je nekdo preimenoval, na primer v olya.txt. Toda datoteka ostaja ista in še vedno želim vanjo posneti svojega asa. Vsakič, ko se datoteka odpre s sistemskim klicem odprite v katerem koli programskem jeziku prejmem edinstven ID, ki me usmeri na datoteko; ta ID je deskriptor datoteke. In sploh ni pomembno, kaj in kdo naredi s to datoteko naprej, lahko jo izbrišem, lahko jo preimenujem, lahko spremenim lastnika ali lahko odvzamem pravice za branje in pisanje, še vedno bom imel dostop nanjo, ker sem imel v času odpiranja datoteke pravice za branje in/ali pisanje in mi je uspelo začeti delati z njo, kar pomeni, da moram to početi še naprej.

V Linuxu knjižnica libc odpre 3 datoteke deskriptorjev za vsako delujočo aplikacijo (proces), oštevilčene z 0,1,2. Več informacij najdete na povezavah človek stdio и man stdout

  • Deskriptor datoteke 0 se imenuje STDIN in je povezan z vnosom aplikacije
  • Datotečni deskriptor 1 se imenuje STDOUT in ga uporabljajo aplikacije za izhod podatkov, kot so ukazi za tiskanje
  • Deskriptor datoteke 2 se imenuje STDERR in ga aplikacije uporabljajo za izpisovanje sporočil o napakah.

Če v svojem programu odprete katero koli datoteko za branje ali pisanje, potem boste najverjetneje dobili prvi prosti ID in bo številka 3.

Seznam deskriptorjev datotek si lahko ogledate za kateri koli proces, če poznate njegov PID.

Na primer, odprimo konzolo bash in poglejmo PID našega procesa

[user@localhost ]$ echo $$
15771

V drugi konzoli poženimo

[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 namene tega članka lahko varno prezrete deskriptor datoteke 255; za svoje potrebe ga je odprl sam bash in ne povezana knjižnica.

Zdaj so vse 3 datoteke deskriptorjev povezane s psevdo terminalsko napravo /dev/pts, vendar lahko še vedno manipuliramo z njimi, na primer jih izvajamo v drugi konzoli

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

In v prvi konzoli bomo videli

[user@localhost ]$ hello world

Preusmeritev in cev

Te 3 datoteke deskriptorjev lahko preprosto preglasite v katerem koli procesu, vključno z bashom, na primer prek cevi, ki povezuje dva procesa, glejte

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

Ta ukaz lahko zaženete sami z strace -f in poglejte, kaj se dogaja notri, vendar vam bom povedal na kratko.

Naš nadrejeni proces bash s PID 15771 razčleni naš ukaz in natančno razume, koliko ukazov želimo izvesti, v našem primeru sta dva: cat in sleep. Bash ve, da mora ustvariti dva podrejena procesa in ju združiti v eno cev. Skupaj bo bash potreboval 2 podrejena procesa in eno cev.

Bash zažene sistemski klic, preden ustvari podrejene procese cevi in prejme nove deskriptorje datotek v začasni medpomnilnik cevi, vendar ta medpomnilnik še ne povezuje naših dveh podrejenih procesov.

Za nadrejeni proces je videti, kot da že obstaja cev, vendar še ni podrejenih procesov:

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

Nato uporabite sistemski klic klon bash ustvari dva podrejena procesa in naši trije procesi bodo videti takole:

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 pozabite, da klon klonira proces skupaj z vsemi deskriptorji datotek, tako da bodo enaki v nadrejenem procesu in v podrejenih. Naloga nadrejenega procesa s PID 15771 je spremljanje podrejenih procesov, tako da preprosto čaka na odgovor otrok.

Zato ne potrebuje cevi in ​​zapre deskriptorje datotek s številkami 3 in 4.

V prvem podrejenem procesu bash s PID 9004 sistemski klic dup2, spremeni naš deskriptor datoteke STDOUT številka 1 v deskriptor datoteke, ki kaže na cev, v našem primeru je to številka 3. Tako bo vse, kar prvi podrejeni proces s PID 9004 zapiše v STDOUT, samodejno končalo v medpomnilniku cevi.

V drugem podrejenem procesu s PID 9005 bash uporablja dup2 za spremembo deskriptorja datoteke STDIN številka 0. Zdaj bo vse, kar bo prebral naš drugi bash s PID 9005, prebrano iz cevi.

Po tem se deskriptorja datotek s številkami 3 in 4 prav tako zapreta v podrejenih procesih, saj se ne uporabljata več.

Datotečni deskriptor 255 namerno prezrem; bash ga uporablja za notranje namene in bo tudi zaprt v podrejenih procesih.

Nato v prvem podrejenem procesu s PID 9004 bash začne uporabljati sistemski klic exec izvršljivo datoteko, ki smo jo določili v ukazni vrstici, v našem primeru je to /usr/bin/cat.

V drugem podrejenem procesu s PID 9005 bash zažene drugo izvršljivo datoteko, ki smo jo določili, v našem primeru /usr/bin/sleep.

Sistemski klic exec ne zapre ročajev datotek, razen če so bile odprte z zastavico O_CLOEXEC v času odprtega klica. V našem primeru bodo po zagonu izvedljivih datotek shranjeni vsi trenutni deskriptorji datotek.

Preverite v 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

Kot lahko vidite, je edinstvena številka naše cevi v obeh procesih enaka. Tako imamo povezavo med dvema različnima procesoma z istim roditeljem.

Za tiste, ki niso seznanjeni s sistemskimi klici, ki jih uporablja bash, toplo priporočam, da ukaze zaženete prek strace in vidite, kaj se dogaja interno, na primer takole:

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

Vrnimo se k naši težavi s pomanjkanjem prostora na disku in poskusom shranjevanja podatkov brez ponovnega zagona postopka. Napišimo majhen program, ki bo na disk pisal približno 1 megabajt na sekundo. Poleg tega, če iz nekega razloga ne moremo zapisati podatkov na disk, bomo to preprosto ignorirali in poskušali znova zapisati podatke čez sekundo. V primeru, v katerem uporabljam Python, lahko uporabite kateri koli drug 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

Zaženimo program in poglejmo deskriptorje datotek

[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

Kot lahko vidite, imamo svoje 3 standardne deskriptorje datotek in še enega, ki smo ga odprli. Preverimo velikost datoteke:

[user@localhost ]$ ls -lah 123.txt 
-rw-rw-r-- 1 user user 117M Oct  7 16:30 123.txt

Podatki se zapisujejo, poskušamo spremeniti dovoljenja za datoteko:

[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 podatki še vedno pišejo, čeprav naš uporabnik nima dovoljenja za pisanje v datoteko. Poskusimo ga odstraniti:

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

Kje so zapisani podatki? In ali so sploh napisane? Preverjamo:

[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š datotečni deskriptor še vedno obstaja in ta datotečni deskriptor lahko obravnavamo kot svojo staro datoteko, lahko jo beremo, brišemo in kopiramo.

Poglejmo velikost datoteke:

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

Velikost datoteke je 19923457. Poskusimo počistiti datoteko:

[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

Kot lahko vidite, se velikost datoteke samo povečuje in naše deblo ni delovalo. Poglejmo dokumentacijo sistemskega klica odprite. Če pri odpiranju datoteke uporabimo zastavico O_APPEND, potem operacijski sistem ob vsakem pisanju preveri velikost datoteke in podatke zapiše čisto na konec datoteke, in to atomsko. To omogoča več nitim ali procesom, da pišejo v isto datoteko. Toda v naši kodi te zastavice ne uporabljamo. Drugačno velikost datoteke v lsof po deblu lahko vidimo le, če datoteko odpremo za dodatno pisanje, kar pomeni v naši kodi namesto

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

moramo postaviti

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

Preverjanje z zastavico "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

in z zastavo "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 že zagnanega procesa

Pogosto programerji pri ustvarjanju in testiranju programov uporabljajo razhroščevalnike (na primer GDB) ali različne nivoje beleženja v aplikaciji. Linux omogoča dejansko pisanje in spreminjanje že delujočega programa, na primer spreminjanje vrednosti spremenljivk, nastavitev prekinitvene točke itd., itd.

Če se vrnemo k prvotnemu vprašanju o premalo prostora na disku za pisanje datoteke, poskusimo simulirati težavo.

Ustvarimo datoteko za našo particijo, ki jo bomo priklopili kot ločen 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 ~]$

Ustvarimo datotečni sistem:

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

Namestite datotečni sistem:

[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

Z našim lastnikom ustvarimo imenik:

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

Odprimo datoteko samo za pisanje v našem programu:

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

Kosilo

[user@localhost ]$ python openforwrite.py 

Počakamo nekaj sekund

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

Imamo torej težavo, opisano na začetku tega članka. Prosti prostor 0, 100 % zaseden.

Ne pozabimo, da v skladu s pogoji naloge poskušamo zabeležiti zelo pomembne podatke, ki jih ni mogoče izgubiti. In hkrati moramo popraviti storitev brez ponovnega zagona postopka.

Recimo, da imamo še vedno prostor na disku, vendar v drugi particiji, na primer v /home.

Poskusimo sproti sprogramirati našo kodo.

Poglejmo PID našega procesa, ki je pojedel ves 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 prek gdb

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

Poglejmo deskriptorje odprtih datotek:

(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

Pogledamo informacije o deskriptorju datoteke št. 3, ki nas zanima

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

Upoštevajoč, kakšen sistemski klic opravi Python (glejte zgoraj, kjer smo zagnali strace in našli odprti klic), pri obdelavi naše kode za odpiranje datoteke storimo enako sami v imenu našega procesa, vendar potrebujemo O_WRONLY|O_CREAT| O_TRUNC bitov nadomesti s številsko vrednostjo. Če želite to narediti, na primer odprite izvorno kodo jedra tukaj in poglejte, katere zastave so odgovorne za kaj

#define O_NAPAČNO 00000001
#define O_CREAT 00000100
#define O_TRUNC 00001000

Vse vrednosti združimo v eno, dobimo 00001101

Klic vodimo iz gdb

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

Tako smo dobili nov deskriptor datoteke s številko 4 in novo odprto datoteko na drugi particiji, preverimo:

(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

Spomnimo se primera s cevjo - kako bash spremeni deskriptorje datotek, naučili pa smo se že sistemskega klica dup2.

En deskriptor datoteke poskušamo zamenjati z drugim

(gdb) call dup2(4,3)
$2 = 3

Preverjamo:

(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

Zapremo deskriptor datoteke 4, ker ga ne potrebujemo:

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

In zapustite 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

Preverjanje 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

Kot lahko vidite, so podatki zapisani v novo datoteko, preverimo staro:

[user@localhost ~]$ ls -lah /mnt/logs/123.txt 
-rw-rw-r-- 1 user user 7.9M Oct  8 11:08 /mnt/logs/123.txt

Podatki se ne izgubijo, aplikacija deluje, dnevniki se pišejo na novo lokacijo.

Malo zakomplicirajmo nalogo

Predstavljajmo si, da so nam podatki pomembni, vendar na nobeni od particij nimamo prostora in diska ne moremo povezati.

Kar lahko naredimo je, da svoje podatke nekam preusmerimo, na primer v cevovod, nato pa preusmerimo podatke iz cevi v omrežje prek nekega programa, na primer netcat.
Poimenovano cev lahko ustvarimo z ukazom mkfifo. V datotečnem sistemu bo ustvaril psevdo datoteko, tudi če v njej ni prostega prostora.

Ponovno zaženite aplikacijo in preverite:

[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

Na disku ni prostora, vendar smo tam uspešno ustvarili imenovano cev:

[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

Zdaj moramo vse podatke, ki gredo v to cev, nekako zaviti na drug strežnik prek omrežja; za to je primeren isti netcat.

Na strežniku remote-server.example.com izvajamo

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

Na našem problematičnem strežniku zaženemo v ločenem terminalu

[user@localhost ~]$ nc remote-server.example.com 7777 < /mnt/logs/megapipe 

Zdaj bodo vsi podatki, ki končajo v cevi, samodejno šli v stdin v netcat, ki jih bo poslal v omrežje na vrata 7777.

Vse kar moramo storiti je, da začnemo pisati svoje podatke v to imenovano cev.

Aplikacijo že izvajamo:

[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 vseh zastavic potrebujemo le O_WRONLY, saj datoteka že obstaja in nam je ni treba počistiti

[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

Preverjanje oddaljenega strežnika remote-server.example.com

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

Podatki prihajajo, preverimo težavni strežnik

[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

Podatki so shranjeni, problem rešen.

Ob tej priložnosti pozdravljam svoje kolege iz Degira.
Poslušajte poddaje Radia-T.

Vse dobro.

Kot domačo nalogo predlagam, da razmislite o tem, kaj bo v deskriptorjih procesnih datotek cat in sleep, če zaženete naslednji ukaz:

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

Vir: www.habr.com

Dodaj komentar