Tại sao vfork () dự định sẽ được sử dụng khi tiến trình con gọi exec () hoặc exit () ngay sau khi tạo?


11

Khái niệm hệ điều hành và APUE cho biết

Với vfork (), quy trình cha bị đình chỉ và quy trình con sử dụng không gian địa chỉ của cha mẹ. Vì vfork () không sử dụng copy-on-write, nếu tiến trình con thay đổi bất kỳ trang nào trong không gian địa chỉ của cha mẹ, các trang bị thay đổi sẽ hiển thị cho cha mẹ sau khi nó tiếp tục. Do đó, vfork () phải được sử dụng một cách thận trọng để đảm bảo rằng tiến trình con không sửa đổi không gian địa chỉ của cha mẹ.

vfork () được dự định sẽ được sử dụng khi tiến trình con gọi exec () hoặc exit () ngay sau khi tạo.

Làm thế nào để tôi hiểu câu cuối cùng?

Khi một tiến trình con được tạo bởi vfork()các cuộc gọi exec(), không exec()sửa đổi không gian địa chỉ của tiến trình cha, bằng cách tải chương trình mới?

Khi một tiến trình con được tạo bởi vfork()các cuộc gọi exit(), exit()không sửa đổi không gian địa chỉ của tiến trình cha khi kết thúc con?

Tôi có ưu tiên với Linux.

Cảm ơn.

Câu trả lời:


15

Khi một tiến trình con được tạo bởi vfork()các cuộc gọi exec(), không exec()sửa đổi không gian địa chỉ của tiến trình cha, bằng cách tải chương trình mới?

Không, exec()cung cấp một không gian địa chỉ mới cho chương trình mới; nó không sửa đổi không gian địa chỉ cha. Xem ví dụ các cuộc thảo luận của các execchức năng trong POSIX , và Linux execve()manpage .

Khi một tiến trình con được tạo bởi vfork () gọi exit (), exit () không sửa đổi không gian địa chỉ của tiến trình cha khi kết thúc con?

Đồng bằng exit()có thể - nó chạy các hook thoát được cài đặt bởi chương trình đang chạy (bao gồm cả các thư viện của nó). vfork()là hạn chế hơn; do đó, trên Linux, nó bắt buộc sử dụng _exit()không gọi các chức năng dọn dẹp của thư viện C.

vfork()hóa ra là khá khó khăn để có được đúng; nó đã bị xóa trong các phiên bản hiện tại của tiêu chuẩn POSIX và posix_spawn()nên được sử dụng thay thế.

Tuy nhiên, trừ khi bạn thực sự biết những gì bạn đang làm, bạn không nên sử dụng vfork()hoặc posix_spawn(); dính vào tốt cũ fork()exec().

Trang web Linux được liên kết ở trên cung cấp nhiều ngữ cảnh hơn:

Tuy nhiên, trong những ngày xưa tồi tệ, fork(2) sẽ yêu cầu tạo một bản sao hoàn chỉnh của không gian dữ liệu của người gọi, thường là không cần thiết, vì thường ngay lập tức sau đó exec(3)sẽ được thực hiện. Do đó, để có hiệu quả cao hơn, BSD đã giới thiệu vfork() cuộc gọi hệ thống, không sao chép hoàn toàn không gian địa chỉ của tiến trình cha mẹ, nhưng đã mượn bộ nhớ và luồng điều khiển của cha mẹ cho đến khi execve(2)xảy ra cuộc gọi đến hoặc thoát. Quá trình cha mẹ đã bị đình chỉ trong khi đứa trẻ đang sử dụng tài nguyên của nó. Việc sử dụng vfork()rất khó khăn: ví dụ, không sửa đổi dữ liệu trong quy trình cha phụ thuộc vào việc biết biến nào được giữ trong một thanh ghi.


Cảm ơn. "exec () cung cấp một không gian địa chỉ mới cho chương trình mới;" Là hành vi bình thường của exec () để tải một chương trình vào không gian địa chỉ của tiến trình? Tôi đã không tìm thấy trong hai liên kết nơi nó tạo ra một không gian địa chỉ mới thông thường hoặc đặc biệt cho vfork ().
Tim

1
Điều thú vị là vfork () đang chiến thắng trước mọi thứ khác. Nó nhanh hơn nhiều so với fork () khi bạn có một gigabyte bộ nhớ có thể ghi.
Joshua

2
Xin đừng nói với mọi người sử dụng posix_spawn. Viết mã chính xác sẽ khó hơn đáng kể posix_spawnso với sử dụng đơn giản forkvà nếu bạn cố gắng, bạn có thể chạy vào bức tường gạch không có hành động hoặc thuộc tính tệp nào thực hiện việc bạn cần thực hiện ở giữa forkexec. Và nó không được đảm bảo có hiệu quả như vfork, vì vậy nó thậm chí không giải quyết được vấn đề mà mọi người muốn nó giải quyết.
zwol

1
@zwol: Đó là lời khuyên thực sự tồi tệ. Mặc dù posix_spawncó thể thiếu chức năng bạn muốn (bạn có thể giải quyết vấn đề này thông qua chương trình trợ giúp trung gian, được viết bằng C hoặc tập lệnh shell nội tuyến trên cmdline), bất kỳ nỗ lực nào để đạt được những gì bạn muốn với việc vforkgọi ra hành vi nguy hiểm không xác định. Đặc tả cho vforkphép không cho phép gọi các hàm ngẫu nhiên để thiết lập trạng thái cho trẻ kế thừa trước đó execvevà cố gắng làm như vậy có thể làm hỏng trạng thái của cha mẹ.
R .. GitHub DỪNG GIÚP ICE

1
@Joshua: Một triển khai hiện đại thực posix_spawnhiện gần giống như vforktrong hầu hết các điều kiện. Những trường hợp có sự khác biệt có xu hướng chính xác là những trường hợp vforkrất không an toàn: nơi có các trình xử lý tín hiệu được cài đặt posix_spawnphải ngăn chặn việc chạy trong đứa trẻ trước khi thực hiện.
R .. GitHub DỪNG GIÚP ICE

4

Khi bạn gọi vfork(), một quy trình mới được tạo và quy trình mới đó mượn hình ảnh quy trình của quy trình cha ngoại trừ ngăn xếp. Quá trình con được cung cấp một ngôi sao ngăn xếp mới của riêng mình, tuy nhiên không cho phép returntừ hàm được gọi vfork().

Trong khi đứa trẻ đang chạy, quá trình cha mẹ bị chặn, vì đứa trẻ đã mượn không gian địa chỉ của cha mẹ.

Bất kể bạn làm gì, mọi thứ chỉ cần truy cập vào ngăn xếp đều chỉ sửa đổi ngăn xếp riêng tư của trẻ. Tuy nhiên, nếu bạn sửa đổi dữ liệu toàn cầu, điều này sẽ sửa đổi dữ liệu chung và do đó cũng ảnh hưởng đến cha mẹ.

Những thứ sửa đổi dữ liệu toàn cầu là ví dụ:

  • gọi malloc () hoặc miễn phí ()

  • sử dụng stdio

  • sửa đổi cài đặt tín hiệu

  • sửa đổi các biến không cục bộ với hàm được gọi vfork().

  • ...

Khi bạn gọi _exit()(quan trọng, không bao giờ gọi exit()), đứa trẻ bị chấm dứt và quyền kiểm soát được trao lại cho cha mẹ.

Nếu bạn gọi bất kỳ chức năng nào từ exec*()gia đình, một không gian địa chỉ mới sẽ được tạo bằng mã chương trình mới, dữ liệu mới và một phần của ngăn xếp từ cha mẹ (xem bên dưới). Một khi điều này đã sẵn sàng, đứa trẻ không còn mượn không gian địa chỉ từ đứa trẻ, mà sử dụng một không gian địa chỉ riêng.

Kiểm soát được trao lại cho cha mẹ, vì không gian địa chỉ của nó không còn được sử dụng bởi quy trình khác.

Quan trọng: Trên Linux, không có vfork()triển khai thực sự . Linux thực hiện vfork()dựa trên fork()khái niệm Copy on Write do SunOS-4.0 giới thiệu vào năm 1988. Để khiến người dùng tin rằng họ sử dụng vfork(), Linux chỉ thiết lập dữ liệu chia sẻ và đình chỉ phụ huynh trong khi đứa trẻ không gọi _exit()hoặc một trong các exec*()chức năng.

Do đó, Linux không được hưởng lợi từ thực tế là vfork()không thực sự cần thiết lập một mô tả không gian địa chỉ cho đứa trẻ trong kernel. Điều này dẫn đến kết quả vfork()là không nhanh hơn fork(). Trên các hệ thống triển khai thực tế vfork(), nó thường nhanh hơn gấp 3 lần fork()và ảnh hưởng đến hiệu suất của các shell sử dụng vfork()- ksh93, gần đây Bourne Shellcsh.

Lý do tại sao bạn không bao giờ nên gọi exit()từ vfork()đứa trẻ ed là để exit()xóa stdio trong trường hợp có dữ liệu không được lưu lại từ thời điểm trước khi gọi vfork(). Điều này có thể gây ra kết quả kỳ lạ.

BTW: posix_spawn()được triển khai trên đầu trang vfork(), do đó vfork()sẽ không bị xóa khỏi HĐH. Nó đã được đề cập rằng Linux không sử dụng vfork()cho posix_spawn().

Đối với ngăn xếp, có rất ít tài liệu, đây là những gì trang người Solaris nói:

 The vfork() and vforkx() functions can normally be used  the
 same  way  as  fork() and forkx(), respectively. The calling
 procedure, however, should not return while running  in  the
 child's  context,  since the eventual return from vfork() or
 vforkx() in the parent would be to a  stack  frame  that  no
 longer  exists. 

Vì vậy, việc thực hiện có thể làm bất cứ điều gì nó thích. Việc triển khai Solaris sử dụng bộ nhớ dùng chung cho khung ngăn xếp của chức năng gọi vfork(). Không có triển khai nào cấp quyền truy cập vào các phần cũ của ngăn xếp từ cha mẹ.


4
Cả thư viện GNU C lẫn thư viện C musl đều không cài đặt posix_spawn()trên Linux vfork(). Cả hai đều thực hiện nó trên đầu trang __clone().
JdeBP

1
@JdeBP: Bạn biết vfork()chỉ cần gọi clone()đúng không? Đó thực sự là một lớp lót trong nhân.
Joshua

1
"Quan trọng: Trên Linux, không có triển khai vfork () thực sự." <- Điều này không đúng và không đúng trong ít nhất một thập kỷ. Nếu điểm chuẩn shell của bạn không quan sát thấy bất kỳ sự khác biệt hiệu năng nào giữa vforkforktrên Linux, thì nó đang làm sai điều gì đó.
zwol

1
Nửa sau của câu trả lời này bắt đầu bằng "Quan trọng: Trên Linux, không có triển khai vfork () thực sự" hầu hết hoặc hoàn toàn sai.
R .. GitHub DỪNG GIÚP ICE

1
Xin vui lòng không đưa ra yêu cầu mà không cần xác minh. Bourne Shell hiện tại có thể được biên dịch có và không có hỗ trợ vfork, vì vậy ngay cả khi bạn tin rằng các tính năng gỡ lỗi từ Linux không thể cho kết quả đáng tin cậy, bạn có thể so sánh thời gian thực hiện cho một cuộc gọi cấu hình với và vfork trong trình bao. Tôi sử dụng một kịch bản cấu hình với 800 bài kiểm tra. Trên Solaris, Bourne Shell sử dụng vfork cần tổng thời gian cpu hệ thống ít hơn 30% so với vỏ fork. Trên Linux, thử nghiệm tương tự cho kết quả thời gian cpu hệ thống ít hơn 10%. Trên Solaris, nó không phải là 3x vì có nhiều lệnh gọi trình biên dịch đi kèm.
schily
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.