Có an toàn để di chuyển một tập tin đang được gắn vào?


28

Tôi có một quy trình node.js sử dụng fs.appendFileđể thêm các dòng vào file.log. Chỉ các dòng hoàn chỉnh khoảng 40 ký tự trên mỗi dòng được nối thêm, ví dụ như các cuộc gọi giống như fs.appendFile("start-end"), không phải 2 cuộc gọi như fs.appendFile("start-")fs.appendFile("end"). Nếu tôi di chuyển tệp này đến file2.logtôi có thể chắc chắn rằng không có dòng nào bị mất hoặc sao chép một phần không?

Câu trả lời:


36

Miễn là bạn không di chuyển tệp qua biên giới hệ thống tệp, thao tác sẽ an toàn. Điều này là do cơ chế, làm thế nào »di chuyển« thực sự được thực hiện.

Nếu bạn mvlà một tệp trên cùng một hệ thống tệp, thì tệp đó không thực sự được chạm vào mà chỉ thay đổi mục nhập hệ thống tệp.

$ mv foo bar

thực sự làm một cái gì đó như

$ ln foo bar
$ rm foo

Điều này sẽ tạo ra một liên kết cứng (mục nhập thư mục thứ hai) cho tệp (thực tế là nút được trỏ bởi mục nhập hệ thống tệp) foođược đặt tên barvà xóa foomục nhập. Kể từ bây giờ khi gỡ bỏ foo, có một mục nhập hệ thống tệp thứ hai trỏ đến foonút inode, loại bỏ mục nhập cũ fookhông thực sự loại bỏ bất kỳ khối nào thuộc về inode.

Chương trình của bạn sẽ vui vẻ nối thêm vào tập tin, vì xử lý tệp đang mở của nó trỏ đến nút của tệp chứ không phải mục nhập hệ thống tệp.

Lưu ý: Nếu chương trình của bạn đóng và mở lại tệp giữa các lần ghi, cuối cùng bạn sẽ có một tệp mới được tạo bằng mục nhập hệ thống tệp cũ!

Di chuyển hệ thống tập tin chéo:

Nếu bạn di chuyển tệp qua biên giới hệ thống tệp, mọi thứ sẽ trở nên tồi tệ. Trong trường hợp này, bạn không thể đảm bảo giữ cho tệp của mình được nhất quán, vì mvthực tế sẽ

  • tạo một tệp mới trên hệ thống tệp đích
  • sao chép nội dung của tập tin cũ sang tập tin mới
  • xóa tập tin cũ

hoặc là

$ cp /path/to/foo /path/to/bar
$ rm /path/to/foo

tôn trọng.

$ touch /path/to/bar
$ cat < /path/to/foo > /path/to/bar
$ rm /path/to/foo

Tùy thuộc vào việc sao chép có đạt đến cuối tệp trong quá trình ghi ứng dụng của bạn hay không, có thể xảy ra rằng bạn chỉ có một nửa dòng trong tệp mới.

Ngoài ra, nếu ứng dụng của bạn không đóng và mở lại tệp cũ, nó sẽ tiếp tục ghi vào tệp cũ, ngay cả khi nó dường như bị xóa: kernel biết tệp nào đang mở và mặc dù nó sẽ xóa mục nhập hệ thống tệp, nó sẽ không xóa inode của các tập tin cũ và các khối liên quan cho đến khi ứng dụng của bạn đóng xử lý tệp đang mở.


3
FYI, các phiên bản đầu của Unix không có rename()cuộc gọi hệ thống. Vì vậy, phiên bản gốc mvthực sự đã gọi link()để tạo liên kết cứng, tiếp theo là unlink()xóa tên gốc. rename()đã được thêm vào FreeBSD, để thực hiện điều này một cách nguyên tử trong kernel.
Barmar

Tôi xin lỗi nhưng là file-system bordersgì?
laike9m

1
@ laike9m - Đường viền hệ thống tệp đề cập đến thực tế là một hệ thống tệp đơn giản phải nằm trên một phân vùng trên một thiết bị bộ nhớ như ổ đĩa. Nếu bạn đổi tên một tệp trong hệ thống tệp, tất cả những thay đổi đó là tên trong mục nhập thư mục. Nó vẫn có cùng một nút - nếu nó nằm trong một hệ thống tệp dựa trên các nút để bắt đầu - giống như hầu hết các hệ thống tệp Linux. Nhưng, nếu bạn di chuyển tệp sang hệ thống tệp khác, dữ liệu thực tế phải được di chuyển và tệp sẽ nhận được một nút mới từ hệ thống tệp mới. Điều này sẽ phá vỡ mọi hoạt động trên tệp đang diễn ra khi điều này xảy ra.
Joe

9

Vì bạn nói rằng bạn đang sử dụng node.js, tôi giả sử bạn đang sử dụng fs.rename()(hoặc fs.renameSync()) để đổi tên các tệp. Phương thức node.js này được ghi lại để sử dụng lệnh gọi hệ thống (2) , không chạm vào tệp theo bất kỳ cách nào, mà chỉ thay đổi tên mà nó được liệt kê trong hệ thống tệp:

" đổi tên () đổi tên một tệp, di chuyển nó giữa các thư mục nếu cần. Bất kỳ liên kết cứng nào khác đến tệp (như được tạo bằng liên kết (2) ) đều không bị ảnh hưởng. Các mô tả tệp mở cho đường dẫn cũ cũng không bị ảnh hưởng."

Cụ thể, lưu ý câu cuối cùng được trích dẫn ở trên, trong đó nói rằng bất kỳ mô tả tệp mở nào (như chương trình của bạn sẽ được sử dụng để ghi vào tệp) sẽ tiếp tục trỏ đến nó ngay cả khi nó đã được đổi tên. Do đó, sẽ không có mất dữ liệu hoặc tham nhũng ngay cả khi tệp được đổi tên trong khi nó đồng thời được ghi vào.


Như Andreas Weise lưu ý trong câu trả lời của mình , cuộc gọi hệ thống (2) đổi tên (và do đó fs.rename()trong node.js) sẽ không hoạt động trên các ranh giới hệ thống tập tin. Do đó, việc cố gắng di chuyển một tệp sang một hệ thống tệp khác theo cách này sẽ đơn giản là thất bại.

Lệnh Unix mvcố gắng che giấu giới hạn này bằng cách phát hiện lỗi và thay vào đó, di chuyển tệp bằng cách sao chép nội dung của nó sang tệp mới và xóa bản gốc. Thật không may, di chuyển các tệp như thế này nguy cơ mất dữ liệu nếu tệp được di chuyển trong khi nó được ghi vào. Do đó, nếu bạn muốn đổi tên một cách an toàn các tệp có thể được ghi đồng thời, bạn không nên sử dụng mv(hoặc, ít nhất, bạn nên chắc chắn rằng đường dẫn mới và cũ nằm trên cùng một hệ thống tệp).

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.