File descriptor sa Linux na may mga halimbawa

Minsan, sa isang panayam, tinanong ako, ano ang gagawin mo kung makakita ka ng isang serbisyo na hindi gumagana dahil ang disk ay naubusan ng espasyo?

Syempre, sumagot ako na titingnan ko kung ano ang inookupahan ng lugar na ito at, kung maaari, lilinisin ko ang lugar.
Pagkatapos ay nagtanong ang tagapanayam, paano kung walang libreng espasyo sa partisyon, ngunit wala ka ring nakikitang anumang mga file na kukuha ng lahat ng espasyo?

Dito ko sinabi na maaari mong palaging tumingin sa mga open file descriptor, halimbawa sa lsof command, at maunawaan kung aling application ang kumuha ng lahat ng magagamit na espasyo, at pagkatapos ay maaari kang kumilos ayon sa mga pangyayari, depende sa kung ang data ay kinakailangan .

Pinutol ako ng tagapanayam sa huling salita, idinagdag sa kanyang tanong: "Ipagpalagay na hindi namin kailangan ang data, ito ay isang log ng debug lamang, ngunit ang application ay hindi gumagana dahil hindi ito makakasulat ng isang debug"?

"Okay," sagot ko, "maaari naming i-off ang debug sa application config at i-restart ito."
Ang tagapanayam ay tumutol: "Hindi, hindi namin mai-restart ang application, mayroon pa kaming mahalagang data na nakaimbak sa memorya, at ang mga mahahalagang kliyente ay konektado sa serbisyo mismo, na hindi namin mapipilit na muling kumonekta."

"okay," sabi ko, "kung hindi namin ma-restart ang application at ang data ay hindi mahalaga sa amin, pagkatapos ay maaari naming i-clear ang bukas na file na ito sa pamamagitan ng file descriptor, kahit na hindi namin ito nakikita sa ls command sa file system."

Natuwa ang tagapanayam, ngunit ako ay hindi.

Pagkatapos ay naisip ko, bakit hindi mas malalim ang paghuhukay ng taong sumusubok sa aking kaalaman? Ngunit paano kung ang data ay mahalaga pagkatapos ng lahat? Paano kung hindi namin ma-restart ang isang proseso, at ang proseso ay nagsusulat sa file system sa isang partisyon na walang libreng espasyo? Paano kung hindi lamang mawala ang data na naisulat na, kundi pati na rin ang data na isinusulat o sinusubukang isulat ng prosesong ito?

Tuzik

Sa unang bahagi ng aking karera, sinubukan kong lumikha ng isang maliit na application na kailangan upang mag-imbak ng impormasyon ng gumagamit. At pagkatapos ay naisip ko, paano ko maitugma ang gumagamit sa kanyang data. Halimbawa, mayroon akong Ivanov Ivan Ivanovich, at mayroon siyang ilang impormasyon, ngunit paano ako makikipagkaibigan sa kanila? Maaari kong direktang ituro na ang aso na pinangalanang "Tuzik" ay kabilang sa mismong Ivan na ito. Ngunit paano kung binago niya ang kanyang pangalan at sa halip na si Ivan ay naging, halimbawa, si Olya? Pagkatapos ay lalabas na ang aming Olya Ivanovna Ivanova ay hindi na magkakaroon ng aso, at ang aming Tuzik ay mapapabilang pa rin sa hindi umiiral na Ivan. Ang isang database na nagbigay sa bawat user ng natatanging identifier (ID) ay nakatulong sa paglutas ng problemang ito, at ang aking Tuzik ay nakatali sa ID na ito, na, sa katunayan, ay isang serial number lamang. Kaya, ang may-ari ng alas ay may ID number 2, at sa ilang sandali ay nasa ilalim ng ID na ito si Ivan, at pagkatapos ay si Olya ay nasa ilalim ng parehong ID. Ang problema ng sangkatauhan at pag-aalaga ng hayop ay praktikal na nalutas.

Deskriptor ng file

Ang problema ng file at ang program na gumagana sa file na ito ay humigit-kumulang kapareho ng sa aming aso at tao. Ipagpalagay na binuksan ko ang isang file na tinatawag na ivan.txt at sinimulan kong isulat ang salitang tuzik dito, ngunit pinamamahalaang isulat lamang ang unang titik na "t" sa file, at ang file na ito ay pinalitan ng pangalan ng isang tao, halimbawa, sa olya.txt. Ngunit ang file ay nananatiling pareho, at gusto ko pa ring i-record ang aking alas dito. Sa tuwing magbubukas ang isang file sa pamamagitan ng isang system call buksan sa anumang programming language nakakatanggap ako ng natatanging ID na nagtuturo sa akin sa isang file, ang ID na ito ay ang file descriptor. At hindi mahalaga kung ano at sino ang susunod sa file na ito, maaari itong tanggalin, maaari itong palitan ng pangalan, maaaring baguhin ang may-ari, o ang mga karapatang magbasa at magsulat ay maaaring alisin, magkakaroon pa rin ako ng access dito, dahil sa oras ng pagbubukas ng file, mayroon akong mga karapatan na basahin at/o isulat ito at nagawa kong simulan ang pagtatrabaho dito, na nangangahulugang dapat kong ipagpatuloy ito.

Sa Linux, ang libc library ay nagbubukas ng 3 descriptor file para sa bawat tumatakbong application (proseso), na may bilang na 0,1,2. Higit pang impormasyon ay matatagpuan sa mga link man stdio ΠΈ lalaking stdout

  • Ang file descriptor 0 ay tinatawag na STDIN at nauugnay sa input ng application
  • Ang file descriptor 1 ay tinatawag na STDOUT at ginagamit ng mga application para mag-output ng data, gaya ng mga print command
  • Ang file descriptor 2 ay tinatawag na STDERR at ginagamit ng mga application upang mag-output ng mga mensahe ng error.

Kung sa iyong programa ay nagbukas ka ng anumang file para sa pagbabasa o pagsusulat, malamang na makukuha mo ang unang libreng ID at ito ay magiging numero 3.

Maaaring tingnan ang listahan ng mga deskriptor ng file para sa anumang proseso kung alam mo ang PID nito.

Halimbawa, buksan natin ang bash console at tingnan ang PID ng ating proseso

[user@localhost ]$ echo $$
15771

Sa pangalawang console, tumakbo tayo

[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

Maaari mong ligtas na balewalain ang file descriptor number 255 para sa mga layunin ng artikulong ito; ito ay binuksan para sa mga pangangailangan nito sa pamamagitan ng bash mismo, at hindi ng naka-link na library.

Ngayon lahat ng 3 descriptor file ay nauugnay sa pseudo terminal device /dev/pts, ngunit maaari pa rin nating manipulahin ang mga ito, halimbawa, patakbuhin ang mga ito sa pangalawang console

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

At sa unang console makikita natin

[user@localhost ]$ hello world

I-redirect at Pipe

Madali mong ma-override ang 3 descriptor file na ito sa anumang proseso, kasama ang bash, halimbawa sa pamamagitan ng pipe na nagkokonekta sa dalawang proseso, tingnan

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

Maaari mong patakbuhin ang utos na ito sa iyong sarili guhit -f at tingnan kung ano ang nangyayari sa loob, ngunit sasabihin ko sa iyo nang maikli.

Pino-parse ng aming parent bash na proseso sa PID 15771 ang aming command at eksaktong nauunawaan kung gaano karaming mga command ang gusto naming patakbuhin, sa aming kaso, dalawa sa mga ito: pusa at pagtulog. Alam ng Bash na kailangan nitong lumikha ng dalawang proseso ng bata, at pagsamahin ang mga ito sa isang tubo. Sa kabuuan, kakailanganin ng bash ang 2 proseso ng bata at isang pipe.

Ang Bash ay nagpapatakbo ng isang tawag sa system bago gumawa ng mga proseso ng bata tubo at tumatanggap ng mga bagong file descriptor sa pansamantalang pipe buffer, ngunit ang buffer na ito ay hindi pa nagkokonekta sa aming dalawang proseso ng bata.

Para sa proseso ng magulang, mukhang mayroon nang pipe, ngunit wala pang mga proseso ng bata:

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

Pagkatapos ay gamit ang system call clone bash ay lumilikha ng dalawang proseso ng bata, at ang aming tatlong proseso ay magiging ganito:

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

Huwag kalimutan na clone clone ang proseso kasama ang lahat ng file descriptors, kaya sila ay pareho sa proseso ng magulang at sa mga bata. Ang trabaho ng proseso ng magulang sa PID 15771 ay subaybayan ang mga proseso ng bata, kaya naghihintay lang ito ng tugon mula sa mga bata.

Samakatuwid, hindi nito kailangan ng pipe, at isinasara nito ang mga deskriptor ng file na may bilang na 3 at 4.

Sa unang proseso ng child bash sa PID 9004, ang system call dup2, binabago ang aming STDOUT file descriptor number 1 sa isang file descriptor na tumuturo sa pipe, sa aming kaso ito ay numero 3. Kaya, lahat ng isinulat ng unang child process na may PID 9004 sa STDOUT ay awtomatikong mapupunta sa pipe buffer.

Sa pangalawang proseso ng bata na may PID 9005, ang bash ay gumagamit ng dup2 para baguhin ang file descriptor STDIN number 0. Ngayon lahat ng mababasa ng aming pangalawang bash na may PID 9005 ay mababasa mula sa pipe.

Pagkatapos nito, ang mga file descriptor na may bilang na 3 at 4 ay sarado din sa mga proseso ng bata, dahil hindi na ginagamit ang mga ito.

Sinadya kong balewalain ang file descriptor 255; ito ay ginagamit para sa mga panloob na layunin ng bash mismo at isasara din sa mga proseso ng bata.

Susunod, sa unang proseso ng bata na may PID 9004, ang bash ay magsisimulang gumamit ng system call exec ang executable file na aming tinukoy sa command line, sa aming kaso ito ay /usr/bin/cat.

Sa pangalawang proseso ng bata na may PID 9005, pinapatakbo ng bash ang pangalawang executable na tinukoy namin, sa aming kaso /usr/bin/sleep.

Hindi isinasara ng exec system call ang mga file handle maliban kung binuksan ang mga ito gamit ang O_CLOEXEC flag sa oras na ginawa ang bukas na tawag. Sa aming kaso, pagkatapos ilunsad ang mga executable na file, ang lahat ng kasalukuyang file descriptor ay mase-save.

Tingnan sa console:

[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

Tulad ng nakikita mo, ang natatanging bilang ng aming pipe ay pareho sa parehong mga proseso. Kaya mayroon kaming koneksyon sa pagitan ng dalawang magkaibang proseso na may parehong magulang.

Para sa mga hindi pamilyar sa mga tawag sa system na ginagamit ng bash, lubos kong inirerekumenda na patakbuhin ang mga utos sa pamamagitan ng strace at makita kung ano ang nangyayari sa loob, halimbawa tulad nito:

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

Bumalik tayo sa aming problema sa mababang espasyo sa disk at sinusubukang i-save ang data nang hindi na-restart ang proseso. Sumulat tayo ng isang maliit na programa na magsusulat ng humigit-kumulang 1 megabyte bawat segundo sa disk. Bukod dito, kung sa ilang kadahilanan ay hindi kami nakapagsulat ng data sa disk, babalewalain lang namin ito at susubukan naming isulat muli ang data sa isang segundo. Sa halimbawang ginagamit ko ang Python, maaari kang gumamit ng anumang iba pang programming language.

[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

Patakbuhin natin ang programa at tingnan ang mga deskriptor ng file

[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

Gaya ng nakikita mo, mayroon kaming 3 karaniwang file descriptor at isa pa na binuksan namin. Suriin natin ang laki ng file:

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

Sinusulat ang data, sinusubukan naming baguhin ang mga pahintulot sa file:

[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

Nakikita namin na ang data ay isinusulat pa rin, kahit na ang aming user ay walang pahintulot na magsulat sa file. Subukan nating tanggalin ito:

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

Saan nakasulat ang datos? At nakasulat ba ang mga ito? Sinusuri namin:

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

Oo, umiiral pa rin ang aming file descriptor at maaari naming ituring ang file descriptor na ito tulad ng aming lumang file, maaari naming basahin, i-clear at kopyahin ito.

Tingnan natin ang laki ng file:

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

Ang laki ng file ay 19923457. Subukan nating i-clear ang file:

[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

Tulad ng nakikita mo, ang laki ng file ay tumataas lamang at ang aming trunk ay hindi gumana. Tingnan natin ang dokumentasyon ng system call buksan. Kung gagamitin namin ang flag na O_APPEND kapag nagbubukas ng file, sa bawat pagsusulat, sinusuri ng operating system ang laki ng file at nagsusulat ng data hanggang sa pinakadulo ng file, at ginagawa ito nang atomically. Nagbibigay-daan ito sa maraming thread o proseso na magsulat sa parehong file. Ngunit sa aming code hindi namin ginagamit ang watawat na ito. Makakakita lang kami ng ibang laki ng file sa lsof pagkatapos ng trunk kung bubuksan namin ang file para sa karagdagang pagsusulat, na nangangahulugang nasa aming code na lang

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

kailangan nating ilagay

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

Pagsusuri gamit ang bandilang "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

at may bandilang "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

Pagprograma ng tumatakbo nang proseso

Kadalasan, ang mga programmer, kapag gumagawa at sumusubok ng mga program, ay gumagamit ng mga debugger (halimbawa, GDB) o iba't ibang antas ng pag-log in sa application. Nagbibigay ang Linux ng kakayahang aktwal na magsulat at magbago ng tumatakbo nang programa, halimbawa, baguhin ang mga halaga ng mga variable, magtakda ng breakpoint, atbp., atbp.

Pagbabalik sa orihinal na tanong tungkol sa hindi sapat na espasyo sa disk para magsulat ng file, subukan nating gayahin ang problema.

Gumawa tayo ng file para sa ating partition, na ilalagay natin bilang hiwalay na 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 ~]$

Gumawa tayo ng file system:

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

I-mount ang file system:

[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

Lumilikha kami ng isang direktoryo kasama ang aming may-ari:

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

Buksan natin ang file para sa pagsusulat lamang sa aming programa:

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

Ilunsad

[user@localhost ]$ python openforwrite.py 

Naghintay kami ng ilang segundo

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

Kaya, mayroon kaming problema na inilarawan sa simula ng artikulong ito. Libreng espasyo 0, 100% occupied.

Naaalala namin na ayon sa mga kondisyon ng gawain, sinusubukan naming i-record ang napakahalagang data na hindi maaaring mawala. At sa parehong oras, kailangan naming ayusin ang serbisyo nang hindi na-restart ang proseso.

Sabihin nating mayroon pa rin tayong espasyo sa disk, ngunit sa ibang partition, halimbawa sa /home.

Subukan nating "reprogram on the fly" ang aming code.

Tingnan natin ang PID ng aming proseso, na kinain ang lahat ng espasyo sa disk:

[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

Kumonekta sa proseso sa pamamagitan ng gdb

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

Tingnan natin ang bukas na mga deskriptor ng file:

(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

Tinitingnan namin ang impormasyon tungkol sa file descriptor number 3, na interesado sa amin

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

Isinasaisip kung ano ang system call na ginagawa ng Python (tingnan sa itaas kung saan kami nagpatakbo ng strace at natagpuan ang bukas na tawag), kapag pinoproseso ang aming code upang magbukas ng file, ginagawa namin ang parehong sa ngalan ng aming proseso, ngunit kailangan namin ang O_WRONLY|O_CREAT| Ang mga O_TRUNC bit ay pinapalitan ng isang numeric na halaga. Upang gawin ito, buksan ang mga mapagkukunan ng kernel, halimbawa dito at tingnan kung aling mga bandila ang may pananagutan sa kung ano

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

Pinagsasama namin ang lahat ng mga halaga sa isa, nakakakuha kami ng 00001101

Pinapatakbo namin ang aming tawag mula sa gdb

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

Kaya nakakuha kami ng bagong file descriptor na may numero 4 at isang bagong bukas na file sa isa pang partition, sinusuri namin:

(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

Naaalala namin ang halimbawa sa pipe - kung paano binabago ng bash ang mga deskriptor ng file, at natutunan na namin ang dup2 system call.

Sinusubukan naming palitan ang isang file descriptor ng isa pa

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

Namin suriin:

(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

Isinasara namin ang file descriptor 4, dahil hindi namin ito kailangan:

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

At lumabas sa 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

Sinusuri ang bagong file:

[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

Tulad ng nakikita mo, ang data ay nakasulat sa isang bagong file, tingnan natin ang luma:

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

Walang nawala na data, gumagana ang application, isinulat ang mga log sa isang bagong lokasyon.

Medyo gawing kumplikado ang gawain

Isipin natin na ang data ay mahalaga sa amin, ngunit wala kaming puwang sa disk sa alinman sa mga partisyon at hindi namin maikonekta ang disk.

Ang magagawa namin ay i-redirect ang aming data sa isang lugar, halimbawa sa pipe, at sa turn ay i-redirect ang data mula sa pipe patungo sa network sa pamamagitan ng ilang programa, halimbawa netcat.
Maaari tayong lumikha ng pinangalanang pipe na may mkfifo command. Ito ay lilikha ng isang pseudo file sa file system kahit na walang libreng espasyo dito.

I-restart ang application at suriin:

[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

Walang puwang sa disk, ngunit matagumpay kaming lumikha ng pinangalanang pipe doon:

[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

Ngayon kailangan nating ibalot ang lahat ng data na pumapasok sa pipe na ito sa isa pang server sa pamamagitan ng network; ang parehong netcat ay angkop para dito.

Sa server remote-server.example.com kami ay tumatakbo

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

Sa aming may problemang server naglulunsad kami sa isang hiwalay na terminal

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

Ngayon ang lahat ng data na nagtatapos sa pipe ay awtomatikong mapupunta sa stdin sa netcat, na magpapadala nito sa network sa port 7777.

Ang kailangan lang nating gawin ay simulan ang pagsulat ng ating data sa pinangalanang pipe na ito.

Mayroon na kaming tumatakbong application:

[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

Sa lahat ng mga flag, O_WRONLY lang ang kailangan namin dahil umiiral na ang file at hindi na namin kailangang i-clear ito

[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

Sinusuri ang remote server remote-server.example.com

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

Darating ang data, sinusuri namin ang server ng problema

[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

Ang data ay nai-save, ang problema ay nalutas.

Sinasamantala ko ang pagkakataong ito para kumustahin ang aking mga kasamahan mula sa Degiro.
Makinig sa mga podcast ng Radio-T.

Mabuti sa lahat.

Bilang araling-bahay, iminumungkahi kong isipin mo kung ano ang magiging proseso ng mga deskriptor ng file na pusa at pagtulog kung patakbuhin mo ang sumusunod na utos:

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

Pinagmulan: www.habr.com

Magdagdag ng komento