Hãy để tôi phá vỡ nó.
Khi bạn chạy một thực thi, một chuỗi các cuộc gọi hệ thống được thực hiện, đáng chú ý nhất fork()
và execve()
:
fork()
tạo ra một quy trình con của quy trình gọi, phần lớn là bản sao chính xác của cha mẹ, cả hai vẫn chạy cùng một tệp thực thi (sử dụng các trang bộ nhớ sao chép, vì vậy nó hiệu quả). Nó trả về hai lần: Trong cha mẹ, nó trả về PID con. Trong đứa trẻ, nó trả về 0. Thông thường, tiến trình con gọi thực thi ngay lập tức:
execve()
lấy một đường dẫn đầy đủ đến tệp thực thi làm đối số và thay thế quy trình gọi bằng tệp thực thi. Tại thời điểm này, quy trình mới được tạo sẽ có không gian địa chỉ ảo của riêng nó, tức là bộ nhớ ảo và việc thực thi bắt đầu tại điểm vào của nó (ở trạng thái được quy định bởi các quy tắc của nền tảng ABI cho các quy trình mới).
Tại thời điểm này, trình tải ELF của kernel đã ánh xạ các đoạn văn bản và dữ liệu của tệp thực thi vào bộ nhớ, như thể nó đã sử dụng lệnh mmap()
gọi hệ thống (với ánh xạ chỉ đọc và ghi riêng được chia sẻ tương ứng). BSS cũng được ánh xạ như thể với MAP_ANONYMOUS. (BTW, tôi bỏ qua liên kết động ở đây để đơn giản: Trình liên kết động open()
s và mmap()
s tất cả các thư viện động trước khi chuyển đến điểm vào của tệp thực thi chính.)
Chỉ có một vài trang thực sự được tải vào bộ nhớ từ đĩa trước khi ed mới exec () bắt đầu chạy mã của chính nó. Các trang tiếp theo được yêu cầu phân trang khi cần, nếu / khi quá trình chạm vào các phần của không gian địa chỉ ảo của nó. (Tải trước bất kỳ trang mã hoặc dữ liệu nào trước khi bắt đầu thực thi mã vùng người dùng chỉ là tối ưu hóa hiệu suất.)
Các tập tin thực thi được xác định bởi các nút ở cấp độ thấp hơn. Sau khi tệp đã bắt đầu được thực thi, kernel giữ nguyên nội dung tệp bằng tham chiếu inode, không phải theo tên tệp, như đối với các mô tả tệp mở hoặc ánh xạ bộ nhớ được hỗ trợ tệp. Vì vậy, bạn có thể dễ dàng di chuyển tệp thực thi đến một vị trí khác của hệ thống tệp hoặc thậm chí trên một hệ thống tệp khác. Là một lưu ý phụ, để kiểm tra các chỉ số khác nhau của quy trình, bạn có thể xem qua thư mục /proc/PID
(PID là ID quy trình của quy trình đã cho). Bạn thậm chí có thể mở tệp thực thi /proc/PID/exe
, ngay cả khi nó không được liên kết khỏi đĩa.
Bây giờ chúng ta hãy đi xuống di chuyển:
Khi bạn di chuyển một tệp trong cùng một hệ thống tệp, lệnh gọi hệ thống được thực thi là rename()
, chỉ cần đổi tên tệp thành tên khác, inode của tệp vẫn giữ nguyên.
Trong khi giữa hai hệ thống tập tin khác nhau, có hai điều xảy ra:
Nội dung của tệp được sao chép trước vào vị trí mới, bởi read()
vàwrite()
Sau đó, tệp được hủy liên kết khỏi thư mục nguồn bằng cách sử dụng unlink()
và rõ ràng tệp sẽ nhận được một nút mới trên hệ thống tệp mới.
rm
thực ra chỉ là unlink()
-ing tệp đã cho từ cây thư mục, do đó, có quyền ghi trên thư mục sẽ giúp bạn có đủ quyền để xóa bất kỳ tệp nào khỏi thư mục đó.
Bây giờ để giải trí, hãy tưởng tượng điều gì xảy ra khi bạn di chuyển tệp giữa hai tệp và bạn không có quyền đối unlink()
với tệp từ nguồn?
Chà, tập tin sẽ được sao chép đến đích lúc đầu ( read()
, write()
) và sau đó unlink()
sẽ thất bại do không đủ quyền. Vì vậy, tập tin sẽ vẫn còn trong cả hai hệ thống tập tin !!