Faila deskriptors operētājsistēmā Linux ar piemēriem

Reiz intervijas laikā man jautāja, ko jūs darīsiet, ja atklāsiet, ka kāds pakalpojums nedarbojas, jo diskā ir beigusies vieta?

Protams, es atbildēju, ka paskatÄ«Å”os, ko Ŕī vieta aizņem, un, ja iespējams, uzkopÅ”u vietu.
Pēc tam intervētājs jautāja, kā būtu, ja nodalījumā nav brīvas vietas, bet jūs arī neredzat nevienu failu, kas aizņemtu visu vietu?

Uz to es teicu, ka vienmēr var apskatÄ«t atvērto failu deskriptorus, piemēram, ar komandu lsof, un saprast, kura lietojumprogramma ir aizņēmusi visu pieejamo vietu, un tad var rÄ«koties atbilstoÅ”i apstākļiem, atkarÄ«bā no tā, vai dati ir nepiecieÅ”ami. .

Intervētājs mani pārtrauca uz pēdējo vārdu, papildinot savu jautājumu: "Pieņemsim, ka mums nav vajadzÄ«gi dati, tas ir tikai atkļūdoÅ”anas žurnāls, bet lietojumprogramma nedarbojas, jo tā nevar uzrakstÄ«t atkļūdoÅ”anu"?

"Labi," es atbildēju, "mēs varam izslēgt atkļūdoÅ”anu lietojumprogrammas konfigurācijā un restartēt."
Intervētājs iebilda: "Nē, mēs nevaram restartēt lietojumprogrammu, mums joprojām ir svarÄ«gi dati, kas glabājas atmiņā, un svarÄ«gi klienti ir savienoti ar paÅ”u pakalpojumu, kuru mēs nevaram piespiest atkārtoti savienot."

"Labi," es teicu, "ja mēs nevaram restartēt lietojumprogrammu un dati mums nav svarÄ«gi, mēs varam vienkārÅ”i notÄ«rÄ«t Å”o atvērto failu, izmantojot faila deskriptoru, pat ja mēs to neredzam komandā ls failu sistēmā."

Intervētājs bija apmierināts, bet es nē.

Tad es domāju, kāpēc cilvēks, kurÅ” pārbauda manas zināŔanas, neiedziļinās? Bet ko darÄ«t, ja dati tomēr ir svarÄ«gi? Ko darÄ«t, ja mēs nevaram restartēt procesu un process ieraksta failu sistēmu nodalÄ«jumā, kurā nav brÄ«vas vietas? Ko darÄ«t, ja mēs nevaram zaudēt ne tikai jau ierakstÄ«tos datus, bet arÄ« datus, kurus Å”is process raksta vai mēģina rakstÄ«t?

Tuzik

Savas karjeras sākumā es mēģināju izveidot nelielu lietojumprogrammu, kurā bija jāglabā lietotāja informācija. Un tad es domāju, kā es varu saskaņot lietotāju ar viņa datiem. Piemēram, man ir Ivanovs Ivans Ivanovičs, un viņam ir zināma informācija, bet kā es varu ar viņiem sadraudzēties? Varu tieÅ”i norādÄ«t, ka suns vārdā ā€œTuzikā€ pieder tieÅ”i Å”im Ivanam. Bet ja viņŔ maina vārdu un Ivana vietā kļūst, piemēram, Olja? Tad izrādÄ«sies, ka mÅ«su Oļai Ivanovnai Ivanovai vairs nebÅ«s suņa, un mÅ«su Tuziks joprojām piederēs neesoÅ”ajam Ivanam. Datubāze, kas katram lietotājam pieŔķīra unikālu identifikatoru (ID), palÄ«dzēja atrisināt Å”o problēmu, un mans Tuzik tika piesaistÄ«ts Å”im ID, kas patiesÄ«bā bija tikai sērijas numurs. Tādējādi dūža Ä«paÅ”niekam bija ID numurs 2, un kādā brÄ«dÄ« Ivans atradās ar Å”o ID, un tad Olya kļuva ar to paÅ”u ID. Cilvēces un lopkopÄ«bas problēma tika praktiski atrisināta.

Faila deskriptors

Faila un programmas, kas darbojas ar Å”o failu, problēma ir aptuveni tāda pati kā mÅ«su sunim un cilvēkam. Pieņemsim, ka es atvēru failu ar nosaukumu ivan.txt un sāku tajā rakstÄ«t vārdu tuzik, bet failā paspēju ierakstÄ«t tikai pirmo burtu ā€œtā€, un kāds Å”o failu pārdēvēja, piemēram, par olya.txt. Bet fails paliek nemainÄ«gs, un es joprojām gribu tajā ierakstÄ«t savu dÅ«zi. Katru reizi, kad fails tiek atvērts ar sistēmas zvanu atvērt jebkurā programmÄ“Å”anas valodā es saņemu unikālu ID, kas norāda uz failu, Å”is ID ir faila deskriptors. Un pilnÄ«gi vienalga, ko un kurÅ” ar Å”o failu dara tālāk, to var dzēst, var pārdēvēt, var mainÄ«t Ä«paÅ”nieku vai atņemt lasÄ«Å”anas un rakstÄ«Å”anas tiesÄ«bas, man joprojām bÅ«s pieeja uz to, jo faila atvērÅ”anas brÄ«dÄ« man bija tiesÄ«bas to lasÄ«t un/vai rakstÄ«t un man izdevās sākt ar to strādāt, kas nozÄ«mē, ka man tas ir jāturpina.

Operētājsistēmā Linux libc bibliotēka atver 3 deskriptorus katrai lietojumprogrammai (procesam), kas numurētas ar 0,1,2. Vairāk informācijas var atrast saitēs man stdio Šø man stdout

  • Faila deskriptors 0 tiek saukts par STDIN un ir saistÄ«ts ar lietojumprogrammas ievadi
  • Faila deskriptors 1 tiek saukts par STDOUT, un lietojumprogrammas to izmanto, lai izvadÄ«tu datus, piemēram, drukāŔanas komandas
  • Faila deskriptors 2 tiek saukts par STDERR, un lietojumprogrammas to izmanto kļūdu ziņojumu izvadÄ«Å”anai.

Ja programmā atverat kādu failu lasīŔanai vai rakstīŔanai, visticamāk, jūs iegūsit pirmo bezmaksas ID, un tas būs 3. numurs.

Failu deskriptoru sarakstu var skatīt jebkuram procesam, ja zināt tā PID.

Piemēram, atveram bash konsoli un apskatīsim mūsu procesa PID

[user@localhost ]$ echo $$
15771

Otrajā pultī palaist

[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

Å Ä« raksta vajadzÄ«bām varat droÅ”i ignorēt faila deskriptora numuru 255; to savām vajadzÄ«bām atvēra pats bash, nevis saistÄ«tā bibliotēka.

Tagad visi 3 deskriptora faili ir saistīti ar pseidotermināļa ierīci /dev/pts, taču mēs joprojām varam ar tiem manipulēt, piemēram, palaist tos otrajā konsolē

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

Un pirmajā konsolē mēs redzēsim

[user@localhost ]$ hello world

Redirect un Pipe

JÅ«s varat viegli ignorēt Å”os 3 deskriptora failus jebkurā procesā, ieskaitot bash, piemēram, caur cauruli, kas savieno divus procesus, sk.

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

Å o komandu var palaist pats ar strace -f un redzēt, kas notiek iekŔā, bet es jums pastāstÄ«Å”u Ä«si.

MÅ«su vecāku bash process ar PID 15771 parsē mÅ«su komandu un precÄ«zi saprot, cik komandu mēs vēlamies palaist. MÅ«su gadÄ«jumā tās ir divas: kaÄ·is un miegs. BaÅ”s zina, ka tam ir jāizveido divi pakārtotie procesi un jāapvieno tie vienā caurulē. Kopumā bash bÅ«s nepiecieÅ”ami 2 bērnu procesi un viena caurule.

Pirms pakārtoto procesu izveides Bash izpilda sistēmas izsaukumu caurule un saņem jaunus failu deskriptorus pagaidu caurules buferÄ«, taču Å”is buferis vēl nesavieno mÅ«su divus pakārtotos procesus.

Šķiet, ka vecāku procesam jau ir caurule, bet vēl nav neviena pakārtota 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

Pēc tam izmantojiet sistēmas zvanu klons bash izveido divus bērnu procesus, un mÅ«su trÄ«s procesi izskatÄ«sies Ŕādi:

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

Neaizmirstiet, ka klons klonē procesu kopā ar visiem failu deskriptoriem, tāpēc tie bÅ«s vienādi vecākprocesā un pakārtotajos procesos. Vecāku procesa uzdevums ar PID 15771 ir uzraudzÄ«t bērna procesus, tāpēc tas vienkārÅ”i gaida atbildi no bērniem.

Tāpēc tai nav nepiecieÅ”ama caurule, un tas aizver failu deskriptorus ar numuru 3 un 4.

Pirmajā pakārtotajā bash procesā ar PID 9004 sistēmas izsaukums dup2, maina mūsu STDOUT faila deskriptora numuru 1 uz faila deskriptoru, kas norāda uz cauruli, mūsu gadījumā tas ir numurs 3. Tādējādi viss, ko pirmais atvasinātais process ar PID 9004 raksta uz STDOUT, automātiski nonāks caurules buferī.

Otrajā pakārtotajā procesā ar PID 9005 bash izmanto dup2, lai mainītu faila deskriptora STDIN numuru 0. Tagad viss, ko nolasīs mūsu otrais bash ar PID 9005, tiks nolasīts no caurules.

Pēc tam 3. un 4. failu deskriptori tiek aizvērti arÄ« bērnprocesos, jo tie vairs netiek izmantoti.

Es apzināti ignorēju faila deskriptoru 255; to iekŔējiem nolÅ«kiem izmanto pats bash, un tas tiks aizvērts arÄ« bērnu procesos.

Pēc tam pirmajā pakārtotajā procesā ar PID 9004 bash sāk izmantot sistēmas zvanu exec izpildāmais fails, ko norādījām komandrindā, mūsu gadījumā tas ir /usr/bin/cat.

Otrajā pakārtotajā procesā ar PID 9005 bash palaiž otro mūsu norādīto izpildāmo failu, mūsu gadījumā /usr/bin/sleep.

Exec sistēmas izsaukums neaizver failu rokturus, ja vien tie nav atvērti ar karogu O_CLOEXEC, kad tika veikts atvērtais zvans. MÅ«su gadÄ«jumā pēc izpildāmo failu palaiÅ”anas tiks saglabāti visi paÅ”reizējie failu deskriptori.

Pārbaudiet konsolē:

[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

Kā redzat, mÅ«su caurules unikālais numurs ir vienāds abos procesos. Tādējādi mums ir saikne starp diviem dažādiem procesiem ar vienu un to paÅ”u vecāku.

Tiem, kas nav pazÄ«stami ar sistēmas izsaukumiem, ko izmanto bash, es ļoti iesaku palaist komandas caur strace un redzēt, kas notiek iekŔēji, piemēram, Ŕādi:

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

AtgriezÄ«simies pie problēmas ar maz vietas diskā un mēģinājumu saglabāt datus, nerestartējot procesu. UzrakstÄ«sim nelielu programmu, kas diskā ierakstÄ«s aptuveni 1 megabaitu sekundē. Turklāt, ja kāda iemesla dēļ mēs nevarējām ierakstÄ«t datus diskā, mēs to vienkārÅ”i ignorēsim un mēģināsim rakstÄ«t datus vēlreiz pēc sekundes. Piemērā, kurā izmantoju Python, varat izmantot jebkuru citu programmÄ“Å”anas valodu.

[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

Palaidīsim programmu un apskatīsim failu deskriptorus

[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

Kā redzat, mums ir 3 standarta failu deskriptori un vēl viens, ko mēs atvērām. Pārbaudīsim faila lielumu:

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

Dati tiek rakstÄ«ti, mēs cenÅ”amies mainÄ«t faila atļaujas:

[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

Mēs redzam, ka dati joprojām tiek rakstīti, lai gan mūsu lietotājam nav atļaujas rakstīt failā. Mēģināsim to noņemt:

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

Kur ir rakstīti dati? Un vai tie vispār ir rakstīti? Mēs pārbaudām:

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

Jā, mÅ«su faila deskriptors joprojām pastāv, un mēs varam izturēties pret Å”o faila deskriptoru kā mÅ«su veco failu, mēs varam to lasÄ«t, notÄ«rÄ«t un kopēt.

Apskatīsim faila lielumu:

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

Faila lielums ir 19923457. Mēģināsim notīrīt failu:

[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

Kā redzat, faila lielums tikai palielinās, un mÅ«su bagāžnieks nedarbojās. ApskatÄ«sim sistēmas izsaukuma dokumentāciju atvērt. Ja, atverot failu, mēs izmantojam karogu O_APPEND, tad katrā rakstÄ«Å”anas reizē operētājsistēma pārbauda faila lielumu un ieraksta datus faila paŔā galā, un to dara atomiski. Tas ļauj vairākiem pavedieniem vai procesiem rakstÄ«t vienā failā. Bet mÅ«su kodā mēs neizmantojam Å”o karogu. Mēs varam redzēt atŔķirÄ«gu faila lielumu lsof pēc stumbra tikai tad, ja atveram failu papildu rakstÄ«Å”anai, kas nozÄ«mē mÅ«su kodā

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

mums ir jāliek

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

Pārbaude ar ā€œwā€ karogu

[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

un ar "a" karogu

[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

Jau notiekoŔa procesa programmēŔana

Bieži vien programmētāji, veidojot un testējot programmas, lieto atkļūdotājus (piemēram, GDB) vai dažādu lÄ«meņu reÄ£istrÄ“Å”anu aplikācijā. Linux nodroÅ”ina iespēju faktiski rakstÄ«t un mainÄ«t jau darbojoÅ”os programmu, piemēram, mainÄ«t mainÄ«go vērtÄ«bas, iestatÄ«t pārtraukuma punktu utt., utt.

Atgriežoties pie sākotnējā jautājuma par diskā nepietiek vietas faila rakstÄ«Å”anai, mēģināsim simulēt problēmu.

Izveidosim failu savam nodalījumam, kuru pievienosim kā atseviŔķu disku:

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

Izveidosim failu sistēmu:

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

Pievienojiet failu sistēmu:

[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

Mēs izveidojam direktoriju ar mÅ«su Ä«paÅ”nieku:

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

Atvērsim failu rakstÄ«Å”anai tikai mÅ«su programmā:

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

Palaist

[user@localhost ]$ python openforwrite.py 

Mēs gaidām dažas sekundes

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

Tātad, mums ir Ŕī raksta sākumā aprakstÄ«tā problēma. BrÄ«vā vieta 0, 100% aizņemta.

Mēs atceramies, ka saskaņā ar uzdevuma nosacÄ«jumiem mēs cenÅ”amies ierakstÄ«t ļoti svarÄ«gus datus, kurus nevar pazaudēt. Un tajā paŔā laikā mums ir jālabo pakalpojums, nerestartējot procesu.

Pieņemsim, ka mums joprojām ir diska vietas, bet citā nodalījumā, piemēram, /home.

Mēģināsim ā€œpārprogrammēt lidojumāā€ mÅ«su kodu.

Apskatīsim mūsu procesa PID, kas ir paņēmis visu diska vietu:

[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

Pievienojieties procesam, izmantojot gdb

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

Apskatīsim atvērto failu deskriptorus:

(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

Mēs aplūkojam informāciju par faila deskriptora numuru 3, kas mūs interesē

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

Paturot prātā Python sistēmas izsaukumu (skatiet iepriekÅ”, kur mēs palaidām strace un atradām atvērto zvanu), apstrādājot kodu, lai atvērtu failu, mēs paÅ”i darām to paÅ”u sava procesa vārdā, taču mums ir nepiecieÅ”ams O_WRONLY|O_CREAT| O_TRUNC biti tiek aizstāti ar skaitlisku vērtÄ«bu. Lai to izdarÄ«tu, atveriet, piemēram, kodola avotus Å”eit un paskaties, kuri karogi par ko atbild

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

Mēs apvienojam visas vērtības vienā, iegūstam 00001101

Mēs veicam zvanu no gdb

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

Tātad mēs saņēmām jaunu faila deskriptoru ar numuru 4 un jaunu atvērtu failu citā nodalījumā, mēs pārbaudām:

(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

Mēs atceramies piemēru ar pipe - kā bash maina failu deskriptorus, un mēs jau esam iemācÄ«juÅ”ies dup2 sistēmas zvanu.

Mēs cenÅ”amies aizstāt vienu faila deskriptoru ar citu

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

Pārbaude:

(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

Mēs aizveram faila deskriptoru 4, jo mums tas nav nepiecieÅ”ams:

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

Un iziet no 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

Jaunā faila pārbaude:

[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

Kā redzat, dati tiek ierakstīti jaunā failā, pārbaudīsim veco:

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

Dati netiek zaudēti, aplikācija darbojas, žurnāli tiek rakstīti uz jaunu vietu.

Nedaudz sarežģīsim uzdevumu

Iedomāsimies, ka dati mums ir svarīgi, bet mums nav diska vietas nevienā no nodalījumiem un mēs nevaram pieslēgt disku.

Tas, ko mēs varam darīt, ir novirzīt savus datus kaut kur, piemēram, uz cauruli, un savukārt pāradresēt datus no caurules uz tīklu, izmantojot kādu programmu, piemēram, netcat.
Mēs varam izveidot nosauktu cauruli ar komandu mkfifo. Tas failu sistēmā izveidos pseido failu pat tad, ja tajā nav brīvas vietas.

Restartējiet lietojumprogrammu un pārbaudiet:

[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

Diskā nav vietas, taču mēs tur veiksmīgi izveidojam nosauktu cauruli:

[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

Tagad mums ir kaut kā jāiesaiņo visi dati, kas nonāk Å”ajā caurulē, citā serverÄ«, izmantojot tÄ«klu; tam ir piemērots tas pats netcat.

Mēs palaižam serverī remote-server.example.com

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

MÅ«su problemātiskajā serverÄ« mēs palaižam to atseviŔķā terminālā

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

Tagad visi dati, kas nonāk caurulē, automātiski tiks novirzīti uz stdin in netcat, kas tos nosūtīs uz tīklu portā 7777.

Viss, kas mums jādara, ir sākt rakstÄ«t savus datus Å”ajā nosauktajā caurulē.

Mums jau darbojas lietojumprogramma:

[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

No visiem karodziņiem mums ir nepiecieÅ”ams tikai O_WRONLY, jo fails jau pastāv un mums tas nav jādzÄ“Å”.

[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

Pārbauda attālo serveri remote-server.example.com

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

Dati nāk, mēs pārbaudām problēmas serveri

[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

Dati ir saglabāti, problēma ir atrisināta.

Es izmantoju Å”o iespēju, lai sveiktu savus kolēģus no Degiro.
Klausieties Radio-T aplādes.

Labi visiem.

Kā mājasdarbu iesaku padomāt par to, kas būs procesa faila deskriptoros cat and sleep, ja izpildīsiet Ŕādu komandu:

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

Avots: www.habr.com

Pievieno komentāru