Liệu fork () có ngay lập tức sao chép toàn bộ đống tiến trình trong Linux không?


30

Một fork()cuộc gọi hệ thống nhân bản một quá trình con từ quá trình đang chạy. Hai quá trình là giống hệt nhau ngoại trừ PID của họ.

Đương nhiên, nếu các quy trình chỉ đọc từ đống của chúng chứ không phải viết cho nó, sao chép heap sẽ là một sự lãng phí rất lớn của bộ nhớ.

Là toàn bộ quá trình đống sao chép? Có phải nó được tối ưu hóa theo cách chỉ viết kích hoạt một bản sao heap?

Câu trả lời:


19

Các trọn vẹn của fork()được thực hiện sử dụng mmap / sao chép vào ghi.

Điều này không chỉ ảnh hưởng đến heap mà còn chia sẻ các thư viện, stack, BSS.

Điều này, tình cờ, có nghĩa là ngã ba là một hoạt động cực kỳ nhẹ, cho đến khi 2 quá trình kết quả (cha mẹ và con) thực sự bắt đầu ghi vào phạm vi bộ nhớ. Tính năng này là một đóng góp chính cho tính sát thương của bom ngã ba - bạn kết thúc với quá nhiều quá trình trước khi kernel bị quá tải với sự sao chép và phân biệt trang.

Bạn sẽ khó có thể tìm thấy trong một hệ điều hành hiện đại một ví dụ về hoạt động trong đó kernel thực hiện một bản sao cứng (trình điều khiển thiết bị là ngoại lệ) - việc sử dụng chức năng VM trở nên dễ dàng và hiệu quả hơn rất nhiều.

Thậm chí execve()về cơ bản là "vui lòng mmap nhị phân / ld.so / whatnot, tiếp theo là thực thi" - và VM xử lý việc tải thực tế của quá trình vào RAM và thực thi. Các biến chưa được khởi tạo cục bộ cuối cùng được tạo ra từ 'zero-page' - trang sao chép chỉ đọc đặc biệt có chứa số 0, các biến khởi tạo cục bộ cuối cùng được ghi lại (bản sao trên ghi, một lần nữa) từ chính tệp nhị phân, v.v.


Một ngoại lệ đáng chú ý là các quy trình Java. Tìm kiếm "fork java memory" và bạn sẽ tìm thấy hàng tá vấn đề ảnh hưởng đến JVM máy chủ lớn hoặc JVM nhúng khi cố thực thi lệnh shell nhỏ và bị lỗi một cách thảm hại trong ngoại lệ "Không thể cấp phát bộ nhớ" (đây chỉ là các liên kết ngẫu nhiên, vấn đề này là hệ thống đến các môi trường Java). Câu trả lời SO này cáo buộc trình thu thập rác & trình biên dịch JIT của JVM không cho bộ nhớ tiến trình được chia sẻ.
WhiteWinterWolf

24

Nhân Linux thực hiện Copy-on-Write khi fork()được gọi. Khi tòa nhà được thực thi, các trang mà cha mẹ và con chia sẻ được đánh dấu chỉ đọc.

Nếu một ghi được thực hiện trên trang chỉ đọc, thì nó sẽ được sao chép, vì bộ nhớ không còn giống nhau giữa hai quy trình. Do đó, nếu chỉ thực hiện các thao tác đọc, các trang sẽ không được sao chép.


1
+1 Cảm ơn! 1. Bạn có thể vui lòng cung cấp các liên kết tham khảo? 2. Heap được sao chép hoàn toàn, hoặc trong các phần?
Adam Matan

4
2. - Trong các trang :) Nhân có rất ít hiểu về "heap" là gì - đối với kernel, đó chỉ là một loạt các trang riêng được chia nhỏ, mà các cấp phát libc xử lý theo ý muốn.
qdot

Đây thực sự là một quả bom ngã ba? Dường như với tôi, thay vì từ bỏ quy trình hiện tại, mã này sẽ tạo ra nhiều phiên bản của cùng một chương trình thực hiện từ đầu thay vì từ lệnh tiếp theo sau fork()cuộc gọi.
sherrellbc

@mmk FYI, tôi đã khá ngạc nhiên bởi "Lưu ý phụ thú vị" của bạn và vì vậy tôi đã thử nghiệm (trên Linux 3.2.0) để xem, và nó có vẻ không đúng. Tôi đã sử dụng /proc/self/pagemapđể xác định địa chỉ ảo để ánh xạ trang vật lý cho mục đích thử nghiệm. Như tôi dự đoán, nếu cháu lớn và chỉ cháu lớn viết trang chia sẻ, thì cha mẹ và con ban đầu tiếp tục chia sẻ nó. Chỉ có cháu là kết thúc với một bản sao riêng.
Celada

@Celada. Hừm. Tôi đã đọc nó ở đâu đó và tôi không nhớ phiên bản kernel mà nó đang đề cập (có lẽ là phiên bản cũ hơn?), Vì vậy, nó có thể không còn hợp lệ.
mmk

10

Linux thực hiện Copy-on-Write. Khi forktạo một quy trình mới, các trang được phân bổ được đánh dấu là chỉ đọc và chia sẻ giữa cha mẹ và con. Khi một trong hai người cố gắng sửa đổi một trang, một lỗi trang được tạo ra dẫn đến việc sao chép trang và điều chỉnh bảng trang một cách thích hợ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.