Deskriptor file di Linux dengan contoh

Suatu kali, saat wawancara, saya ditanya, apa yang akan Anda lakukan jika Anda menemukan layanan tidak berfungsi karena disk kehabisan ruang?

Tentu saja saya menjawab bahwa saya akan melihat apa yang ditempati tempat ini dan, jika memungkinkan, saya akan membersihkan tempat itu.
Kemudian pewawancara bertanya, bagaimana jika tidak ada ruang kosong di partisi tersebut, tetapi Anda juga tidak melihat file apa pun yang akan menghabiskan semua ruang tersebut?

Untuk ini saya katakan bahwa Anda selalu dapat melihat deskriptor file yang terbuka, misalnya dengan perintah lsof, dan memahami aplikasi mana yang telah menghabiskan semua ruang yang tersedia, dan kemudian Anda dapat bertindak sesuai dengan keadaan, tergantung pada apakah data tersebut diperlukan. .

Pewawancara menyela saya pada kata terakhir, menambahkan pertanyaannya: “Misalkan kita tidak memerlukan data, itu hanya log debug, tetapi aplikasi tidak berfungsi karena tidak dapat menulis debug”?

“Oke,” jawab saya, “kita bisa mematikan debug di konfigurasi aplikasi dan memulai ulang.”
Pewawancara keberatan: “Tidak, kami tidak dapat memulai ulang aplikasi, kami masih memiliki data penting yang tersimpan di memori, dan klien penting terhubung ke layanan itu sendiri, yang tidak dapat kami paksa untuk menyambung kembali.”

“oke,” kata saya, “jika kita tidak dapat me-restart aplikasi dan datanya tidak penting bagi kita, maka kita cukup menghapus file yang terbuka ini melalui deskriptor file, meskipun kita tidak melihatnya di perintah ls pada sistem file.”

Pewawancara senang, tapi saya tidak.

Lalu saya berpikir, kenapa orang yang menguji ilmu saya tidak menggali lebih dalam? Namun bagaimana jika data itu penting? Bagaimana jika kita tidak dapat memulai ulang suatu proses, dan proses tersebut menulis ke sistem file pada partisi yang tidak memiliki ruang kosong? Bagaimana jika kita tidak hanya kehilangan data yang telah ditulis, tetapi juga data yang ditulis atau coba ditulis oleh proses ini?

Tuzik

Di awal karir saya, saya mencoba membuat aplikasi kecil yang diperlukan untuk menyimpan informasi pengguna. Lalu saya berpikir, bagaimana cara mencocokkan pengguna dengan datanya. Misalnya, saya punya Ivanov Ivan Ivanovich, dan dia punya beberapa informasi, tapi bagaimana saya bisa berteman dengan mereka? Saya dapat menunjukkan secara langsung bahwa anjing bernama “Tuzik” itu milik Ivan ini. Tetapi bagaimana jika dia mengubah namanya dan bukannya Ivan menjadi, misalnya, Olya? Maka ternyata Olya Ivanovna Ivanova kita tidak lagi memiliki seekor anjing, dan Tuzik kita akan tetap menjadi milik Ivan yang tidak ada. Basis data yang memberikan pengidentifikasi (ID) unik kepada setiap pengguna membantu memecahkan masalah ini, dan Tuzik saya terikat dengan ID ini, yang sebenarnya hanyalah nomor seri. Jadi, pemilik kartu as memiliki ID nomor 2, dan pada suatu saat Ivan berada di bawah ID ini, dan kemudian Olya menjadi di bawah ID yang sama. Masalah kemanusiaan dan peternakan praktis terselesaikan.

Deskriptor file

Masalah file dan program yang bekerja dengan file ini kira-kira sama dengan masalah anjing dan manusia kita. Misalkan saya membuka file bernama ivan.txt dan mulai menulis kata tuzik ke dalamnya, tetapi hanya berhasil menulis huruf pertama "t" di file tersebut, dan file ini diganti namanya oleh seseorang, misalnya menjadi olya.txt. Namun filenya tetap sama, dan saya masih ingin mencatat kartu as saya di dalamnya. Setiap kali file dibuka oleh panggilan sistem Buka dalam bahasa pemrograman apa pun saya menerima ID unik yang mengarahkan saya ke sebuah file, ID ini adalah deskriptor file. Dan tidak masalah apa dan siapa yang selanjutnya melakukan dengan file ini, file ini dapat dihapus, dapat diganti namanya, pemiliknya dapat diubah, atau hak membaca dan menulis dapat dicabut, saya masih memiliki akses untuk itu, karena pada saat membuka file tersebut, saya mempunyai hak untuk membaca dan/atau menulisnya dan saya berhasil mulai mengerjakannya, yang berarti saya harus terus melakukannya.

Di Linux, perpustakaan libc membuka 3 file deskriptor untuk setiap aplikasi (proses) yang berjalan, bernomor 0,1,2. Informasi lebih lanjut dapat ditemukan di tautan pria stdio и pria gagah

  • Deskriptor file 0 disebut STDIN dan dikaitkan dengan input aplikasi
  • Deskriptor file 1 disebut STDOUT dan digunakan oleh aplikasi untuk mengeluarkan data, seperti perintah cetak
  • Deskriptor file 2 disebut STDERR dan digunakan oleh aplikasi untuk menampilkan pesan kesalahan.

Jika dalam program Anda Anda membuka file apa pun untuk dibaca atau ditulis, kemungkinan besar Anda akan mendapatkan ID gratis pertama dan itu akan menjadi nomor 3.

Daftar deskriptor file dapat dilihat untuk proses apa pun jika Anda mengetahui PID-nya.

Misalnya, buka konsol bash dan lihat PID proses kita

[user@localhost ]$ echo $$
15771

Di konsol kedua, mari kita 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 dapat dengan aman mengabaikan deskriptor file nomor 255 untuk keperluan artikel ini; itu dibuka untuk kebutuhannya oleh bash sendiri, dan bukan oleh perpustakaan tertaut.

Sekarang ketiga file deskriptor dikaitkan dengan perangkat terminal semu /dev/pts, tapi kita masih bisa memanipulasinya, misalnya menjalankannya di konsol kedua

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

Dan di konsol pertama kita akan lihat

[user@localhost ]$ hello world

Pengalihan dan Pipa

Anda dapat dengan mudah mengganti 3 file deskriptor ini dalam proses apa pun, termasuk di bash, misalnya melalui pipa yang menghubungkan dua proses, lihat

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

Anda dapat menjalankan perintah ini sendiri dengan jejak -f dan lihat apa yang terjadi di dalam, tapi saya akan menceritakannya secara singkat.

Proses bash induk kami dengan PID 15771 mem-parsing perintah kami dan memahami dengan tepat berapa banyak perintah yang ingin kami jalankan, dalam kasus kami ada dua di antaranya: cat dan sleep. Bash tahu bahwa ia perlu membuat dua proses anak, dan menggabungkannya menjadi satu pipa. Secara total, bash memerlukan 2 proses anak dan satu pipa.

Bash menjalankan panggilan sistem sebelum membuat proses anak pipa dan menerima deskriptor file baru pada buffer pipa sementara, tetapi buffer ini belum menghubungkan kedua proses anak kita.

Untuk proses induk sepertinya sudah ada pipa, namun belum ada proses anak:

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 clone bash membuat dua proses anak, dan ketiga proses kita akan terlihat 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 bahwa clone mengkloning proses beserta semua deskriptor file, sehingga keduanya akan sama pada proses induk dan pada proses anak. Tugas proses induk dengan PID 15771 adalah memantau proses anak, sehingga tinggal menunggu respon dari anak.

Oleh karena itu, tidak memerlukan pipa, dan menutup deskriptor file bernomor 3 dan 4.

Pada proses bash anak pertama dengan PID 9004, system call dup2, mengubah deskriptor file STDOUT kami nomor 1 menjadi deskriptor file yang menunjuk ke pipa, dalam kasus kami nomor 3. Jadi, segala sesuatu yang ditulis oleh proses anak pertama dengan PID 9004 ke STDOUT akan secara otomatis berakhir di buffer pipa.

Dalam proses anak kedua dengan PID 9005, bash menggunakan dup2 untuk mengubah deskriptor file STDIN nomor 0. Sekarang semua yang dibaca oleh bash kedua kami dengan PID 9005 akan dibaca dari pipa.

Setelah ini, deskriptor file bernomor 3 dan 4 juga ditutup dalam proses anak, karena tidak lagi digunakan.

Saya sengaja mengabaikan deskriptor file 255; ini digunakan untuk keperluan internal oleh bash itu sendiri dan juga akan ditutup dalam proses anak.

Selanjutnya, pada proses anak pertama dengan PID 9004, bash mulai menggunakan panggilan sistem eksekutif file yang dapat dieksekusi yang kami tentukan pada baris perintah, dalam kasus kami adalah /usr/bin/cat.

Dalam proses anak kedua dengan PID 9005, bash menjalankan executable kedua yang kami tentukan, dalam kasus kami /usr/bin/sleep.

Panggilan sistem exec tidak menutup pegangan file kecuali dibuka dengan flag O_CLOEXEC pada saat panggilan terbuka dilakukan. Dalam kasus kami, setelah meluncurkan file yang dapat dieksekusi, semua deskriptor file saat ini akan disimpan.

Periksa di 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, nomor unik pipa kita sama di kedua proses. Jadi kita memiliki hubungan antara dua proses berbeda dengan induk yang sama.

Bagi yang belum familiar dengan system call yang digunakan bash, saya sangat menyarankan untuk menjalankan perintah melalui strace dan melihat apa yang terjadi secara internal, misalnya seperti ini:

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

Mari kita kembali ke masalah kita dengan ruang disk yang rendah dan mencoba menyimpan data tanpa memulai ulang proses. Mari kita menulis sebuah program kecil yang akan menulis sekitar 1 megabyte per detik ke disk. Selain itu, jika karena alasan tertentu kami tidak dapat menulis data ke disk, kami akan mengabaikannya dan mencoba menulis data lagi dalam hitungan detik. Pada contoh saya menggunakan Python, Anda bisa menggunakan bahasa pemrograman lainnya.

[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

Mari jalankan program dan lihat deskriptor 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

Seperti yang Anda lihat, kami memiliki 3 deskriptor file standar dan satu lagi yang kami buka. Mari kita periksa ukuran file:

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

Data sedang ditulis, kami mencoba mengubah izin pada 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

Kami melihat bahwa data masih ditulis, meskipun pengguna kami tidak memiliki izin untuk menulis ke file tersebut. Mari kita coba menghapusnya:

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

Dimana datanya ditulis? Dan apakah itu tertulis sama sekali? Kami memeriksa:

[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 file kami masih ada dan kami dapat memperlakukan deskriptor file ini seperti file lama kami, kami dapat membaca, menghapus, dan menyalinnya.

Mari kita lihat ukuran filenya:

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

Ukuran filenya adalah 19923457. Mari kita coba bersihkan filenya:

[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, ukuran file hanya bertambah dan trunk kami tidak berfungsi. Mari kita lihat dokumentasi panggilan sistem Buka. Jika kita menggunakan flag O_APPEND saat membuka file, maka pada setiap penulisan, sistem operasi memeriksa ukuran file dan menulis data hingga akhir file, dan melakukannya secara atom. Hal ini memungkinkan beberapa thread atau proses untuk menulis ke file yang sama. Namun dalam kode kami, kami tidak menggunakan tanda ini. Kita dapat melihat ukuran file yang berbeda di lsof setelah trunk hanya jika kita membuka file tersebut untuk penulisan tambahan, yang berarti dalam kode kita sebagai gantinya

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

kita harus menempatkan

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

Memeriksa dengan tanda “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

Memprogram proses yang sudah berjalan

Seringkali pemrogram, saat membuat dan menguji program, menggunakan debugger (misalnya GDB) atau berbagai tingkat pencatatan dalam aplikasi. Linux menyediakan kemampuan untuk benar-benar menulis dan mengubah program yang sudah berjalan, misalnya mengubah nilai variabel, menyetel breakpoint, dll., dll.

Kembali ke pertanyaan awal tentang tidak cukup ruang disk untuk menulis file, mari kita coba simulasikan masalahnya.

Mari buat file untuk partisi kita, yang akan kita mount sebagai disk terpisah:

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

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

Pasang sistem file:

[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

Kami membuat direktori dengan pemilik kami:

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

Mari kita buka file untuk menulis hanya di program kita:

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

Meluncurkan

[user@localhost ]$ python openforwrite.py 

Kami menunggu beberapa detik

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

Jadi, kami memiliki masalah yang dijelaskan di awal artikel ini. Ruang kosong 0, 100% terisi.

Kami ingat bahwa sesuai dengan kondisi permasalahan, kami berusaha mencatat data yang sangat penting yang tidak boleh hilang. Dan pada saat yang sama, kita perlu memperbaiki layanan tanpa memulai ulang prosesnya.

Katakanlah kita masih mempunyai ruang disk, tetapi di partisi yang berbeda, misalnya di /home.

Mari kita coba “memprogram ulang dengan cepat” kode kita.

Mari kita lihat PID dari proses kita, yang telah menghabiskan seluruh ruang 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

Hubungkan ke proses melalui gdb

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

Mari kita lihat deskriptor file yang 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 informasi tentang deskriptor file nomor 3, yang menarik minat kami

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

Mengingat panggilan sistem yang dilakukan Python (lihat di atas di mana kita menjalankan strace dan menemukan panggilan terbuka), saat memproses kode untuk membuka file, kita melakukan hal yang sama atas nama proses kita, tetapi kita memerlukan O_WRONLY|O_CREAT| O_TRUNC bit diganti dengan nilai numerik. Untuk melakukan ini, buka sumber kernel, misalnya di sini dan lihat bendera mana yang bertanggung jawab atas apa

#definisikan O_SALAH 00000001
#definisikan O_CREAT 00000100
#definisikan O_TRUNC 00001000

Kami menggabungkan semua nilai menjadi satu, kami mendapatkan 00001101

Kami menjalankan panggilan kami dari gdb

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

Jadi kami mendapatkan deskriptor file baru dengan nomor 4 dan file terbuka baru di partisi lain, kami memeriksa:

(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

Kita ingat contoh dengan pipa - bagaimana bash mengubah deskriptor file, dan kita telah mempelajari panggilan sistem dup2.

Kami mencoba mengganti satu deskriptor file dengan yang lain

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

Kami memeriksa:

(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

Kami menutup file deskriptor 4, karena 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

Memeriksa file baru:

[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 file baru, mari kita periksa 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

Tidak ada data yang hilang, aplikasi berfungsi, log ditulis ke lokasi baru.

Mari kita mempersulit tugas ini sedikit

Mari kita bayangkan bahwa data itu penting bagi kita, tetapi kita tidak memiliki ruang disk di partisi mana pun dan kita tidak dapat menghubungkan disk tersebut.

Yang bisa kita lakukan adalah mengarahkan data kita ke suatu tempat, misalnya ke pipa, dan pada gilirannya mengarahkan data dari pipa ke jaringan melalui beberapa program, misalnya netcat.
Kita dapat membuat pipa bernama dengan perintah mkfifo. Ini akan membuat file semu pada sistem file meskipun tidak ada ruang kosong di dalamnya.

Mulai ulang aplikasi dan periksa:

[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

Tidak ada ruang disk, tapi kami berhasil membuat pipa 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 menggabungkan semua data yang masuk ke pipa ini ke server lain melalui jaringan; netcat yang sama cocok untuk ini.

Di server remote-server.example.com kita jalankan

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

Di server kami yang bermasalah, kami meluncurkannya di terminal terpisah

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

Sekarang semua data yang berakhir di pipa akan otomatis masuk ke stdin di netcat, yang akan mengirimkannya ke jaringan pada port 7777.

Yang harus kita lakukan adalah mulai menulis data kita ke dalam pipa bernama ini.

Kami sudah menjalankan aplikasinya:

[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

Dari semua flag yang ada, kita hanya perlu O_WRONLY karena file sudah ada dan kita tidak perlu menghapusnya

[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

Memeriksa server jarak jauh remote-server.example.com

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

Datanya sudah masuk, kita cek server yang bermasalah

[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, masalahnya terpecahkan.

Saya menggunakan kesempatan ini untuk menyapa rekan-rekan saya dari Degiro.
Dengarkan podcast Radio T.

Baik untuk semua

Sebagai pekerjaan rumah, saya sarankan Anda memikirkan apa yang akan terjadi pada proses deskriptor file cat dan sleep jika Anda menjalankan perintah berikut:

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

Sumber: www.habr.com

Tambah komentar