Thay thế các tập tin nói chung
Đầu tiên, có một số chiến lược để thay thế một tệp:
Mở tệp hiện có để viết, cắt nó thành 0 độ dài và viết nội dung mới. (Một biến thể ít phổ biến hơn là mở tệp hiện có, ghi đè nội dung cũ bằng nội dung mới, cắt ngắn tệp thành độ dài mới nếu nó ngắn hơn.) Về mặt vỏ:
echo 'new content' >somefile
Xóa tệp cũ và tạo một tệp mới có cùng tên. Về mặt vỏ:
rm somefile
echo 'new content' >somefile
Viết vào một tệp mới dưới một tên tạm thời, sau đó di chuyển tệp mới sang tên hiện có. Di chuyển xóa tập tin cũ. Về mặt vỏ:
echo 'new content' >somefile.new
mv somefile.new somefile
Tôi sẽ không liệt kê tất cả sự khác biệt giữa các chiến lược, tôi sẽ chỉ đề cập đến một số điều quan trọng ở đây. Với chiến lược 1, nếu bất kỳ quy trình nào hiện đang sử dụng tệp, quy trình sẽ thấy nội dung mới khi nó được cập nhật. Điều này có thể gây ra một số nhầm lẫn nếu quá trình mong muốn nội dung tệp vẫn giữ nguyên. Lưu ý rằng đây chỉ là về các quy trình mở tệp (như hiển thị trong lsof
hoặc trong ; các ứng dụng tương tác có tài liệu mở (ví dụ: mở tệp trong trình chỉnh sửa) thường không giữ tệp mở, chúng tải nội dung tệp trong khi Tài liệu mở bằng tiếng Anh hoạt động và họ thay thế tệp (sử dụng một trong các chiến lược ở trên) trong quá trình lưu tài liệu lưu lại./proc/PID/fd/
Với chiến lược 2 và 3, nếu một số quy trình có tệp somefile
mở, tệp cũ vẫn mở trong quá trình nâng cấp nội dung. Với chiến lược 2, bước thực hiện xóa tệp trong thực tế chỉ xóa mục nhập của tệp trong thư mục. Bản thân tệp chỉ bị xóa khi không có mục nhập thư mục dẫn đến nó (trên các hệ thống tệp Unix điển hình, có thể có nhiều mục nhập thư mục cho cùng một tệp ) và không có quá trình nào mở được. Đây là một cách để quan sát điều này - tệp chỉ bị xóa khi sleep
quá trình bị hủy ( rm
chỉ xóa mục nhập thư mục của nó).
echo 'old content' >somefile
sleep 9999999 <somefile &
df .
rm somefile
df .
cat /proc/$!/fd/0
kill $!
df .
Với chiến lược 3, bước di chuyển tệp mới sang tên hiện có sẽ xóa mục nhập thư mục dẫn đến nội dung cũ và tạo mục nhập thư mục dẫn đến nội dung mới. Điều này được thực hiện trong một thao tác nguyên tử, vì vậy chiến lược này có một lợi thế lớn: nếu một quá trình mở tệp bất cứ lúc nào, nó sẽ thấy nội dung cũ hoặc nội dung mới - không có nguy cơ nhận được nội dung hỗn hợp hoặc không phải là tệp hiện có.
Thay thế thực thi
Nếu bạn thử chiến lược 1 với một chương trình thực thi đang chạy trên Linux, bạn sẽ gặp lỗi.
cp /bin/sleep .
./sleep 999999 &
echo oops >|sleep
bash: sleep: Text file busy
Một tập tin văn bản Tiếng Việt có nghĩa là một tập tin chứa mã thực thi vì lý do lịch sử tối nghĩa . Linux, giống như nhiều biến thể unix khác, từ chối ghi đè mã của chương trình đang chạy; một vài biến thể unix cho phép điều này, dẫn đến sự cố trừ khi mã mới là một sửa đổi rất tốt của mã cũ.
Trên Linux, bạn có thể ghi đè mã của thư viện được tải động. Nó có khả năng dẫn đến sự cố của chương trình sử dụng nó. (Bạn có thể không quan sát được điều này sleep
vì nó tải tất cả mã thư viện cần khi khởi động. Hãy thử một chương trình phức tạp hơn, có ích gì đó sau khi ngủ, như thế perl -e 'sleep 9; print lc $ARGV[0]'
.)
Nếu một trình thông dịch đang chạy một tập lệnh, thì tập tin tập lệnh được trình thông dịch mở theo cách thông thường, do đó không có sự bảo vệ nào đối với việc ghi đè tập lệnh. Một số phiên dịch viên đọc và phân tích toàn bộ tập lệnh trước khi họ bắt đầu thực thi dòng đầu tiên, những người khác đọc kịch bản khi cần thiết. Xem Điều gì xảy ra nếu bạn chỉnh sửa tập lệnh trong khi thực thi? và Linux xử lý các kịch bản shell như thế nào? để biết thêm chi tiết.
Chiến lược 2 và 3 cũng an toàn cho các tệp thực thi: mặc dù đang chạy các tệp thực thi (và các thư viện được tải động) không mở các tệp theo nghĩa là có một bộ mô tả tệp, chúng hoạt động theo cách rất giống nhau. Miễn là một số chương trình đang chạy mã, tệp vẫn còn trên đĩa ngay cả khi không có mục nhập thư mục.
Nâng cấp ứng dụng
Hầu hết các nhà quản lý gói sử dụng chiến lược 3 để thay thế các tệp, vì lợi thế chính được đề cập ở trên - tại bất kỳ thời điểm nào, việc mở tệp dẫn đến một phiên bản hợp lệ của nó.
Trường hợp nâng cấp ứng dụng có thể bị phá vỡ là trong khi nâng cấp một tệp là nguyên tử, thì việc nâng cấp toàn bộ ứng dụng không phải là nếu ứng dụng bao gồm nhiều tệp (chương trình, thư viện, dữ liệu, chế độ). Hãy xem xét chuỗi các sự kiện sau đây:
- Một phiên bản của ứng dụng được bắt đầu.
- Ứng dụng được nâng cấp.
- Ứng dụng cá thể đang chạy sẽ mở một trong các tệp dữ liệu của nó.
Trong bước 3, phiên bản đang chạy của phiên bản cũ của ứng dụng đang mở tệp dữ liệu từ phiên bản mới. Việc này có hoạt động hay không phụ thuộc vào ứng dụng, đó là tập tin nào và tập tin đã được sửa đổi bao nhiêu.
Sau khi nâng cấp, bạn sẽ lưu ý rằng chương trình cũ vẫn đang chạy. Nếu bạn muốn chạy phiên bản mới, bạn sẽ phải thoát khỏi chương trình cũ và chạy phiên bản mới. Trình quản lý gói thường giết và khởi động lại daemon khi nâng cấp, nhưng chỉ để lại các ứng dụng của người dùng cuối.
Một vài daemon có các quy trình đặc biệt để xử lý các nâng cấp mà không phải giết daemon và đợi phiên bản mới khởi động lại (điều này gây ra sự gián đoạn dịch vụ). Điều này là cần thiết trong trường hợp init , không thể bị giết; hệ thống init cung cấp một cách để yêu cầu cuộc gọi cá thể đang chạy execve
thay thế chính nó bằng phiên bản mới.