Mặc dù câu hỏi Stack Overflow ban đầu dường như là đủ, nhưng tôi hiểu, từ nhận xét của bạn, tại sao bạn vẫn có thể nghi ngờ về điều này. Đối với tôi, đây chính xác là loại tình huống quan trọng liên quan khi hai hệ thống con UNIX (quy trình và tệp) giao tiếp với nhau.
Như bạn có thể biết, các hệ thống UNIX thường được chia thành hai hệ thống con: hệ thống con tệp và hệ thống con quy trình. Bây giờ, trừ khi nó được hướng dẫn khác thông qua một cuộc gọi hệ thống, kernel không nên có hai hệ thống con này tương tác với nhau. Tuy nhiên, có một ngoại lệ: việc tải tệp thực thi vào các vùng văn bản của quy trình . Tất nhiên, người ta có thể tranh luận rằng hoạt động này cũng được kích hoạt bởi một cuộc gọi hệ thống ( execve
), nhưng điều này thường được biết đến là những một trong trường hợp hệ thống phụ quá trình làm cho một yêu cầu tiềm ẩn tới hệ thống tập tin.
Bởi vì hệ thống con quá trình tự nhiên không có cách xử lý tệp (nếu không sẽ không có điểm nào trong việc chia toàn bộ thành hai phần), nên nó phải sử dụng bất cứ thứ gì mà hệ thống con tệp cung cấp để truy cập tệp. Điều này cũng có nghĩa là hệ thống con quy trình được gửi tới bất kỳ biện pháp nào mà hệ thống con tập tin thực hiện liên quan đến phiên bản / xóa tập tin. Về điểm này, tôi sẽ khuyên bạn nên đọc Gilles' câu trả lời để U & L câu hỏi này . Phần còn lại của câu trả lời của tôi dựa trên câu trả lời tổng quát hơn này từ Gilles.
Điều đầu tiên cần lưu ý là trong nội bộ, các tệp chỉ có thể truy cập thông qua các nút . Nếu kernel được cung cấp một đường dẫn, bước đầu tiên của nó sẽ là dịch nó thành một inode được sử dụng cho tất cả các hoạt động khác. Khi một quá trình tải một tệp thực thi vào bộ nhớ, nó sẽ thực hiện nó thông qua inode của nó, được hệ thống con tệp cung cấp sau khi dịch một đường dẫn. Các nút có thể được liên kết với một số đường dẫn (liên kết) và các chương trình chỉ có thể xóa các liên kết. Để xóa một tập tin và inode của nó, userland phải xóa tất cả các liên kết hiện có đến inode đó và đảm bảo rằng nó hoàn toàn không được sử dụng. Khi các điều kiện này được đáp ứng, kernel sẽ tự động xóa tệp khỏi đĩa.
Nếu bạn xem phần thực thi thay thế trong câu trả lời của Gilles, bạn sẽ thấy rằng tùy thuộc vào cách bạn chỉnh sửa / xóa tệp, hạt nhân sẽ phản ứng / điều chỉnh khác nhau, luôn thông qua cơ chế được triển khai trong hệ thống con tệp.
- Nếu bạn thử chiến lược một ( mở / cắt ngắn thành 0 / ghi hoặc mở / ghi / cắt ngắn sang kích thước mới ), bạn sẽ thấy hạt nhân sẽ không xử lý yêu cầu của bạn. Bạn sẽ gặp lỗi 26: Tệp văn bản bận (
ETXTBSY
). Không có hậu quả gì.
- Nếu bạn thử chiến lược hai, bước đầu tiên là xóa tệp thực thi của bạn. Tuy nhiên, vì nó đang được sử dụng bởi một quy trình, hệ thống con tệp sẽ khởi động và ngăn không cho tệp (và inode của nó) thực sự bị xóa khỏi đĩa. Từ thời điểm này, cách duy nhất để truy cập nội dung của tệp cũ là thực hiện thông qua inode của nó, đó là điều mà hệ thống con quy trình thực hiện bất cứ khi nào nó cần tải dữ liệu mới vào các phần văn bản (bên trong, không có điểm nào trong việc sử dụng các đường dẫn, ngoại trừ khi dịch chúng thành inodes). Mặc dù bạn đã bỏ liên kếttệp (đã xóa tất cả các đường dẫn của nó), quá trình vẫn có thể sử dụng nó như thể bạn không làm gì cả. Tạo một tệp mới với đường dẫn cũ sẽ không thay đổi bất cứ điều gì: tệp mới sẽ được cung cấp một nút hoàn toàn mới, mà quá trình đang chạy không có kiến thức.
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.
- Chiến lược ba khá giống nhau vì
mv
hoạt động là một nguyên tử. Điều này có thể sẽ yêu cầu sử dụng rename
lệnh gọi hệ thống và vì các quy trình không thể bị gián đoạn trong khi ở chế độ kernel, không có gì có thể can thiệp vào thao tác này cho đến khi hoàn thành (thành công hay không). Một lần nữa, không có sự thay đổi nào đối với inode của tệp cũ: một cái mới được tạo và các quy trình đã chạy sẽ không có kiến thức về nó, ngay cả khi nó được liên kết với một trong các liên kết của inode cũ.
Với chiến lược 3, bước di chuyển tệp mới sang tên hiện có sẽ loại bỏ mục nhập thư mục dẫn đến nội dung cũ và tạo một 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ó.
Biên dịch lại một tệp : khi sử dụng gcc
(và hành vi có thể tương tự đối với nhiều trình biên dịch khác), bạn đang sử dụng chiến lược 2. Bạn có thể thấy điều đó bằng cách chạy một strace
quy trình của trình biên dịch:
stat("a.out", {st_mode=S_IFREG|0750, st_size=8511, ...}) = 0
unlink("a.out") = 0
open("a.out", O_RDWR|O_CREAT|O_TRUNC, 0666) = 3
chmod("a.out", 0750) = 0
- Trình biên dịch phát hiện ra rằng tệp đã tồn tại thông qua các cuộc gọi
stat
và lstat
hệ thống.
- Các tập tin được liên kết . Ở đây, mặc dù nó không còn có thể truy cập thông qua tên
a.out
, inode và nội dung của nó vẫn còn trên đĩa, miễn là chúng đang được sử dụng bởi các quy trình đã chạy.
- Một tập tin mới được tạo và thực hiện dưới tên
a.out
. Đây là một inode hoàn toàn mới và các nội dung hoàn toàn mới, mà các quy trình đã chạy không quan tâm.
Bây giờ, khi nói đến các thư viện chia sẻ, hành vi tương tự sẽ được áp dụng. Miễn là một đối tượng thư viện được sử dụng bởi một tiến trình, nó sẽ không bị xóa khỏi đĩa, bất kể bạn thay đổi liên kết của nó như thế nào. Bất cứ khi nào một cái gì đó phải được tải vào bộ nhớ, kernel sẽ thực hiện nó thông qua inode của tệp và do đó sẽ bỏ qua những thay đổi bạn đã thực hiện với các liên kết của nó (chẳng hạn như liên kết chúng với các tệp mới).