Temukan dan hapus file besar yang terbuka tetapi telah dihapus

120

Bagaimana cara menemukan file besar yang telah dihapus tetapi masih terbuka di aplikasi? Bagaimana seseorang dapat menghapus file seperti itu, meskipun suatu proses membuatnya terbuka?

Situasinya adalah bahwa kami menjalankan proses yang mengisi file log pada tingkat yang luar biasa. Saya tahu alasannya, dan saya bisa memperbaikinya. Sampai saat itu, saya ingin rm atau mengosongkan file log tanpa mematikan proses.

Cukup melakukan rm output.loghanya menghapus referensi ke file, tetapi terus menempati ruang pada disk hingga proses dihentikan. Lebih buruk lagi: setelah rming saya sekarang tidak punya cara untuk menemukan di mana file itu atau seberapa besar itu! Apakah ada cara untuk menemukan file, dan mungkin mengosongkannya, meskipun masih terbuka dalam proses lain?

Saya secara khusus merujuk ke sistem operasi berbasis Linux seperti Debian atau RHEL.

dotancohen
sumber
2
Jika Anda tahu pid maka Anda dapat menggunakan lsof -p <pid>untuk daftar file yang terbuka dan ukurannya. File yang dihapus akan memiliki di (deleted)sebelahnya. File yang dihapus mungkin ditautkan pada /proc/<pid>/fd/1. Saya tidak tahu bagaimana membuat proses berhenti menulis ke deskriptor file tanpa menghentikannya. Saya pikir itu akan tergantung pada prosesnya.
donat sukses
Terima kasih. Bagaimana mungkin orang mendapatkan PID dari semua rmfile ed yang masih terbuka?
dotancohen
@tidak berhasil Tag "dihapus" yang dilaporkan oleh lsof khusus untuk Solaris, sebenarnya hanya Solaris 10 atau lebih baru. OP tidak menentukan sistem operasi apa yang ia gunakan. @dotancohen Pada Solaris Anda dapat menyalurkan output dari lsof untuk mencari yang dihapus, misalnya lsof | grep "(deleted)". Ketika tidak ada lagi proses menahan file yang dihapus terbuka, kernel akan membebaskan blok inode dan disk. Proses tidak memiliki "penangan" di mana mereka dapat diberitahu bahwa file yang terbuka, pada dasarnya terkunci, telah dihapus dari disk.
Johan
2
@ Johan, lsof | grep '(deleted)'bekerja di Linux juga. Di Linux, Anda dapat diberitahu tentang penghapusan file (bahkan file yang sudah tidak memiliki entri di direktori apa pun selain / proc / some-pid / fd lagi) dengan mekanisme inotify (acara IN_DELETE_SELF)
Stéphane Chazelas
Saya membuat somefiledan membukanya di VIM, lalu rmmengeditnya di proses bash lain. Saya kemudian jalankan lsof | grep somefiledan tidak ada di sana, meskipun file terbuka di VIM.
dotancohen

Jawaban:

141

Jika Anda tidak dapat mematikan aplikasi Anda, Anda dapat memotong alih-alih menghapus file log untuk mendapatkan kembali ruang tersebut. Jika file tidak terbuka dalam mode append (with O_APPEND), maka file akan muncul sebesar sebelum waktu berikutnya aplikasi menulis ke sana (meskipun dengan bagian terkemuka jarang dan tampak seolah-olah berisi NUL byte), tetapi ruang akan telah direklamasi (yang tidak berlaku untuk sistem file HFS + di Apple OS / X yang tidak mendukung file jarang).

Untuk memotongnya:

: > /path/to/the/file.log

Jika sudah dihapus, di Linux, Anda masih dapat memotongnya dengan melakukan:

: > "/proc/$pid/fd/$fd"

Di mana $pidid proses dari proses yang memiliki file dibuka, dan $fdsatu deskriptor file itu dibuka di bawah (yang dapat Anda periksa dengan lsof -p "$pid".

Jika Anda tidak tahu pid, dan sedang mencari file yang dihapus, Anda dapat melakukan:

lsof -nP | grep '(deleted)'

lsof -nP +L1, seperti yang disebutkan oleh @ user75021 adalah opsi yang lebih baik (lebih dapat diandalkan dan lebih portabel) (daftar file yang memiliki kurang dari 1 tautan).

Atau (di Linux):

find /proc/*/fd -ls | grep  '(deleted)'

Atau untuk menemukan yang besar dengan zsh:

ls -ld /proc/*/fd/*(-.LM+1l0)

Alternatif, jika aplikasi terhubung secara dinamis adalah melampirkan debugger ke sana dan membuatnya memanggil close(fd)diikuti oleh yang baru open("the-file", ....).

Stéphane Chazelas
sumber
1
Ada juga truncateperintah yang melakukan hal yang sama secara lebih eksplisit.
Tobu
1
@dotancohen Stephane diedit untuk memasukkan info tentang cara melakukan ini ketika pid tidak diketahui.
Didi Kohen
1
@OlivierDulac, lsofmungkin akan menjadi yang paling dekat dengan solusi portabel yang bisa Anda dapatkan dari daftar file yang terbuka. pendekatan debugger untuk menutup fd di bawah kaki aplikasi juga harus cukup portabel.
Stéphane Chazelas
2
@StephaneChazelas: terima kasih. Saya menemukan cara untuk membuat daftar semua PID yang memiliki file terbuka di setiap partisi: df -k | awk 'NR>1 { print $NF }' | xargs fuser -Vud (dan kemudian mudah mengirim sinyal kepada pelanggar untuk memaksa mereka melepaskan fd)
Olivier Dulac
6
Anda juga bisa menggunakan lsof +L1. Dari halaman manual lsof: "Spesifikasi formulir +L1akan memilih file terbuka yang telah dibatalkan tautannya. Spesifikasi formulir +aL1 <file_system>akan memilih file terbuka yang tidak terkait pada sistem file yang ditentukan.". Itu seharusnya sedikit lebih dapat diandalkan daripada grepping.
Sinkronisasi
31

Lihat mulai cepat di sini: lsofMulai cepat

Saya terkejut tidak ada yang menyebutkan file quickstart lsof (termasuk dengan lsof). Bagian "3.a" menunjukkan cara menemukan file yang terbuka dan tidak ditautkan:

lsof -a +L1 *mountpoint*

Misalnya:

[root@enterprise ~]# lsof -a +L1 /tmp
COMMAND   PID   USER   FD   TYPE DEVICE    SIZE NLINK  NODE NAME
httpd    2357 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
mysqld   2588  mysql    4u   REG 253,17      52     0  1495 /tmp/ibY0cXCd (deleted)
mysqld   2588  mysql    5u   REG 253,17    1048     0  1496 /tmp/ibOrELhG (deleted)
mysqld   2588  mysql    6u   REG 253,17       0     0  1497 /tmp/ibmDFAW8 (deleted)
mysqld   2588  mysql    7u   REG 253,17       0     0 11387 /tmp/ib2CSACB (deleted)
mysqld   2588  mysql   11u   REG 253,17       0     0 11388 /tmp/ibQpoZ94 (deleted)
httpd    3457   root   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8437 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8438 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8439 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8440 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8441 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8442 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8443 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8444 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd   16990 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd   19595 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd   27495 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd   28142 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd   31478 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)

Pada sistem Red Hat untuk menemukan salinan lokal dari file mulai cepat, saya biasanya melakukan ini:

[root@enterprise ~]# locate -i quickstart |grep lsof
/usr/share/doc/lsof-4.78/00QUICKSTART

... atau ini:

[root@enterprise ~]# rpm -qd lsof
/usr/share/doc/lsof-4.78/00.README.FIRST
/usr/share/doc/lsof-4.78/00CREDITS
/usr/share/doc/lsof-4.78/00DCACHE
/usr/share/doc/lsof-4.78/00DIALECTS
/usr/share/doc/lsof-4.78/00DIST
/usr/share/doc/lsof-4.78/00FAQ
/usr/share/doc/lsof-4.78/00LSOF-L
/usr/share/doc/lsof-4.78/00MANIFEST
/usr/share/doc/lsof-4.78/00PORTING
/usr/share/doc/lsof-4.78/00QUICKSTART
/usr/share/doc/lsof-4.78/00README
/usr/share/doc/lsof-4.78/00TEST
/usr/share/doc/lsof-4.78/00XCONFIG
/usr/share/man/man8/lsof.8.gz
pengguna75021
sumber
1

Terserah driver sistem file untuk benar-benar membebaskan ruang yang dialokasikan, dan itu biasanya akan terjadi hanya setelah semua deskriptor file yang merujuk ke file tersebut dirilis. Jadi Anda tidak dapat benar-benar mendapatkan kembali ruang, kecuali jika Anda membuat aplikasi menutup file. Yang berarti mengakhiri atau bermain dengannya "sedikit" dalam debugger (mis. Menutup file dan memastikan itu tidak dibuka / ditulis lagi, atau dibuka /dev/nullsebagai gantinya). Atau Anda bisa meretas kernel, tetapi saya menyarankan untuk tidak melakukannya.

Memotong file seperti yang disarankan Stephane mungkin membantu, tetapi hasil sebenarnya juga akan bergantung pada sistem file Anda (misalnya blok pra-alokasi kemungkinan akan dibebaskan hanya setelah Anda menutup file dalam kasus apa pun).

Alasan di balik perilaku ini adalah bahwa kernel tidak akan tahu apa yang harus dilakukan dengan permintaan data (baik membaca dan menulis, tetapi membaca sebenarnya lebih penting) menargetkan file seperti itu.

peterph
sumber
2
Karena Linux mendukung file jarang pada sebagian besar sistem file, perilaku didefinisikan dengan baik dan driver disk dapat benar-benar membebaskan ruang disk. Saya telah mengujinya untuk ext3 dan ext4, dan berfungsi seperti yang ditulis Stephane.
jofel
1
Apa yang membuat Anda mengatakan bahwa memotong file tidak akan mendapatkan kembali blok yang sudah dialokasikan? Memotong dimaksudkan untuk menghapus data, saya tidak merasa ada ambiguitas dengan itu.
Stéphane Chazelas
1
Sistem file dapat menjaga blok yang dialokasikan untuk menghemat waktu kemudian (terutama jika file masih terbuka), terutama ketika itu cukup besar sebelum dipotong. Setidaknya itulah yang tampaknya dilakukan XFS.
peterph
Peter terima kasih Saya senang Anda mengatasi "mengapa" di pos ini.
dotancohen
2
Sejauh yang saya tahu, memotong file terbuka tidak merebut kembali ruang di XFS juga. Diuji dengan file normal dan file yang dialokasikan dengan fallocatedi Linux 4.9. Bisakah Anda mengklarifikasi di bawah sistem file apa dan kondisi pemotongan file tidak merebut kembali ruang?
Stéphane Chazelas