Tại sao fork () phải được thiết kế để trả về một bộ mô tả tệp?


15

Trên mình trang web về các mẹo tự ống , Dan Bernstein giải thích một điều kiện chủng tộc với select()và tín hiệu, cung cấp một cách giải quyết và kết luận rằng

Tất nhiên, Điều đúng sẽ là fork()trả về một bộ mô tả tệp chứ không phải ID quy trình.

Ý anh ta là gì - đây có phải là điều gì đó về khả năng select()xử lý các thay đổi trạng thái của họ thay vì phải sử dụng trình xử lý tín hiệu để được thông báo về những thay đổi trạng thái đó không?


Có phải bài viết đó nhận được đầu vào và đầu ra lẫn lộn, hoặc tôi không đọc nó đúng?
ctrl-alt-delor

Bạn có thể yêu cầu các tín hiệu được gửi qua đường ống. Đó là những gì tôi làm.
ctrl-alt-delor

@ ctrl-alt-delor, vâng, anh ấy có vẻ sử dụng "đầu vào / đầu ra ống" hơi kỳ quặc, nhưng tôi nghĩ rõ ràng anh ấy đang viết và đọc từ ống ở đâu. Văn bản đó là từ năm 2003, và tôi không chắc chắn signalfdvà đó là một điều trước đó?
ilkkachu

5
Dan biết anh ta đang nói về cái gì, mặc dù anh ta có thể hơi cố tình khiêu khích. Nếu tôi cố tình khiêu khích, tôi sẽ phản đối rằng Tất nhiên, Điều đúng sẽ là loại bỏ SIGCHLD.
Hội nghị thượng đỉnh Steve

1
@mosvy Tôi hơi phóng đại, nhưng mọi chương trình và mọi lập trình viên tôi từng thấy ai đã thử sử dụng SIGCHLD đều gặp vấn đề với nó. Đó là một điều kiện cuộc đua đang chờ để xảy ra. Quay lại khi tất cả những gì chúng tôi đã chặn wait(), có những thứ bạn không thể làm, vì vậy ai đó đã phát minh ra SIGCHLD, nhưng đó là một công việc tồi tệ. Theo kinh nghiệm của tôi, và bây giờ mà chúng tồn tại, rắc đẹp, nonblocking wait3(), wait4()và / hoặc waitpid()các ghé cảng tại khu vực trọng điểm (có lẽ chính vòng lặp sự kiện của bạn) là một lựa chọn lớn hơn hẳn.
Hội nghị thượng đỉnh Steve

Câu trả lời:


14

Vấn đề được mô tả ở đó trong nguồn của bạn, select()nên bị gián đoạn bởi các tín hiệu như SIGCHLD, nhưng trong một số trường hợp, nó không hoạt động tốt. Vì vậy, cách giải quyết là có tín hiệu ghi vào đường ống, sau đó được theo dõi select(). Xem mô tả tập tin là những gì select()dành cho, để giải quyết vấn đề.

Cách giải quyết về cơ bản biến sự kiện tín hiệu thành sự kiện mô tả tệp. Nếu fork()chỉ trả về một fd ở vị trí đầu tiên, cách giải quyết sẽ không được yêu cầu, vì fd đó có thể được sử dụng trực tiếp với select().

Vì vậy, có, mô tả của bạn trong đoạn cuối có vẻ đúng với tôi.


Một lý do khác mà một fd (hoặc một loại xử lý kernel nào đó) sẽ tốt hơn số id của quy trình đơn giản, đó là các PID có thể được sử dụng lại sau khi quá trình chết. Đó có thể là một vấn đề trong một số trường hợp khi gửi tín hiệu đến các quy trình, có thể không thể biết chắc chắn rằng quy trình đó là quy trình mà bạn nghĩ là nó chứ không phải là một quy trình khác sử dụng lại cùng một PID. (Mặc dù tôi nghĩ rằng đây không phải là vấn đề khi gửi tín hiệu đến tiến trình con, vì cha mẹ phải chạy wait()trên con để bộ giải mã của nó được phát hành.)


Điều đó nói rằng, tôi không nhớ chính xác các trường hợp tôi đã đọc về việc tái sử dụng PID là một vấn đề, vì vậy nếu bất cứ ai muốn giải thích hoặc làm rõ về điều đó, hoặc thậm chí chỉnh sửa những điều trên, hãy thoải mái làm điều đó.
ilkkachu

2
Như bạn đã nói, không có lý do gì để cha mẹ tìm thấy con riêng của mình đã được sử dụng lại. Nó hoàn toàn kiểm soát tình huống đó bởi vì đó là người gọi wait().
Joshua

Chúng được gọi là các quy trình zombie : "một quy trình đã hoàn thành thực thi nhưng vẫn có một mục trong bảng quy trình: đó là một quy trình trong" Trạng thái kết thúc ". Điều này xảy ra đối với các quy trình con, trong đó mục nhập vẫn cần thiết để cho phép cha mẹ quá trình đọc trạng thái thoát của con nó "
Lassi

6
Điều đáng nói là bây giờ Linux có thể trả về một bộ mô tả tệp (pidfd) từ cloneđó, đó là cuộc gọi hệ thống thực tế mà fork gọi trên LInux. Cờ để kích hoạt tính năng này được gọi là CLONE_PIDFD- Xem ví dụ lwn.net/Articles/784831 .
KJ Tsanaktsidis

1
@Lie Ryan, Re " Có fork () trả về một số xác thực toàn cục thay vì một bộ xử lý cục bộ thì đúng hơn về mặt khái niệm vì ", Windows sử dụng các bộ xử lý quy trình. Mã pid và exit thoát xung quanh cho đến khi tất cả các tay cầm của tiến trình được đóng lại (thay vì chờ cha mẹ gặt hái), tránh các điều kiện chạy đua phổ biến trong các hệ thống unix. Khi các tay cầm giữ cho quá trình tồn tại, nó có ý nghĩa hơn nhiều đối với chúng là các tay cầm cục bộ hơn là các tay cầm toàn cầu.
ikegami

9

Nó chỉ là một sự suy ngẫm về dòng chữ "sẽ thật tuyệt nếu Unix được thiết kế khác với nó".

Vấn đề với các PID là chúng sống trong một không gian tên toàn cầu nơi chúng có thể được sử dụng lại cho một quy trình khác, và sẽ rất tuyệt nếu được fork()trả lại cho cha mẹ một loại xử lý nào đó được đảm bảo luôn luôn đề cập đến quy trình con và rằng nó có thể chuyển qua các quy trình khác thông qua kế thừa hoặc ổ cắm unix / SCM_RIGHTS[1].

Xem thêm các cuộc thảo luận ở đây để biết một nỗ lực gần đây để "khắc phục" điều đó trong Linux, bao gồm cả việc thêm một cờ clone()sẽ khiến nó trả về pid-fd thay vì PID.

Nhưng ngay cả khi đó, điều đó sẽ không loại bỏ sự cần thiết của bản hack tự ống [2] hoặc các giao diện tốt hơn, vì các tín hiệu thông báo cho quá trình cha mẹ về trạng thái của một đứa trẻ không phải là thứ duy nhất bạn muốn xử lý trong vòng lặp chính của chương trình. Thật không may, những thứ như epoll(7) + signalfd(2)trên Linux hoặc kqueue(2)trên BSD không phải là tiêu chuẩn - giao diện tiêu chuẩn duy nhất (nhưng không được hỗ trợ trên các hệ thống cũ) là kém hơn nhiều pselect(2).

[1] Ngăn chặn PID không được tái chế theo thời gian tòa nhà waitpid()đã trở lại và giá trị hoàn trả của nó được sử dụng có thể đạt được trên các hệ thống mới hơn bằng cách sử dụng waitid(.., WNOWAIT)thay thế.

[2] Tôi sẽ không bình luận về DJ Bernstein tuyên bố rằng anh ấy đã phát minh ra nó (xin lỗi vì sự thờ ơ ;-)).


8

Bernstein không đưa ra nhiều bối cảnh cho nhận xét "Điều đúng" này, nhưng tôi sẽ mạo hiểm đoán: có ngã ba (2) trả về một PID không phù hợp với mô tả tệp trả lại mở (2), creat (2), v.v. Phần còn lại của hệ thống Unix có thể đã thực hiện thao tác xử lý với bộ mô tả tệp đại diện cho một quy trình, thay vì PID. Một dấu hiệu cuộc gọi hệ thống (2) tồn tại, cho phép tương tác tốt hơn giữa các tín hiệu và mô tả tệp và cho thấy rằng một mô tả tệp - đại diện cho một quy trình có thể giải quyết.


Signalfd (2) trông tuyệt vời, cảm ơn vì đã đề cập đến nó! Quá tệ, nó chỉ dành cho Linux.
Lassi

1
Đã có các cuộc thảo luận về pidfd_openLinux, xem ví dụ lwn.net/Articles/789023
dhag
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.