Deskriptor fail dalam Linux dengan contoh

Pernah, dalam temu bual, saya ditanya apa yang anda akan lakukan jika anda mendapati perkhidmatan yang rosak kerana fakta bahawa cakera telah kehabisan ruang?

Sudah tentu, saya menjawab bahawa saya akan melihat apa yang dilakukan oleh tempat ini dan, jika boleh, saya akan membersihkan tempat itu.
Kemudian penemuduga bertanya, bagaimana jika tiada ruang kosong pada partition, tetapi anda juga tidak melihat fail yang akan mengambil semua ruang?

Untuk ini, saya berkata bahawa anda sentiasa boleh melihat deskriptor fail terbuka, sebagai contoh, dengan perintah lsof dan memahami aplikasi mana yang telah mengambil semua ruang yang ada, dan kemudian anda boleh bertindak mengikut keadaan, bergantung pada sama ada data diperlukan .

Pewawancara mengganggu saya pada perkataan terakhir, sambil menambah soalannya: "Katakanlah kami tidak memerlukan data, ia hanya log nyahpepijat, tetapi aplikasi tidak berfungsi kerana ia tidak dapat menulis nyahpepijat"?

"ok," saya menjawab, "kita boleh mematikan penyahpepijatan dalam konfigurasi aplikasi dan memulakannya semula."
Penemuduga membantah: "Tidak, kami tidak boleh memulakan semula aplikasi, kami masih mempunyai data penting dalam ingatan, dan pelanggan penting disambungkan ke perkhidmatan itu sendiri, yang tidak boleh kami paksa untuk menyambung semula."

β€œokay,” saya berkata, β€œjika kami tidak boleh memulakan semula aplikasi dan kami tidak mengambil berat tentang data, maka kami boleh mengosongkan fail terbuka ini melalui deskriptor fail, walaupun kami tidak melihatnya dalam ls arahan pada sistem fail."

Penemuduga berpuas hati, tetapi saya tidak.

Kemudian saya berfikir, mengapa orang yang menguji pengetahuan saya tidak menggali lebih dalam? Tetapi bagaimana jika data itu penting? Bagaimana jika kita tidak boleh memulakan semula proses, dan pada masa yang sama proses ini menulis ke sistem fail pada partition yang tidak mempunyai ruang kosong? Bagaimana jika kita tidak boleh kehilangan bukan sahaja data yang telah ditulis, tetapi juga data yang sedang ditulis atau cuba ditulis oleh proses ini?

Tuzik

Pada awal kerjaya saya, saya cuba mencipta aplikasi kecil yang diperlukan untuk menyimpan maklumat tentang pengguna. Dan kemudian saya berfikir, bagaimana saya boleh memadankan pengguna dengan datanya. Sebagai contoh, saya mempunyai Ivanov Ivan Ivanovich, dan dia mempunyai beberapa data, tetapi bagaimana untuk berkawan dengan mereka? Saya boleh menunjukkan secara langsung bahawa anjing bernama "Tuzik" adalah milik Ivan yang sama ini. Tetapi bagaimana jika dia menukar namanya dan bukannya Ivan menjadi, sebagai contoh, Olya? Kemudian ternyata Olya Ivanovna Ivanova kami tidak akan mempunyai anjing lagi, dan Tuzik kami akan tetap menjadi milik Ivan yang tidak wujud. Pangkalan data membantu menyelesaikan masalah ini, yang memberikan setiap pengguna pengecam unik (ID), dan Tuzik saya terikat dengan ID ini, yang sebenarnya, hanyalah nombor siri. Oleh itu, pemilik tuzik mempunyai ID nombor 2, dan pada satu ketika Ivan berada di bawah ID ini, dan kemudian Olya menjadi di bawah ID yang sama. Masalah manusia dan penternakan telah diselesaikan secara praktikal.

Deskriptor fail

Masalah fail dan program yang berfungsi dengan fail ini hampir sama dengan anjing dan manusia kita. Katakan saya membuka fail bernama ivan.txt dan mula menulis perkataan tuzik ke dalamnya, tetapi berjaya menulis hanya huruf pertama "t" ke dalam fail, dan fail ini telah dinamakan semula oleh seseorang, sebagai contoh, kepada olya.txt. Tetapi fail itu sama dan saya masih mahu menulis ace saya kepadanya. Setiap kali anda membuka fail dengan panggilan sistem membuka dalam mana-mana bahasa pengaturcaraan, saya mendapat ID unik yang menunjukkan saya ke fail, ID ini ialah deskriptor fail. Dan tidak kira sama sekali apa dan siapa yang akan lakukan seterusnya dengan fail ini, ia boleh dipadamkan, ia boleh dinamakan semula, ia boleh menukar pemiliknya atau mengambil hak untuk membaca dan menulis, saya masih akan mempunyai akses kepadanya, kerana pada masa membuka fail saya mempunyai hak untuk membaca dan / atau menulisnya dan saya berjaya mula bekerja dengannya, yang bermaksud saya mesti terus melakukannya.

Di Linux, perpustakaan libc membuka 3 fail deskriptor untuk setiap aplikasi yang sedang berjalan (proses), dengan nombor 0,1,2. Maklumat lanjut anda boleh dapatkan pada pautan lelaki stdio ΠΈ lelaki stdout

  • Deskriptor fail 0 dipanggil STDIN dan dikaitkan dengan input aplikasi.
  • Deskriptor fail 1 dipanggil STDOUT dan digunakan oleh aplikasi output seperti arahan cetak.
  • Deskriptor fail 2 dinamakan STDERR dan digunakan oleh aplikasi untuk mengeluarkan mesej ralat.

Jika dalam program anda anda membuka sebarang fail untuk membaca atau menulis, kemungkinan besar anda akan mendapat ID percuma pertama dan ia akan menjadi nombor 3.

Anda boleh melihat senarai deskriptor fail untuk sebarang proses jika anda mengetahui PIDnya.

Sebagai contoh, mari buka konsol dengan bash dan lihat PID proses kami

[user@localhost ]$ echo $$
15771

Dalam konsol kedua, jalankan

[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

Anda boleh mengabaikan deskriptor fail dengan nombor 255 dengan selamat dalam rangka artikel ini, ia dibuka untuk keperluan anda oleh bash itu sendiri, dan bukan oleh perpustakaan yang dipautkan.

Kini kesemua 3 fail deskriptor dikaitkan dengan peranti pseudo-terminal /dev/pts, tetapi kita masih boleh memanipulasinya, sebagai contoh, dijalankan dalam konsol kedua

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

Dan dalam konsol pertama kita akan lihat

[user@localhost ]$ hello world

Ubah hala dan Paip

Anda boleh mengatasi 3 fail deskriptor ini dengan mudah dalam sebarang proses, termasuk dalam bash, contohnya, melalui paip (paip) yang menyambungkan dua proses, lihat

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

Anda boleh menjalankan arahan ini sendiri dengan strace -f dan lihat apa yang berlaku di dalam, tetapi saya akan pendekkan.

Proses bash induk kami dengan PID 15771 menghuraikan perintah kami dan memahami dengan tepat berapa banyak perintah yang ingin kami jalankan, dalam kes kami terdapat dua daripadanya: kucing dan tidur. Bash tahu bahawa ia perlu mencipta dua proses kanak-kanak, dan menggabungkannya ke dalam satu paip. Secara keseluruhan, bash memerlukan 2 proses kanak-kanak dan satu paip.

Sebelum membuat proses anak, bash menjalankan panggilan sistem paip dan menerima deskriptor fail baharu pada penimbal paip sementara, tetapi penimbal ini belum lagi menyambungkan dua proses anak kami dalam apa jua cara.

Untuk proses induk, nampaknya paip sudah ada, tetapi belum ada proses anak lagi:

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

Kemudian menggunakan panggilan sistem mengklon bash mencipta dua proses kanak-kanak, dan tiga proses kami akan kelihatan seperti ini:

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

Jangan lupa bahawa klon mengklon proses bersama-sama dengan semua deskriptor fail, jadi mereka akan sama dalam proses induk dan dalam proses kanak-kanak. Tugas proses induk dengan PID 15771 adalah untuk memantau proses kanak-kanak, jadi ia hanya menunggu respons daripada kanak-kanak.

Oleh itu, dia tidak memerlukan paip, dan dia menutup deskriptor fail dengan nombor 3 dan 4.

Dalam proses anak bash pertama dengan PID 9004, panggilan sistem dup2, menukar deskriptor fail STDOUT kami nombor 1 kepada deskriptor fail yang menunjuk ke paip, dalam kes kami ia adalah nombor 3. Oleh itu, semua yang proses anak pertama dengan PID 9004 tulis kepada STDOUT akan secara automatik jatuh ke dalam penimbal paip.

Dalam proses anak kedua dengan PID 9005, bash dup2s fail ke nombor deskriptor STDIN 0. Sekarang semua yang akan dibaca oleh bash kedua kami dengan PID 9005 akan dibaca dari paip.

Selepas itu, deskriptor fail dengan nombor 3 dan 4 juga ditutup dalam proses anak, kerana ia tidak lagi digunakan.

Saya sengaja mengabaikan deskriptor fail 255, ia digunakan secara dalaman oleh bash itu sendiri dan juga akan ditutup dalam proses anak.

Seterusnya, dalam proses anak pertama dengan PID 9004, bash bermula dengan panggilan sistem exec fail boleh laku yang kami tentukan pada baris arahan, dalam kes kami ia adalah /usr/bin/cat.

Dalam proses anak kedua dengan PID 9005, bash menjalankan boleh laku kedua yang kami tentukan, dalam kes kami /usr/bin/sleep.

Panggilan sistem exec tidak menutup deskriptor fail melainkan ia dibuka dengan bendera O_CLOEXEC pada masa panggilan terbuka dilaksanakan. Dalam kes kami, selepas menjalankan fail boleh laku, semua deskriptor fail semasa akan disimpan.

Menyemak dalam 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

Seperti yang anda lihat, nombor unik paip kami adalah sama dalam kedua-dua proses. Oleh itu, kami mempunyai hubungan antara dua proses berbeza dengan induk yang sama.

Bagi mereka yang tidak biasa dengan panggilan sistem yang digunakan oleh bash, saya sangat mengesyorkan menjalankan arahan melalui strace dan lihat apa yang berlaku di dalam, contohnya, seperti ini:

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

Mari kita kembali kepada masalah kita kehabisan ruang cakera dan cuba menyimpan data tanpa memulakan semula proses. Mari kita tulis program kecil yang akan menulis ke cakera kira-kira 1 megabait sesaat. Selain itu, jika atas sebab tertentu kami tidak dapat menulis data ke cakera, kami hanya akan mengabaikan ini dan cuba menulis data sekali lagi dalam satu saat. Dalam contoh yang saya gunakan Python, anda boleh menggunakan mana-mana bahasa pengaturcaraan lain.

[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

Jalankan program dan lihat deskriptor fail

[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

Seperti yang anda lihat, kami mempunyai 3 deskriptor fail standard kami dan satu lagi yang telah kami buka. Mari semak saiz fail:

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

data ditulis, kami cuba menukar hak kepada fail:

[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

Kami melihat bahawa data masih sedang ditulis, walaupun pengguna kami tidak mempunyai hak untuk menulis ke fail. Mari cuba keluarkan:

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

Di manakah data ditulis? Dan adakah mereka ditulis sama sekali? Kami menyemak:

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

Ya, deskriptor fail kami masih wujud dan kami boleh bekerja dengan deskriptor fail ini seperti fail lama kami, kami boleh membacanya, membersihkannya dan menyalinnya.

Lihat saiz fail:

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

Saiz fail ialah 19923457. Cuba mengosongkan fail:

[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

Seperti yang anda lihat, saiz fail hanya bertambah dan batang kami tidak berfungsi. Mari kita beralih kepada dokumentasi pada panggilan sistem membuka. Jika kami menggunakan bendera O_APPEND semasa membuka fail, maka dengan setiap penulisan, sistem pengendalian menyemak saiz fail dan menulis data ke hujung fail, dan melakukannya secara atom. Ini membenarkan berbilang benang atau proses untuk menulis ke fail yang sama. Tetapi dalam kod kami, kami tidak menggunakan bendera ini. Kita boleh melihat saiz fail yang berbeza dalam lsof selepas batang hanya jika kita membuka fail untuk menulis, yang bermaksud bahawa dalam kod kita, bukannya

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

kita kena letak

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

Menyemak dengan bendera "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

dan dengan bendera "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

Memprogramkan proses yang sudah berjalan

Selalunya, semasa membuat dan menguji program, pengaturcara menggunakan penyahpepijat (contohnya, GDB) atau pelbagai peringkat pengelogan dalam aplikasi. Linux menyediakan keupayaan untuk benar-benar menulis dan menukar program yang sudah berjalan, seperti menukar nilai pembolehubah, menetapkan titik putus, dan sebagainya dan sebagainya.

Kembali kepada soalan asal tentang kekurangan ruang cakera untuk menulis fail, mari cuba meniru masalah tersebut.

Mari buat fail untuk partition kami, yang akan kami lekapkan sebagai pemacu berasingan:

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

Mari buat sistem fail:

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

Mari pasang sistem fail:

[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

Buat direktori dengan pemilik kami:

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

Mari buka fail untuk menulis hanya dalam program kami:

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

Kami melancarkan

[user@localhost ]$ python openforwrite.py 

Menunggu beberapa saat

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

Jadi, kami mendapat masalah yang diterangkan pada permulaan artikel ini. Ruang kosong 0, diduduki 100%.

Kami ingat bahawa mengikut keadaan masalah, kami cuba merekodkan data yang sangat penting yang tidak boleh hilang. Dan dengan berbuat demikian, kami perlu membetulkan perkhidmatan tanpa memulakan semula proses.

Katakan kita masih mempunyai ruang cakera, tetapi dalam partition yang berbeza, sebagai contoh, dalam / rumah.

Mari cuba "program semula dengan cepat" kod kami.

Kami melihat PID proses kami, yang memakan semua ruang cakera:

[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

Menyambung ke proses dengan gdb

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

Kami melihat deskriptor fail terbuka:

(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

Kami melihat maklumat tentang deskriptor fail dengan nombor 3, yang menarik minat kami

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

Mengingat apa yang dilakukan oleh panggilan sistem Python (lihat di atas tempat kami menjalankan strace dan menemui panggilan terbuka), semasa memproses kod kami untuk membuka fail, kami melakukan perkara yang sama sendiri bagi pihak proses kami, tetapi kami memerlukan O_WRONLY|O_CREAT| Bit O_TRUNC digantikan dengan nilai angka. Untuk melakukan ini, buka sumber kernel, sebagai contoh di sini dan lihat bendera mana yang bertanggungjawab untuk apa

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

Kami menggabungkan semua nilai menjadi satu, kami mendapat 00001101

Menjalankan panggilan kami daripada gdb

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

Jadi kami mendapat deskriptor fail baharu dengan nombor 4 dan fail terbuka baharu pada partition lain, semak:

(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

Kami masih ingat contoh dengan paip - bagaimana bash menukar deskriptor fail, dan telah mempelajari panggilan sistem dup2.

Cuba menggantikan satu deskriptor fail dengan yang lain

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

Kami menyemak:

(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

Tutup deskriptor fail 4, kerana kami tidak memerlukannya:

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

Dan keluar dari 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

Menyemak fail baharu:

[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

Seperti yang anda lihat, data ditulis ke fail baru, kami menyemak yang lama:

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

Data tidak hilang, aplikasi berfungsi, log ditulis ke lokasi baru.

Mari kita membuat perkara lebih sukar

Bayangkan bahawa data itu penting kepada kami, tetapi kami tidak mempunyai ruang cakera dalam mana-mana partition dan kami tidak boleh menyambung cakera.

Apa yang boleh kami lakukan ialah mengubah hala data kami ke suatu tempat, contohnya, ke paip, dan ubah hala data daripada paip ke rangkaian melalui beberapa program, seperti netcat.
Kita boleh mencipta paip bernama dengan arahan mkfifo. Ia akan mencipta fail pseudo pada sistem fail, walaupun tiada ruang kosong padanya.

Mulakan semula aplikasi dan semak:

[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

Tiada ruang cakera, tetapi kami berjaya mencipta paip bernama di sana:

[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

Sekarang kita perlu membungkus semua data yang masuk ke dalam paip ini ke pelayan lain melalui rangkaian, netcat yang sama sesuai untuk ini.

Pada pelayan remote-server.example.com, jalankan

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

Pada pelayan masalah kami, jalankan dalam terminal yang berasingan

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

Sekarang semua data yang masuk ke dalam paip secara automatik akan pergi ke stdin dalam netcat, yang akan menghantarnya ke rangkaian pada port 7777.

Apa yang perlu kita lakukan ialah mula menulis data kita ke paip bernama ini.

Kami sudah mempunyai aplikasi yang sedang berjalan:

[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

Daripada semua bendera, kami hanya memerlukan O_WRONLY kerana fail itu sudah wujud dan kami tidak perlu mengosongkannya

[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

Menyemak pelayan jauh remote-server.example.com

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

Data akan datang, kami menyemak pelayan masalah

[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

Data disimpan, masalah diselesaikan.

Saya mengambil kesempatan ini untuk bertanya khabar kepada rakan sekerja dari Degiro.
Dengar podcast Radio-T.

Selamat untuk semua.

Sebagai kerja rumah, saya bercadang untuk memikirkan apa yang akan ada dalam deskriptor fail proses kucing dan tidur jika anda menjalankan arahan berikut:

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

Sumber: www.habr.com

Tambah komen