Tìm và xóa các tệp lớn đang mở nhưng đã bị xóa


120

Làm thế nào để tìm một tệp lớn đã bị xóa nhưng vẫn mở trong một ứng dụng? Làm thế nào người ta có thể loại bỏ một tập tin như vậy, mặc dù một quá trình đã mở nó?

Tình huống là chúng tôi đang chạy một quy trình đang lấp đầy một tệp nhật ký với tốc độ khủng khiếp. Tôi biết lý do, và tôi có thể sửa nó. Cho đến lúc đó, tôi muốn rm hoặc làm trống tệp nhật ký mà không tắt quá trình.

Đơn giản chỉ cần rm output.logloại bỏ các tham chiếu đến tệp, nhưng nó tiếp tục chiếm dung lượng trên đĩa cho đến khi quá trình kết thúc. Tệ hơn: sau khi rming tôi không có cách nào để tìm tập tin ở đâu hoặc nó lớn như thế nào! Có cách nào để tìm tệp và có thể làm trống nó, mặc dù nó vẫn đang mở trong một quy trình khác không?

Tôi đặc biệt đề cập đến các hệ điều hành dựa trên Linux như Debian hoặc RHEL.


2
Nếu bạn biết pid thì bạn có thể sử dụng lsof -p <pid>để liệt kê các tệp đang mở và kích thước của chúng. Các tập tin bị xóa sẽ có một (deleted)bên cạnh nó. Các tập tin bị xóa sẽ được liên kết /proc/<pid>/fd/1có thể. Tôi không biết làm thế nào để làm cho một quá trình dừng ghi vào bộ mô tả tệp của nó mà không chấm dứt nó. Tôi nghĩ rằng điều đó sẽ phụ thuộc vào quá trình.
donothings thành công

Cảm ơn. Làm thế nào người ta có thể có được các PID của tất cả rmcác tệp ed vẫn đang mở?
dotancohen

@donothingsuccessantly Thẻ "đã xóa" được báo cáo bởi lsof là dành riêng cho Solaris, trên thực tế chỉ có Solaris 10 trở lên. OP không chỉ định anh ta đang sử dụng hệ điều hành nào. @dotancohen Trên Solaris, bạn có thể dẫn đầu ra của lsof để tìm kiếm xóa, ví dụ lsof | grep "(deleted)". Khi không còn quá trình giữ tệp bị xóa mở, kernel sẽ giải phóng các khối inode và đĩa. Các quy trình không có "trình xử lý" mà theo đó chúng có thể được thông báo rằng một tệp mở, về cơ bản bị khóa, đã bị xóa khỏi đĩa.
Johan

2
@Johan, các lsof | grep '(deleted)'công việc trên Linux là tốt. Trên Linux, bạn có thể được thông báo về việc xóa tệp (ngay cả các tệp đã không có bất kỳ mục nào trong bất kỳ thư mục nào ngoài / Proc / some-pid / fd nữa) với cơ chế inotify (sự kiện IN_DELETE_SELF)
Stéphane Chazelas

Tôi đã tạo somefilevà mở nó trong VIM, sau đó chỉnh sửa rmnó trong một quy trình bash khác. Sau đó tôi chạy lsof | grep somefilevà nó không ở đó, mặc dù tệp được mở trong VIM.
dotancohen

Câu trả lời:


141

Nếu bạn không thể tắt ứng dụng của mình, bạn có thể cắt bớt thay vì xóa tệp nhật ký để lấy lại dung lượng. Nếu tệp không được mở ở chế độ chắp thêm (với O_APPEND), thì tệp sẽ xuất hiện lớn như trước lần sau khi ứng dụng ghi vào tệp đó (mặc dù với phần đầu thưa thớt và trông như thể nó chứa NUL byte), nhưng khoảng trống sẽ được thu hồi (điều này không áp dụng cho các hệ thống tệp HFS + trên Apple OS / X không hỗ trợ các tệp thưa thớt).

Để cắt nó:

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

Nếu nó đã bị xóa, trên Linux, bạn vẫn có thể cắt nó bằng cách thực hiện:

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

Trong trường hợp $pidlà quá trình id của quá trình đó có các tập tin mở ra, và $fdmô tả một tập tin nó có nó được mở dưới (mà bạn có thể kiểm tra với lsof -p "$pid".

Nếu bạn không biết pid và đang tìm kiếm các tập tin bị xóa, bạn có thể làm:

lsof -nP | grep '(deleted)'

lsof -nP +L1, như được đề cập bởi @ user75021 là một tùy chọn thậm chí tốt hơn (đáng tin cậy hơn và dễ mang theo hơn) (liệt kê các tệp có ít hơn 1 liên kết).

Hoặc (trên Linux):

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

Hoặc để tìm những cái lớn với zsh:

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

Một cách khác, nếu ứng dụng được liên kết động là gắn một trình gỡ lỗi vào nó và làm cho nó được gọi close(fd)theo sau bởi một cái mới open("the-file", ....).


1
Cũng có một truncatelệnh thực hiện điều tương tự rõ ràng hơn.
Tobu

1
@dotancohen Stephane được chỉnh sửa để bao gồm thông tin về cách thực hiện việc này khi không biết pid.
Didi Kohen

1
@OlivierDulac, lsofcó lẽ sẽ là giải pháp gần nhất với giải pháp di động mà bạn có thể nhận được để liệt kê các tệp đang mở. Cách tiếp cận trình gỡ lỗi để đóng fd dưới chân ứng dụng cũng khá dễ mang theo.
Stéphane Chazelas

2
@StephaneChazelas: cảm ơn. Tôi tìm thấy một cách để liệt kê tất cả các PID có tệp mở trên mỗi phân vùng: df -k | awk 'NR>1 { print $NF }' | xargs fuser -Vud (và sau đó dễ dàng gửi tín hiệu cho những người vi phạm để buộc họ giải phóng fd)
Olivier Dulac

6
Bạn cũng có thể sử dụng lsof +L1. Từ trang lsof man: "Một đặc tả của biểu mẫu +L1sẽ chọn các tệp đang mở đã được hủy liên kết. Một đặc tả của biểu mẫu +aL1 <file_system>sẽ chọn các tệp đang mở không được liên kết trên hệ thống tệp được chỉ định." Điều đó đáng tin cậy hơn một chút so với grepping.
Synchro

31

Kiểm tra quickstart tại đây: lsofQuickstart

Tôi ngạc nhiên không ai đề cập đến tập tin khởi động lsof (kèm theo lsof). Mục "3.a" hiển thị cách tìm các tệp đang mở, không liên kết:

lsof -a +L1 *mountpoint*

Ví dụ:

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

Trên các hệ thống Red Hat để tìm bản sao cục bộ của tệp khởi động nhanh, tôi thường làm điều này:

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

... Hoặc cái này:

[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

1

Trình điều khiển hệ thống tệp thực sự giải phóng không gian được phân bổ và điều đó thường sẽ chỉ xảy ra khi tất cả các mô tả tệp tham chiếu đến tệp đó được phát hành. Vì vậy, bạn không thể thực sự lấy lại dung lượng, trừ khi bạn làm cho ứng dụng đóng tệp. Điều đó có nghĩa là chấm dứt nó hoặc chơi với nó "một chút" trong trình gỡ lỗi (ví dụ: đóng tệp và đảm bảo rằng nó không được mở / ghi lại hoặc /dev/nullthay vào đó mở ). Hoặc bạn có thể hack kernel, nhưng tôi sẽ khuyên bạn điều đó.

Cắt bớt tệp như Stephane gợi ý có thể giúp ích, nhưng kết quả thực sự cũng sẽ phụ thuộc vào hệ thống tệp của bạn (ví dụ: các khối được phân bổ trước sẽ chỉ có thể được giải phóng sau khi bạn đóng tệp trong mọi trường hợp).

Lý do đằng sau hành vi này là hạt nhân sẽ không biết phải làm gì với các yêu cầu dữ liệu (cả đọc và viết, nhưng việc đọc thực sự quan trọng hơn) nhắm mục tiêu vào một tệp như vậy.


2
Vì Linux hỗ trợ các tệp thưa thớt trên hầu hết các hệ thống tệp, hành vi được xác định rõ và trình điều khiển đĩa thực sự có thể giải phóng không gian đĩa. Tôi đã thử nghiệm nó cho ext3 và ext4, và nó hoạt động như Stephane đã viết.
jofel

1
Điều gì khiến bạn nói rằng việc cắt bớt một tập tin sẽ không đòi lại được các khối preallocated? Cắt ngắn có nghĩa là để phân bổ dữ liệu, tôi không có bất kỳ sự mơ hồ nào với điều đó.
Stéphane Chazelas

1
Hệ thống tệp có thể giữ các khối được phân bổ để tiết kiệm thời gian sau đó (đặc biệt là nếu tệp vẫn mở), đặc biệt là khi nó đủ lớn trước khi cắt. Ít nhất đó là những gì XFS dường như đang làm.
peterph

Cảm ơn bạn Peter. Tôi vui mừng khi bạn giải quyết "tại sao" trong bài viết này.
dotancohen

2
Theo như tôi có thể nói, việc cắt bớt các tệp đang mở cũng đòi lại không gian trên XFS. Đã thử nghiệm với cả tệp bình thường và tệp được phân bổ fallocatetrên Linux 4.9. Bạn có thể vui lòng làm rõ theo những gì hệ thống tập tin và điều kiện cắt ngắn một tập tin không lấy lại không gian?
Stéphane Chazelas
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.