Chủ đề và tín hiệu POSIX


81

Tôi đang cố gắng hiểu sự phức tạp của cách các luồng POSIX và tín hiệu POSIX tương tác. Đặc biệt, tôi quan tâm đến:

  • Cách tốt nhất để kiểm soát luồng tín hiệu nào được phân phối (giả sử nó không gây tử vong ngay từ đầu)?
  • Cách tốt nhất để nói với một luồng khác (thực sự có thể đang bận) rằng tín hiệu đã đến là gì? (Tôi đã biết rằng việc sử dụng các biến điều kiện pthread từ trình xử lý tín hiệu là một ý tưởng tồi.)
  • Làm thế nào tôi có thể xử lý một cách an toàn việc chuyển thông tin mà một tín hiệu đã xảy ra đến các chuỗi khác? Điều này có cần phải xảy ra trong trình xử lý tín hiệu không? (Nói chung, tôi không muốn giết các chủ đề khác; tôi cần một cách tiếp cận tinh tế hơn nhiều.)

Để tham khảo về lý do tại sao tôi muốn điều này, tôi đang nghiên cứu cách chuyển đổi gói TclX để hỗ trợ các luồng hoặc tách nó ra và ít nhất là tạo một số phần hữu ích hỗ trợ các luồng. Tín hiệu là một trong những phần được quan tâm đặc biệt.

Câu trả lời:


48
  • Cách tốt nhất để kiểm soát luồng tín hiệu được chuyển đến là gì?

Như @ zoli2k đã chỉ ra, chỉ định rõ ràng một luồng duy nhất để xử lý tất cả các tín hiệu bạn muốn xử lý (hoặc một tập hợp mỗi luồng có trách nhiệm tín hiệu cụ thể), là một kỹ thuật tốt.

  • Cách tốt nhất để nói với một chuỗi khác (thực sự có thể đang bận) rằng tín hiệu đã đến là gì? [...]
  • Làm thế nào tôi có thể xử lý một cách an toàn việc chuyển thông tin mà một tín hiệu đã xảy ra đến các chuỗi khác? Điều này có cần phải xảy ra trong trình xử lý tín hiệu không?

Tôi sẽ không nói "tốt nhất", nhưng đây là đề xuất của tôi:

Chặn tất cả các tín hiệu mong muốn trong main, để tất cả các luồng kế thừa mặt nạ tín hiệu đó. Sau đó, tạo chuỗi nhận tín hiệu đặc biệt như một vòng lặp sự kiện theo hướng tín hiệu, gửi các tín hiệu mới đến như một số giao tiếp nội bộ khác .

Cách đơn giản nhất để làm điều này là để luồng chấp nhận các tín hiệu trong một vòng lặp bằng cách sử dụng sigwaitinfohoặcsigtimedwait . Sau đó, luồng chuyển đổi các tín hiệu bằng cách nào đó, có lẽ là phát a pthread_cond_t, đánh thức các luồng khác với nhiều I / O hơn, xếp hàng một lệnh trong hàng đợi an toàn luồng dành riêng cho ứng dụng, bất cứ điều gì.

Ngoài ra, luồng đặc biệt có thể cho phép các tín hiệu được gửi đến bộ xử lý tín hiệu, chỉ xuất hiện để gửi khi sẵn sàng xử lý tín hiệu. ( sigwaitTuy nhiên, việc phân phối tín hiệu qua bộ xử lý có xu hướng dễ xảy ra lỗi hơn so với việc nhận tín hiệu qua bộ xử lý.) Trong trường hợp này, bộ xử lý tín hiệu của bộ nhận thực hiện một số hành động đơn giản và không an toàn tín hiệu: thiết lậpsig_atomic_t cờ, gọi sigaddset(&signals_i_have_seen_recently, latest_sig), write() một byte đến một đường ống tự không chặn , v.v. Sau đó, quay trở lại vòng lặp chính được che mặt của nó, luồng giao tiếp nhận tín hiệu đến các luồng khác như trên.

( CẬP NHẬT @caf chỉ ra một cách đúng đắn rằng các sigwaitphương pháp tiếp cận là ưu việt hơn.)


1
Đó là một câu trả lời hữu ích hơn nhiều, đặc biệt là vì nó cũng có thể được sử dụng để xử lý việc xử lý tín hiệu không gây tử vong. Cảm ơn!
Donal Fellows

1
Sẽ dễ dàng nhất nếu luồng xử lý tín hiệu hoàn toàn không cài đặt các trình xử lý tín hiệu - thay vào đó, nó lặp lại trên sigwaitinfo()(hoặc sigtimedwait()), rồi gửi chúng đến phần còn lại của ứng dụng như được mô tả trong đoạn trước.
caf

@caf, thực sự là như vậy. Đã cập nhật
ăn cắp vặt vào

14

Theo tiêu chuẩn POSIX, tất cả các luồng phải xuất hiện với cùng một PID trên hệ thống và bằng cách sử dụng, pthread_sigmask()bạn có thể xác định mặt nạ chặn tín hiệu cho mọi luồng.

Vì chỉ được phép xác định một trình xử lý tín hiệu cho mỗi PID, tôi thích xử lý tất cả các tín hiệu trong một luồng và gửi pthread_cancel()nếu một luồng đang chạy cần được hủy bỏ. Đây là cách được ưu tiên chống lại pthread_kill()vì nó cho phép xác định các chức năng dọn dẹp cho các luồng.

Trên một số hệ thống cũ hơn, do không được hỗ trợ nhân thích hợp, các luồng đang chạy có thể có PID khác với PID của luồng cha. Xem Câu hỏi thường gặp về xử lý tín hiệu với linuxThreads trên Linux 2.4 .


Trong những gì bạn nói "đã thực hiện" có nghĩa là gì? Ngoài ra, việc luôn đánh số các luồng khác để phản hồi lại một tín hiệu là không đúng (SIGHUP và SIGWINCH yêu cầu sự tinh tế hơn) và không an toàn khi sử dụng các biến điều kiện để cho các luồng khác biết. Câu trả lời kém.
Nghiên cứu sinh Donal

1
Đã xóa bỏ phiếu phản đối của tôi, nhưng đó vẫn không phải là câu trả lời đầy đủ vì tôi không thể giết chủ đề để phản hồi một tín hiệu. Trong một số trường hợp, tôi sẽ xếp hàng các sự kiện cục bộ để đáp ứng, trong một số trường hợp khác, tôi phải chia nhỏ các chủ đề rất cẩn thận (BTW, tôi đã có hầu hết máy móc để làm những phần đó rồi; đó là các kết nối với hệ điều hành tín hiệu bị thiếu).
Donal Fellows

1
@ zoli2k: Tôi vừa mới thử chạy make menuconfigvới nhánh git master mới được nhân bản của uClibc. Có một sự lựa chọn giữa LinuxThreads cũ và NPTL mới hơn như việc triển khai chủ đề POSIX, nhưng sự giúp đỡ như của năm 2012 vẫn khuyến cáo không nên chọn NPTL. Vì vậy, trong các hệ thống Linux nhúng hiện đại vẫn thường thấy việc triển khai LinuxThreads lỗi thời được sử dụng, ngay cả khi hệ thống đang chạy đủ nhân Linux gần đây.
FooF

3

Nơi tôi đang ở cho đến nay:

  • Các tín hiệu có các lớp chính khác nhau, một số trong số đó thường chỉ giết quá trình (SIGILL) và một số trong số đó không bao giờ cần làm gì cả (SIGIO; dễ dàng hơn để thực hiện không đồng bộ IO ngay). Hai lớp đó không cần hành động.
  • Một số tín hiệu không cần phải được xử lý ngay lập tức; những thứ như SIGWINCH có thể được xếp hàng đợi cho đến khi thuận tiện (giống như một sự kiện từ X11).
  • Những cái khó là những cái mà bạn muốn đáp lại chúng bằng cách làm gián đoạn những gì bạn đang làm nhưng không đến mức xóa sạch một chủ đề. Đặc biệt, SIGINT trong chế độ tương tác nên để mọi thứ phản hồi.

Tôi vẫn còn để sắp xếp thông qua signalvs sigaction, pselect, sigwait, sigaltstack, và một bó toàn bộ các bit và miếng của POSIX (và không POSIX) API khác.


3

Tín hiệu IMHO, Unix V và luồng posix không kết hợp tốt. Unix V là 1970. POSIX là 1980;)

Có các Điểm hủy và nếu bạn cho phép các tín hiệu và pthreads trong một ứng dụng, cuối cùng bạn sẽ phải ghi các Vòng lặp xung quanh mỗi cuộc gọi, điều này có thể trả về EINTR một cách đáng ngạc nhiên.

Vì vậy, những gì tôi đã làm trong (một vài) trường hợp mà tôi phải lập trình đa luồng trên Linux hoặc QNX là che giấu tất cả các tín hiệu cho tất cả (trừ một) luồng.

Khi một Tín hiệu Unix V đến, quy trình sẽ Chuyển ngăn xếp (điều đó xảy ra trong Unix V càng nhiều càng tốt mà bạn có thể nhận được trong một quy trình).

Như các bài viết khác ở đây gợi ý, bây giờ có thể nói với Hệ thống, chuỗi posix nào sẽ là nạn nhân của việc chuyển đổi ngăn xếp đó.

Sau khi, bạn đã quản lý để luồng xử lý Tín hiệu của mình hoạt động, câu hỏi vẫn còn là, làm thế nào để chuyển đổi thông tin tín hiệu sang một thứ gì đó văn minh mà các luồng khác có thể sử dụng. Cần có cơ sở hạ tầng cho giao tiếp giữa các luồng. Một kiểu hữu ích là kiểu diễn viên, trong đó mỗi luồng của bạn là mục tiêu cho một số cơ chế Nhắn tin trong quá trình.

Vì vậy, thay vì hủy các chuỗi khác hoặc giết chúng (hoặc những thứ kỳ lạ khác), bạn nên cố gắng điều chỉnh Tín hiệu từ ngữ cảnh Tín hiệu sang chuỗi xử lý Tín hiệu của bạn, sau đó sử dụng các cơ chế giao tiếp kiểu diễn viên của bạn để gửi các thông điệp hữu ích về mặt ngữ nghĩa cho các tác nhân đó, ai cần thông tin liên quan đến tín hiệu.

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.