signal --- Đặt trình xử lý cho các sự kiện không đồng bộ¶
Source code: Lib/signal.py
Mô-đun này cung cấp cơ chế sử dụng trình xử lý tín hiệu trong Python.
Quy tắc chung¶
Hàm signal.signal() cho phép xác định các trình xử lý tùy chỉnh sẽ được thực thi khi nhận được tín hiệu. Một số lượng nhỏ trình xử lý mặc định được cài đặt: SIGPIPE bị bỏ qua (vì vậy lỗi ghi trên đường ống và ổ cắm có thể được báo cáo là ngoại lệ Python thông thường) và SIGINT được dịch thành ngoại lệ KeyboardInterrupt nếu quy trình gốc không thay đổi.
Trình xử lý cho một tín hiệu cụ thể, sau khi được đặt, vẫn được cài đặt cho đến khi được đặt lại rõ ràng (Python mô phỏng giao diện kiểu BSD bất kể cách triển khai cơ bản), ngoại trừ trình xử lý dành cho SIGCHLD, tuân theo cách triển khai cơ bản.
Trên nền tảng WebAssembly, các tín hiệu được mô phỏng và do đó hoạt động khác nhau. Một số chức năng và tín hiệu không có sẵn trên các nền tảng này.
Thực thi các trình xử lý tín hiệu Python¶
Trình xử lý tín hiệu Python không được thực thi bên trong trình xử lý tín hiệu cấp thấp (C). Thay vào đó, trình xử lý tín hiệu cấp thấp sẽ đặt một cờ báo cho virtual machine thực thi trình xử lý tín hiệu Python tương ứng vào thời điểm sau đó (ví dụ: ở lệnh bytecode tiếp theo). Điều này có hậu quả:
Sẽ chẳng có ý nghĩa gì khi phát hiện các lỗi đồng bộ như
SIGFPEhoặcSIGSEGVdo thao tác không hợp lệ trong mã C gây ra. Python sẽ quay trở lại từ trình xử lý tín hiệu về mã C, điều này có khả năng lại tăng tín hiệu tương tự, khiến Python dường như bị treo. Từ Python 3.3 trở đi, bạn có thể sử dụng mô-đunfaulthandlerđể báo cáo lỗi đồng bộ.Một phép tính dài hạn được triển khai hoàn toàn bằng C (chẳng hạn như khớp biểu thức chính quy trên một lượng lớn văn bản) có thể chạy không bị gián đoạn trong một khoảng thời gian tùy ý, bất kể bất kỳ tín hiệu nào nhận được. Trình xử lý tín hiệu Python sẽ được gọi khi quá trình tính toán kết thúc.
Nếu trình xử lý đưa ra một ngoại lệ, nó sẽ được đưa ra "bất ngờ" trong luồng chính. Xem note below để thảo luận.
Tín hiệu và chủ đề¶
Trình xử lý tín hiệu Python luôn được thực thi trong luồng Python chính của trình thông dịch chính, ngay cả khi tín hiệu được nhận trong luồng khác. Điều này có nghĩa là tín hiệu không thể được sử dụng làm phương tiện liên lạc giữa các luồng. Thay vào đó, bạn có thể sử dụng các nguyên hàm đồng bộ hóa từ mô-đun threading.
Ngoài ra, chỉ luồng chính của trình thông dịch chính mới được phép đặt bộ xử lý tín hiệu mới.
Cảnh báo
Không nên sử dụng các nguyên hàm đồng bộ hóa như threading.Lock trong trình xử lý tín hiệu. Làm như vậy có thể dẫn đến những bế tắc không mong muốn.
Nội dung mô-đun¶
Thay đổi trong phiên bản 3.5: Các hằng số liên quan đến tín hiệu (SIG*), bộ xử lý (SIG_DFL, SIG_IGN) và sigmask (SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK) được liệt kê bên dưới đã được chuyển thành enums (Signals, Handlers và Sigmasks tương ứng). Các hàm getsignal(), pthread_sigmask(), sigpending() và sigwait() trả về enums mà con người có thể đọc được dưới dạng đối tượng Signals.
Mô-đun tín hiệu xác định ba enum:
- class signal.Signals¶
enum.IntEnumtập hợp các hằng số SIG* và các hằng số CTRL_*.Added in version 3.5.
- class signal.Handlers¶
enum.IntEnumtập hợp các hằng sốSIG_DFLvàSIG_IGN.Added in version 3.5.
- class signal.Sigmasks¶
enum.IntEnumtập hợp các hằng sốSIG_BLOCK,SIG_UNBLOCKvàSIG_SETMASK.sẵn có: Unix.
Xem trang hướng dẫn sigprocmask(2) và pthread_sigmask(3) để biết thêm thông tin.
Added in version 3.5.
Các biến được xác định trong mô-đun signal là:
- signal.SIG_DFL¶
Đây là một trong hai tùy chọn xử lý tín hiệu tiêu chuẩn; nó sẽ chỉ thực hiện chức năng mặc định cho tín hiệu. Ví dụ: trên hầu hết các hệ thống, hành động mặc định cho
SIGQUITlà kết xuất lõi và thoát, trong khi hành động mặc định choSIGCHLDchỉ đơn giản là bỏ qua nó.
- signal.SIG_IGN¶
Đây là một trình xử lý tín hiệu tiêu chuẩn khác, sẽ đơn giản bỏ qua tín hiệu đã cho.
- signal.SIGFPE¶
Ngoại lệ dấu phẩy động. Ví dụ: chia cho số 0.
Xem thêm
ZeroDivisionErrorđược nâng lên khi đối số thứ hai của phép chia hoặc phép toán modulo bằng 0.
- signal.SIGHUP¶
Phát hiện treo máy trên thiết bị đầu cuối điều khiển hoặc quá trình điều khiển bị chết.
sẵn có: Unix.
- signal.SIGILL¶
Hướng dẫn bất hợp pháp.
- signal.SIGINT¶
Ngắt từ bàn phím (CTRL + C).
Hành động mặc định là tăng
KeyboardInterrupt.
- signal.SIGPIPE¶
Ống bị hỏng: ghi vào ống không có đầu đọc.
Hành động mặc định là bỏ qua tín hiệu.
sẵn có: Unix.
- signal.SIGSEGV¶
Lỗi phân đoạn: tham chiếu bộ nhớ không hợp lệ.
- signal.SIGSTKFLT¶
Lỗi ngăn xếp trên bộ đồng xử lý. Nhân Linux không đưa ra tín hiệu này: nó chỉ có thể được nâng lên trong không gian người dùng.
sẵn có: Linux.
Trên các kiến trúc có tín hiệu sẵn có. Xem trang man signal(7) để biết thêm thông tin.
Added in version 3.11.
- signal.SIGTERM¶
Tín hiệu kết thúc.
- SIG*
Tất cả các số tín hiệu được xác định một cách tượng trưng. Ví dụ: tín hiệu cúp máy được xác định là
signal.SIGHUP; tên biến giống hệt với tên được sử dụng trong chương trình C, như được tìm thấy trong<signal.h>. Trang man Unix cho 'signal' liệt kê các tín hiệu hiện có (trên một số hệ thống, đây là signal(2), trên các hệ thống khác, danh sách này là signal(7)). Lưu ý rằng không phải tất cả các hệ thống đều xác định cùng một bộ tên tín hiệu; chỉ những tên do hệ thống xác định mới được xác định bởi mô-đun này.
- signal.CTRL_C_EVENT¶
Tín hiệu tương ứng với sự kiện gõ phím Ctrl+C. Tín hiệu này chỉ có thể được sử dụng với
os.kill().sẵn có: Windows.
Added in version 3.2.
- signal.CTRL_BREAK_EVENT¶
Tín hiệu tương ứng với sự kiện gõ phím Ctrl+Break. Tín hiệu này chỉ có thể được sử dụng với
os.kill().sẵn có: Windows.
Added in version 3.2.
- signal.NSIG¶
Nhiều hơn số lượng tín hiệu cao nhất. Sử dụng
valid_signals()để nhận số tín hiệu hợp lệ.
- signal.ITIMER_VIRTUAL¶
Chỉ giảm bộ đếm thời gian khi quá trình đang thực thi và cung cấp SIGVTALRM khi hết hạn.
- signal.ITIMER_PROF¶
Giảm khoảng thời gian định giờ cả khi quá trình thực thi và khi hệ thống thực thi thay mặt cho quá trình đó. Cùng với ITIMER_VIRTUAL, bộ hẹn giờ này thường được sử dụng để lập hồ sơ thời gian mà ứng dụng dành cho không gian người dùng và kernel. SIGPROF được giao khi hết hạn.
- signal.SIG_BLOCK¶
Một giá trị có thể có của tham số how thành
pthread_sigmask()cho biết các tín hiệu sẽ bị chặn.Added in version 3.3.
- signal.SIG_UNBLOCK¶
Một giá trị có thể có của tham số how thành
pthread_sigmask()cho biết rằng các tín hiệu sẽ được bỏ chặn.Added in version 3.3.
- signal.SIG_SETMASK¶
Một giá trị có thể có của tham số how thành
pthread_sigmask()cho biết mặt nạ tín hiệu sẽ được thay thế.Added in version 3.3.
Mô-đun signal xác định một ngoại lệ:
- exception signal.ItimerError¶
Được đưa ra để báo hiệu lỗi từ quá trình triển khai
setitimer()hoặcgetitimer()cơ bản. Có thể xảy ra lỗi này nếu bộ hẹn giờ ngắt quãng không hợp lệ hoặc thời gian âm được chuyển tớisetitimer(). Lỗi này là một kiểu con củaOSError.
Mô-đun signal xác định các chức năng sau:
- signal.alarm(time)¶
Nếu time khác 0, hàm này yêu cầu tín hiệu
SIGALRMđược gửi đến quy trình sau time giây. Mọi báo thức đã lên lịch trước đó đều bị hủy (chỉ có thể lập lịch một báo thức bất cứ lúc nào). Giá trị được trả về sau đó là số giây trước khi bất kỳ cảnh báo nào được đặt trước đó được gửi đi. Nếu time bằng 0, không có cảnh báo nào được lên lịch và mọi cảnh báo đã lên lịch sẽ bị hủy. Nếu giá trị trả về bằng 0 thì hiện tại không có cảnh báo nào được lên lịch.
- signal.getsignal(signalnum)¶
Trả về bộ xử lý tín hiệu hiện tại cho tín hiệu signalnum. Giá trị trả về có thể là một đối tượng Python có thể gọi được hoặc một trong các giá trị đặc biệt
signal.SIG_IGN,signal.SIG_DFLhoặcNone. Ở đây,signal.SIG_IGNcó nghĩa là tín hiệu đã bị bỏ qua trước đó,signal.SIG_DFLcó nghĩa là cách xử lý tín hiệu mặc định đã được sử dụng trước đó vàNonecó nghĩa là trình xử lý tín hiệu trước đó chưa được cài đặt từ Python.
- signal.strsignal(signalnum)¶
Trả về mô tả của tín hiệu signalnum, chẳng hạn như "Ngắt" cho
SIGINT. Trả vềNonenếu signalnum không có mô tả. TăngValueErrornếu signalnum không hợp lệ.Added in version 3.8.
- signal.valid_signals()¶
Trả về bộ số tín hiệu hợp lệ trên nền tảng này. Giá trị này có thể nhỏ hơn
range(1, NSIG)nếu một số tín hiệu được hệ thống dành riêng để sử dụng nội bộ.Added in version 3.8.
- signal.pause()¶
Làm cho quá trình ngủ cho đến khi nhận được tín hiệu; trình xử lý thích hợp sau đó sẽ được gọi. Không trả lại gì cả.
Xem thêm
sigwait(),sigwaitinfo(),sigtimedwait()vàsigpending().
- signal.raise_signal(signum)¶
Gửi tín hiệu đến quá trình gọi. Không trả lại gì cả.
Added in version 3.8.
- signal.pidfd_send_signal(pidfd, sig, siginfo=None, flags=0)¶
Gửi tín hiệu sig đến quy trình được tham chiếu bởi bộ mô tả tệp pidfd. Python hiện không hỗ trợ tham số siginfo; nó phải là
None. Đối số flags được cung cấp cho các tiện ích mở rộng trong tương lai; hiện tại không có giá trị cờ nào được xác định.Xem trang man pidfd_send_signal(2) để biết thêm thông tin.
sẵn có: Linux >= 5.1, Android >=
build-timeAPI level 31Added in version 3.9.
- signal.pthread_kill(thread_id, signalnum)¶
Gửi tín hiệu signalnum đến thread thread_id, một thread khác trong cùng tiến trình với người gọi. Chuỗi đích có thể thực thi bất kỳ mã nào (Python hoặc không). Tuy nhiên, nếu luồng đích đang thực thi trình thông dịch Python thì trình xử lý tín hiệu Python sẽ là executed by the main thread of the main interpreter. Do đó, điểm duy nhất để gửi tín hiệu đến một luồng Python cụ thể là buộc lệnh gọi hệ thống đang chạy không thành công với
InterruptedError.Sử dụng thuộc tính
threading.get_ident()hoặcidentcủa đối tượngthreading.Threadđể nhận giá trị phù hợp cho thread_id.Nếu signalnum bằng 0 thì không có tín hiệu nào được gửi nhưng việc kiểm tra lỗi vẫn được thực hiện; điều này có thể được sử dụng để kiểm tra xem luồng mục tiêu có còn chạy hay không.
Tăng một auditing event
signal.pthread_killvới các đối sốthread_id,signalnum.sẵn có: Unix.
Xem trang man pthread_kill(3) để biết thêm thông tin.
Xem thêm
os.kill().Added in version 3.3.
- signal.pthread_sigmask(how, mask)¶
Tìm nạp và/hoặc thay đổi mặt nạ tín hiệu của chuỗi cuộc gọi. Mặt nạ tín hiệu là tập hợp các tín hiệu mà việc gửi tín hiệu hiện đang bị chặn đối với người gọi. Trả lại mặt nạ tín hiệu cũ dưới dạng một tập hợp tín hiệu.
Hoạt động của cuộc gọi phụ thuộc vào giá trị của how, như sau.
SIG_BLOCK: Tập hợp các tín hiệu bị chặn là sự kết hợp của tập hợp hiện tại và đối số mask.SIG_UNBLOCK: Các tín hiệu trong mask bị xóa khỏi nhóm tín hiệu bị chặn hiện tại. Được phép cố gắng bỏ chặn tín hiệu không bị chặn.SIG_SETMASK: Tập hợp các tín hiệu bị chặn được đặt thành đối số mask.
mask là tập hợp các số tín hiệu (ví dụ: {
signal.SIGINT,signal.SIGTERM}). Sử dụngvalid_signals()cho mặt nạ đầy đủ bao gồm tất cả các tín hiệu.Ví dụ:
signal.pthread_sigmask(signal.SIG_BLOCK, [])đọc mặt nạ tín hiệu của chuỗi cuộc gọi.SIGKILLvàSIGSTOPkhông thể bị chặn.sẵn có: Unix.
Xem trang hướng dẫn sigprocmask(2) và pthread_sigmask(3) để biết thêm thông tin.
Xem thêm
pause(),sigpending()vàsigwait().Added in version 3.3.
- signal.setitimer(which, seconds, interval=0.0)¶
Đặt bộ hẹn giờ khoảng thời gian nhất định (một trong các
signal.ITIMER_REAL,signal.ITIMER_VIRTUALhoặcsignal.ITIMER_PROF) do which chỉ định để kích hoạt sau seconds (float được chấp nhận, khác vớialarm()) và sau đó cứ mỗi interval giây (nếu interval khác 0). Có thể xóa bộ định thời khoảng thời gian do which chỉ định bằng cách đặt seconds về 0.Khi bộ hẹn giờ ngắt quãng kích hoạt, tín hiệu sẽ được gửi đến quy trình. Tín hiệu được gửi phụ thuộc vào bộ hẹn giờ được sử dụng;
signal.ITIMER_REALsẽ gửiSIGALRM,signal.ITIMER_VIRTUALgửiSIGVTALRMvàsignal.ITIMER_PROFsẽ gửiSIGPROF.Các giá trị cũ được trả về dưới dạng một bộ: (độ trễ, khoảng thời gian).
Cố gắng vượt qua bộ hẹn giờ khoảng thời gian không hợp lệ sẽ gây ra
ItimerError.sẵn có: Unix.
- signal.getitimer(which)¶
Trả về giá trị hiện tại của bộ định thời khoảng thời gian nhất định được chỉ định bởi which.
sẵn có: Unix.
- signal.set_wakeup_fd(fd, *, warn_on_full_buffer=True)¶
Đặt bộ mô tả tệp đánh thức thành fd. Khi nhận được tín hiệu mà chương trình của bạn đã đăng ký bộ xử lý tín hiệu, số tín hiệu sẽ được ghi dưới dạng một byte vào fd. Nếu bạn chưa đăng ký trình xử lý tín hiệu cho các tín hiệu mà bạn quan tâm thì sẽ không có gì được ghi vào fd đánh thức. Thư viện có thể sử dụng tính năng này để đánh thức cuộc thăm dò ý kiến hoặc cuộc gọi chọn lọc, cho phép tín hiệu được xử lý hoàn toàn.
Fd đánh thức cũ được trả về (hoặc -1 nếu đánh thức bộ mô tả tệp không được bật). Nếu fd là -1, việc đánh thức bộ mô tả tập tin bị tắt. Nếu không phải -1, fd phải không bị chặn. Tùy thuộc vào thư viện để xóa bất kỳ byte nào khỏi fd trước khi gọi cuộc thăm dò ý kiến hoặc chọn lại.
Khi chủ đề được bật, chức năng này chỉ có thể được gọi từ the main thread of the main interpreter; cố gắng gọi nó từ các luồng khác sẽ gây ra ngoại lệ
ValueError.Có hai cách phổ biến để sử dụng chức năng này. Trong cả hai phương pháp, bạn sử dụng fd để đánh thức khi có tín hiệu đến, nhưng sau đó chúng khác nhau ở cách xác định tín hiệu which hoặc các tín hiệu đã đến.
Trong cách tiếp cận đầu tiên, chúng tôi đọc dữ liệu từ bộ đệm của fd và các giá trị byte cung cấp cho bạn các số tín hiệu. Điều này rất đơn giản, nhưng trong một số ít trường hợp, nó có thể gặp sự cố: nhìn chung fd sẽ có một lượng không gian bộ đệm hạn chế và nếu có quá nhiều tín hiệu đến quá nhanh thì bộ đệm có thể đầy và một số tín hiệu có thể bị mất. Nếu bạn sử dụng phương pháp này thì bạn nên đặt
warn_on_full_buffer=True, điều này ít nhất sẽ khiến cảnh báo được in ra stderr khi mất tín hiệu.Trong cách tiếp cận thứ hai, chúng tôi sử dụng Wakeup fd only để đánh thức và bỏ qua các giá trị byte thực tế. Trong trường hợp này, tất cả những gì chúng ta quan tâm là liệu bộ đệm của fd trống hay không trống; bộ đệm đầy hoàn toàn không cho thấy có vấn đề gì. Nếu sử dụng phương pháp này thì bạn nên đặt
warn_on_full_buffer=Falseđể người dùng không bị nhầm lẫn bởi các thông báo cảnh báo giả mạo.Thay đổi trong phiên bản 3.5: Trên Windows, chức năng này hiện cũng hỗ trợ các tay cầm ổ cắm.
Thay đổi trong phiên bản 3.7: Đã thêm tham số
warn_on_full_buffer.
- signal.siginterrupt(signalnum, flag)¶
Thay đổi hành vi khởi động lại cuộc gọi hệ thống: nếu flag là
False, các cuộc gọi hệ thống sẽ được khởi động lại khi bị gián đoạn bởi tín hiệu signalnum, nếu không các cuộc gọi hệ thống sẽ bị gián đoạn. Không trả lại gì cả.sẵn có: Unix.
Xem trang man siginterrupt(3) để biết thêm thông tin.
Lưu ý rằng việc cài đặt trình xử lý tín hiệu với
signal()sẽ đặt lại hành vi khởi động lại thành có thể gián đoạn bằng cách gọi ngầmsiginterrupt()với giá trị flag thực cho tín hiệu đã cho.
- signal.signal(signalnum, handler)¶
Đặt bộ xử lý tín hiệu signalnum thành hàm handler. handler có thể là một đối tượng Python có thể gọi được với hai đối số (xem bên dưới) hoặc một trong các giá trị đặc biệt
signal.SIG_IGNhoặcsignal.SIG_DFL. Trình xử lý tín hiệu trước đó sẽ được trả về (xem mô tả vềgetsignal()ở trên). (Xem trang Unix signal(2) để biết thêm thông tin.)Khi chủ đề được bật, chức năng này chỉ có thể được gọi từ the main thread of the main interpreter; cố gắng gọi nó từ các luồng khác sẽ gây ra ngoại lệ
ValueError.handler được gọi với hai đối số: số tín hiệu và khung ngăn xếp hiện tại (
Nonehoặc đối tượng khung; để biết mô tả về đối tượng khung, hãy xem description in the type hierarchy hoặc xem mô tả thuộc tính trong mô-đuninspect).Trên Windows,
signal()chỉ có thể được gọi bằngSIGABRT,SIGFPE,SIGILL,SIGINT,SIGSEGV,SIGTERMhoặcSIGBREAK. MộtValueErrorsẽ được nâng lên trong mọi trường hợp khác. Lưu ý rằng không phải tất cả các hệ thống đều xác định cùng một bộ tên tín hiệu; mộtAttributeErrorsẽ được nâng lên nếu tên tín hiệu không được xác định là hằng số mức mô-đunSIG*.
- signal.sigpending()¶
Kiểm tra tập hợp các tín hiệu đang chờ gửi đến luồng đang gọi (tức là các tín hiệu đã được đưa ra trong khi bị chặn). Trả về tập hợp các tín hiệu đang chờ xử lý.
sẵn có: Unix.
Xem trang man sigpending(2) để biết thêm thông tin.
Xem thêm
pause(),pthread_sigmask()vàsigwait().Added in version 3.3.
- signal.sigwait(sigset)¶
Tạm dừng thực thi luồng đang gọi cho đến khi phân phối một trong các tín hiệu được chỉ định trong bộ tín hiệu sigset. Hàm chấp nhận tín hiệu (xóa nó khỏi danh sách tín hiệu đang chờ xử lý) và trả về số tín hiệu.
sẵn có: Unix.
Xem trang man sigwait(3) để biết thêm thông tin.
Xem thêm
pause(),pthread_sigmask(),sigpending(),sigwaitinfo()vàsigtimedwait().Added in version 3.3.
- signal.sigwaitinfo(sigset)¶
Tạm dừng thực thi luồng đang gọi cho đến khi phân phối một trong các tín hiệu được chỉ định trong bộ tín hiệu sigset. Hàm chấp nhận tín hiệu và xóa nó khỏi danh sách tín hiệu đang chờ xử lý. Nếu một trong các tín hiệu trong sigset đang chờ xử lý cho luồng gọi, hàm sẽ trả về ngay lập tức với thông tin về tín hiệu đó. Bộ xử lý tín hiệu không được gọi cho tín hiệu được gửi. Hàm này sẽ tăng
InterruptedErrornếu nó bị gián đoạn bởi tín hiệu không thuộc sigset.Giá trị trả về là một đối tượng biểu thị dữ liệu chứa trong cấu trúc
siginfo_t, cụ thể là:si_signo,si_code,si_errno,si_pid,si_uid,si_status,si_band.sẵn có: Unix.
Xem trang man sigwaitinfo(2) để biết thêm thông tin.
Xem thêm
pause(),sigwait()vàsigtimedwait().Added in version 3.3.
Thay đổi trong phiên bản 3.5: Chức năng này hiện được thử lại nếu bị gián đoạn bởi tín hiệu không thuộc sigset và trình xử lý tín hiệu không đưa ra ngoại lệ (xem PEP 475 để biết lý do).
- signal.sigtimedwait(sigset, timeout)¶
Giống như
sigwaitinfo(), nhưng có thêm một đối số timeout chỉ định thời gian chờ. Nếu timeout được chỉ định là0, một cuộc thăm dò sẽ được thực hiện. Trả vềNonenếu hết thời gian chờ.sẵn có: Unix.
Xem trang man sigtimedwait(2) để biết thêm thông tin.
Xem thêm
pause(),sigwait()vàsigwaitinfo().Added in version 3.3.
Thay đổi trong phiên bản 3.5: Hàm này hiện được thử lại với timeout được tính toán lại nếu bị gián đoạn bởi tín hiệu không thuộc sigset và trình xử lý tín hiệu không đưa ra ngoại lệ (xem PEP 475 để biết lý do).
Ví dụ¶
Đây là một chương trình ví dụ tối thiểu. Nó sử dụng chức năng alarm() để giới hạn thời gian chờ mở tệp; điều này hữu ích nếu tệp dành cho một thiết bị nối tiếp có thể không được bật, điều này thường khiến os.open() bị treo vô thời hạn. Giải pháp là đặt báo thức 5 giây trước khi mở tệp; nếu thao tác mất quá nhiều thời gian, tín hiệu cảnh báo sẽ được gửi và trình xử lý sẽ đưa ra một ngoại lệ.
tín hiệu nhập khẩu, hệ điều hành
trình xử lý def (dấu hiệu, khung):
signame = signal.Signals(signum).name
print(f'Trình xử lý tín hiệu được gọi với tín hiệu {signame} ({signum})')
raise OSError("Không thể mở thiết bị!")
# Set bộ xử lý tín hiệu và cảnh báo 5 giây
signal.signal(signal.SIGALRM, trình xử lý)
tín hiệu.alarm(5)
# This open() có thể bị treo vô thời hạn
fd = os.open('/dev/ttyS0', os.O_RDWR)
signal.alarm(0) # Disable báo động
Lưu ý về SIGPIPE¶
Việc chuyển đầu ra chương trình của bạn tới các công cụ như head(1) sẽ khiến tín hiệu SIGPIPE được gửi đến quy trình của bạn khi bộ thu đầu ra tiêu chuẩn của nó đóng sớm. Điều này dẫn đến một ngoại lệ như BrokenPipeError: [Errno 32] Broken pipe. Để xử lý trường hợp này, hãy bọc điểm vào của bạn để bắt ngoại lệ này như sau
hệ điều hành nhập khẩu
hệ thống nhập khẩu
chắc chắn chính():
thử:
đầu ra lớn # simulate (mã của bạn thay thế vòng lặp này)
cho x trong phạm vi (10000):
in ("y")
# flush xuất ra ở đây để buộc SIGPIPE được kích hoạt
# while bên trong khối thử này.
sys.stdout.flush()
ngoại trừ BrokenPipeError:
# Python xóa các luồng tiêu chuẩn khi thoát; chuyển hướng đầu ra còn lại
# to devnull để tránh BrokenPipeError khác khi tắt máy
devnull = os.open(os.devnull, os.O_WRONLY)
os.dup2(devnull, sys.stdout.fileno())
sys.exit(1) # Python thoát với mã lỗi 1 trên EPIPE
nếu __name__ == '__main__':
chính()
Không đặt vị trí của SIGPIPE thành SIG_DFL để tránh BrokenPipeError. Làm như vậy sẽ khiến chương trình của bạn thoát ra bất ngờ bất cứ khi nào bất kỳ kết nối ổ cắm nào bị gián đoạn trong khi chương trình của bạn vẫn đang ghi vào đó.
Lưu ý về Trình xử lý tín hiệu và ngoại lệ¶
Nếu trình xử lý tín hiệu đưa ra một ngoại lệ, ngoại lệ đó sẽ được truyền đến luồng chính và có thể được đưa ra sau bất kỳ lệnh bytecode nào. Đáng chú ý nhất là KeyboardInterrupt có thể xuất hiện bất kỳ lúc nào trong quá trình thực thi. Hầu hết mã Python, bao gồm cả thư viện tiêu chuẩn, không thể chống lại điều này một cách mạnh mẽ và do đó, KeyboardInterrupt (hoặc bất kỳ ngoại lệ nào khác do trình xử lý tín hiệu) có thể trong một số trường hợp hiếm hoi khiến chương trình ở trạng thái không mong muốn.
Để minh họa vấn đề này, hãy xem xét đoạn mã sau:
lớp SpamContext:
định nghĩa __init__(tự):
self.lock = threading.Lock()
chắc chắn __enter__(tự):
# If Bàn phím bị gián đoạn xảy ra ở đây, mọi thứ đều ổn
self.lock.acquire()
# If Bàn phím bị gián đoạn xảy ra ở đây, __exit__ sẽ không được gọi
...
# KeyboardInterrupt có thể xảy ra ngay trước khi hàm trả về
def __exit__(self, exc_type, exc_val, exc_tb):
...
self.lock.release()
Đối với nhiều chương trình, đặc biệt là những chương trình chỉ muốn thoát trên KeyboardInterrupt, đây không phải là vấn đề, nhưng các ứng dụng phức tạp hoặc yêu cầu độ tin cậy cao nên tránh đưa ra ngoại lệ từ bộ xử lý tín hiệu. Họ cũng nên tránh bắt gặp KeyboardInterrupt như một cách tắt máy một cách nhẹ nhàng. Thay vào đó, họ nên cài đặt trình xử lý SIGINT của riêng mình. Dưới đây là ví dụ về máy chủ HTTP tránh KeyboardInterrupt:
tín hiệu nhập khẩu
ổ cắm nhập khẩu
từ bộ chọn nhập DefaultSelector, EVENT_READ
từ http.server nhập HTTPServer, SimpleHTTPRequestHandler
ngắt_đọc, ngắt_write = socket.socketpair()
trình xử lý def (dấu hiệu, khung):
print('Trình xử lý tín hiệu được gọi bằng tín hiệu', signum)
ngắt_write.send(b'\0')
signal.signal(signal.SIGINT, trình xử lý)
def phục vụ_forver(httpd):
sel = Bộ chọn mặc định()
sel.register(interrupt_read, EVENT_READ)
sel.register(httpd, EVENT_READ)
trong khi Đúng:
cho khóa, _ trong sel.select():
nếu key.fileobj == ngắt_read:
ngắt_read.recv(1)
trở về
nếu key.fileobj == httpd:
httpd.handle_request()
print("Phục vụ trên cổng 8000")
httpd = HTTPServer(('', 8000), SimpleHTTPRequestHandler)
phục vụ_forever(httpd)
print("Tắt máy...")