signal --- تنظیم هندلرها برای رویدادهای ناهمگام¶
کد منبع: Lib/signal.py
این ماژول سازوکارهایی را برای استفاده از هندلرهای سیگنال در پایتون فراهم میکند.
قواعد عمومی¶
تابع signal.signal() به شما اجازه میدهد هندلرهای سفارشی را تعریف کنید تا هنگام دریافت یک سیگنال اجرا شوند. تعداد اندکی از هندلرهای پیشفرض نصب شدهاند: SIGPIPE نادیده گرفته میشود (تا خطاهای نوشتن روی پایپها و سوکتها بتوانند بهعنوان استثناهای معمول پایتون گزارش شوند) و SIGINT در صورتی که فرایند والد آن را تغییر نداده باشد، به استثنای KeyboardInterrupt تبدیل میشود.
یک هندلر برای یک سیگنال خاص، پس از تنظیم شدن، تا زمانی که بهصراحت بازنشانی نشود، نصبشده باقی میماند (پایتون صرفنظر از پیادهسازی زیربنایی، رابط به سبک BSD را شبیهسازی میکند)، به استثنای هندلر SIGCHLD که از پیادهسازی زیربنایی پیروی میکند.
در پلتفرمهای WebAssembly، سیگنالها شبیهسازی میشوند و بنابراین رفتار متفاوتی دارند. چندین تابع و سیگنال در این پلتفرمها در دسترس نیستند.
اجرای هندلرهای سیگنال پایتون¶
هندلر سیگنال پایتون درون هندلر سیگنال سطح پایین (C) اجرا نمیشود. در عوض، هندلر سیگنال سطح پایین پرچمی را تنظیم میکند که به virtual machine میگوید هندلر سیگنال پایتون متناظر را در نقطهای بعدی (برای مثال، در دستور bytecode بعدی) اجرا کند. این موضوع پیامدهایی دارد:
گرفتن خطاهای همگام مانند
SIGFPEیاSIGSEGVکه در اثر یک عملیات نامعتبر در کد C ایجاد میشوند، چندان منطقی نیست. پایتون از هندلر سیگنال به کد C بازمیگردد، که احتمال دارد همان سیگنال را دوباره پرتاب کند و باعث شود پایتون ظاهراً هنگ کند. از پایتون 3.3 به بعد، میتوانید از ماژولfaulthandlerبرای گزارش خطاهای همگام استفاده کنید.محاسبهای طولانیمدت که بهطور کامل در C پیادهسازی شده باشد (مانند تطبیق عبارت باقاعده روی حجم بزرگی از متن) ممکن است صرفنظر از هر سیگنال دریافتی، برای مدت زمانی دلخواه بدون وقفه اجرا شود. هندلرهای سیگنال پایتون هنگامی که محاسبه به پایان برسد فراخوانی خواهند شد.
اگر هندلر استثنایی را پرتاب کند، آن استثنا «بهصورت ناگهانی» در نخ اصلی پرتاب خواهد شد. برای توضیحات بیشتر، یادداشت زیر را ببینید.
سیگنالها و نخها¶
هندلرهای سیگنال پایتون همیشه در نخ اصلی پایتون در مفسر اصلی اجرا میشوند، حتی اگر سیگنال در نخ دیگری دریافت شده باشد. این بدان معناست که نمیتوان از سیگنالها بهعنوان وسیلهای برای ارتباط بین نخها استفاده کرد. در عوض، میتوانید از اولیههای همگامسازی ماژول threading استفاده کنید.
علاوه بر این، فقط نخ اصلی مفسر اصلی مجاز به تنظیم یک مدیر سیگنال (signal handler) جدید است.
هشدار
نباید از اولیههای همگامسازی مانند threading.Lock در هندلرهای سیگنال استفاده شود. این کار میتواند به بنبستهای غیرمنتظره منجر شود.
محتویات ماژول¶
تغییر یافته در نسخهی 3.5: ثابتهای مرتبط با سیگنال (SIG*)، هندلر (SIG_DFL، SIG_IGN) و نقاب سیگنال (SIG_BLOCK، SIG_UNBLOCK، SIG_SETMASK) که در زیر فهرست شدهاند، به enums تبدیل شدهاند (بهترتیب Signals، Handlers و Sigmasks). توابع getsignal()، pthread_sigmask()، sigpending() و sigwait()، enums قابلخواندن برای انسان را بهصورت اشیای Signals بازمیگردانند.
ماژول signal سه enum تعریف میکند:
- class signal.Signals¶
enum.IntEnumمجموعهای از ثابتهای SIG* و ثابتهای CTRL_*.اضافه شده در نسخهی 3.5.
- class signal.Handlers¶
enum.IntEnumمجموعهای از ثابتهایSIG_DFLوSIG_IGN.اضافه شده در نسخهی 3.5.
- class signal.Sigmasks¶
مجموعهی
enum.IntEnumاز ثابتهایSIG_BLOCK،SIG_UNBLOCKوSIG_SETMASK.دسترسپذیری: Unix.
برای اطلاعات بیشتر، صفحههای راهنمای sigprocmask(2) و pthread_sigmask(3) را ببینید.
اضافه شده در نسخهی 3.5.
متغیرهای تعریفشده در ماژول signal عبارتند از:
- signal.SIG_DFL¶
این یکی از دو گزینهی استاندارد مدیریت سیگنال است؛ این گزینه صرفاً رفتار پیشفرض سیگنال را اعمال میکند. برای مثال، در بیشتر سیستمها رفتار پیشفرض برای
SIGQUITبرونریزی core (dump core) و خروج است، در حالی که رفتار پیشفرض برایSIGCHLDصرفاً نادیده گرفتن آن است.
- signal.SIG_IGN¶
این یک هندلر سیگنال استاندارد دیگر است که بهسادگی سیگنال دادهشده را نادیده میگیرد.
- signal.SIGALRM¶
سیگنال زمانسنج از alarm(2).
دسترسپذیری: Unix.
- signal.SIGBREAK¶
وقفه از صفحهکلید (CTRL + BREAK).
دسترسپذیری: Windows.
- signal.SIGBUS¶
خطای گذرگاه (دسترسی نامعتبر به حافظه).
دسترسپذیری: Unix.
- signal.SIGCHLD¶
فرایند فرزند متوقف شد یا خاتمه یافت.
دسترسپذیری: Unix.
- signal.SIGCLD¶
نام مستعار برای
SIGCHLD.دسترسپذیری: not macOS.
- signal.SIGCONT¶
اگر فرآیند در حال حاضر متوقف است، آن را ادامه دهید
دسترسپذیری: Unix.
- signal.SIGFPE¶
استثنای ممیز شناور. برای مثال، تقسیم بر صفر.
همچنین ملاحظه نمائید
ZeroDivisionErrorزمانی پرتاب میشود که دومین آرگومان یک عملیات تقسیم یا باقیمانده صفر باشد.
- signal.SIGHUP¶
قطع ارتباط (Hangup) در پایانه کنترلکننده یا مرگ فرایند کنترلکننده شناسایی شد.
دسترسپذیری: Unix.
- signal.SIGILL¶
دستورالعمل غیرمجاز.
- signal.SIGINT¶
وقفه از صفحهکلید (CTRL + C).
اقدام پیشفرض، پرتاب
KeyboardInterruptاست.
- signal.SIGKILL¶
سیگنال کشتن (Kill signal).
نمیتوان آن را گرفت، مسدود کرد یا نادیده گرفت.
دسترسپذیری: Unix.
- signal.SIGPIPE¶
پایپی شکسته: نوشتن در پایپای بدون خواننده.
اقدام پیشفرض، نادیده گرفتن سیگنال است.
دسترسپذیری: Unix.
- signal.SIGPROF¶
زمانسنج پروفایلسازی منقضی شد.
دسترسپذیری: Unix.
- signal.SIGQUIT¶
سیگنال خروج پایانه.
دسترسپذیری: Unix.
- signal.SIGSEGV¶
خطای قطعهبندی: ارجاع نامعتبر به حافظه.
- signal.SIGSTOP¶
توقف اجرا (قابل گرفتن یا نادیدهگرفتن نیست).
دسترسپذیری: Unix.
- signal.SIGSTKFLT¶
خطای پشته در همپردازنده. هستهی لینوکس این سیگنال را پرتاب نمیکند: فقط میتواند در فضای کاربر پرتاب شود.
دسترسپذیری: Linux.
در معماریهایی که سیگنال در دسترس است. برای اطلاعات بیشتر، صفحهی man signal(7) را ببینید.
اضافه شده در نسخهی 3.11.
- signal.SIGTERM¶
سیگنال خاتمه.
- signal.SIGUSR1¶
سیگنال ۱ تعریفشده توسط کاربر.
دسترسپذیری: Unix.
- signal.SIGUSR2¶
سیگنال ۲ تعریفشده توسط کاربر.
دسترسپذیری: Unix.
- signal.SIGVTALRM¶
زمانسنج مجازی منقضی شد.
دسترسپذیری: Unix.
- signal.SIGWINCH¶
سیگنال تغییر اندازهی پنجره.
دسترسپذیری: Unix.
- signal.SIGXCPU¶
از محدودیت زمان CPU فراتر رفت.
دسترسپذیری: Unix.
- SIG*
همه شمارههای سیگنال بهصورت نمادین تعریف شدهاند. برای مثال، سیگنال قطع ارتباط بهصورت
signal.SIGHUPتعریف شده است؛ نام متغیرها دقیقاً همان نامهایی هستند که در برنامههای C استفاده میشوند، همانطور که در<signal.h>آمده است. صفحه راهنمای یونیکس برای «signal» سیگنالهای موجود را فهرست میکند (در برخی سیستمها این signal(2) است، در برخی دیگر فهرست در signal(7) قرار دارد). توجه داشته باشید که همه سیستمها مجموعه یکسانی از نامهای سیگنال را تعریف نمیکنند؛ این ماژول تنها نامهایی را تعریف میکند که سیستم آنها را تعریف کرده است.
- signal.CTRL_C_EVENT¶
سیگنال متناظر با رویداد فشردن کلید Ctrl+C. این سیگنال را فقط میتوان با
os.kill()استفاده کرد.دسترسپذیری: Windows.
اضافه شده در نسخهی 3.2.
- signal.CTRL_BREAK_EVENT¶
سیگنال متناظر با رویداد فشردن کلید Ctrl+Break. این سیگنال فقط میتواند با
os.kill()استفاده شود.دسترسپذیری: Windows.
اضافه شده در نسخهی 3.2.
- signal.NSIG¶
یک بیشتر از بالاترین شمارهی سیگنال. برای دریافت شمارههای سیگنال معتبر از
valid_signals()استفاده کنید.
- signal.ITIMER_REAL¶
زمانسنج بازهای را در زمان واقعی کاهش میدهد و پس از انقضا،
SIGALRMرا ارسال میکند.
- signal.ITIMER_VIRTUAL¶
زمانسنج فاصلهای را تنها زمانی که فرایند در حال اجرا است کاهش میدهد و پس از انقضا، SIGVTALRM را ارسال میکند.
- signal.ITIMER_PROF¶
زمانسنج بازهای را هم زمانی که فرایند اجرا میشود و هم زمانی که سیستم به نمایندگی از فرایند در حال اجرا است، کاهش میدهد. این زمانسنج معمولاً همراه با ITIMER_VIRTUAL برای پروفایلگیری زمانی که برنامه در فضای کاربر و فضای هسته صرف میکند، استفاده میشود. SIGPROF پس از انقضا ارسال میشود.
- signal.SIG_BLOCK¶
یک مقدار ممکن برای پارامتر how در
pthread_sigmask()که نشان میدهد سیگنالها باید مسدود شوند.اضافه شده در نسخهی 3.3.
- signal.SIG_UNBLOCK¶
یک مقدار ممکن برای پارامتر how در
pthread_sigmask()که نشان میدهد سیگنالها باید رفع مسدود شوند.اضافه شده در نسخهی 3.3.
- signal.SIG_SETMASK¶
یک مقدار ممکن برای پارامتر how در
pthread_sigmask()که نشان میدهد نقاب سیگنال (signal mask) باید جایگزین شود.اضافه شده در نسخهی 3.3.
ماژول signal یک استثنا تعریف میکند:
- exception signal.ItimerError¶
برای اعلام یک خطا از پیادهسازی زیرین
setitimer()یاgetitimer()پرتاب میشود. در صورتی که زمانسنج بازهای نامعتبر یا زمان منفی بهsetitimer()داده شود، این خطا مورد انتظار است. این خطا زیرنوعی ازOSErrorاست.
ماژول signal توابع زیر را تعریف میکند:
- signal.alarm(time)¶
اگر time غیرصفر باشد، این تابع درخواست میکند که یک سیگنال
SIGALRMپس از time ثانیه به فرایند ارسال شود. هر هشدار از پیش زمانبندیشده لغو میشود (در هر زمان فقط یک هشدار میتواند زمانبندی شود). در این صورت، مقدار بازگشتی تعداد ثانیههای باقیمانده تا زمانی است که هر هشدار از پیش تنظیمشده قرار بود تحویل داده شود. اگر time صفر باشد، هشداری زمانبندی نمیشود و هر هشدار زمانبندیشده لغو میشود. اگر مقدار بازگشتی صفر باشد، در حال حاضر هشداری زمانبندی نشده است.دسترسپذیری: Unix.
برای اطلاعات بیشتر، صفحهی man alarm(2) را ببینید.
- signal.getsignal(signalnum)¶
هندلر سیگنال فعلی برای سیگنال signalnum را برمیگرداند. مقدار برگرداندهشده ممکن است یک شیء پایتونی فراخوانیپذیر باشد، یا یکی از مقادیر خاص
signal.SIG_IGN،signal.SIG_DFLیاNone. در اینجا،signal.SIG_IGNبه این معناست که سیگنال پیشتر نادیده گرفته شده است،signal.SIG_DFLبه این معناست که روش پیشفرض رسیدگی به سیگنال پیشتر در حال استفاده بوده است، وNoneبه این معناست که مدیر سیگنال پیشین از پایتون نصب نشده است.
- signal.strsignal(signalnum)¶
توضیح سیگنال signalnum را بازمیگرداند، مانند «Interrupt» برای
SIGINT. اگر signalnum توضیحی نداشته باشد،Noneرا بازمیگرداند. اگر signalnum نامعتبر باشد،ValueErrorرا پرتاب میکند.اضافه شده در نسخهی 3.8.
- signal.valid_signals()¶
مجموعهای از شمارههای سیگنال معتبر در این پلتفرم را برمیگرداند. اگر برخی سیگنالها برای استفادهی داخلی توسط سیستم رزرو شده باشند، این مجموعه میتواند کمتر از
range(1, NSIG)باشد.اضافه شده در نسخهی 3.8.
- signal.pause()¶
باعث میشود فرآیند تا دریافت یک سیگنال متوقف شود؛ سپس هندلر مناسب فراخوانی خواهد شد. چیزی برنمیگرداند.
دسترسپذیری: Unix.
برای اطلاعات بیشتر، صفحه راهنمای signal(2) را ببینید.
همچنین
sigwait()،sigwaitinfo()،sigtimedwait()وsigpending()را ببینید.
- signal.raise_signal(signum)¶
یک سیگنال به فرایند فراخوان ارسال میکند. چیزی برنمیگرداند.
اضافه شده در نسخهی 3.8.
- signal.pidfd_send_signal(pidfd, sig, siginfo=None, flags=0)¶
سیگنال sig را به فرایندی که با توصیفگر پرونده pidfd به آن اشاره شده است ارسال کنید. پایتون در حال حاضر از پارامتر siginfo پشتیبانی نمیکند؛ این پارامتر باید
Noneباشد. آرگومان flags برای توسعههای آینده در نظر گرفته شده است؛ در حال حاضر هیچ مقدار پرچمی تعریف نشده است.برای اطلاعات بیشتر، به صفحه راهنمای pidfd_send_signal(2) مراجعه کنید.
دسترسپذیری: Linux >= 5.1, Android >=
build-timeAPI level 31اضافه شده در نسخهی 3.9.
- signal.pthread_kill(thread_id, signalnum)¶
سیگنال signalnum را به نخ thread_id، نخ دیگری در همان فرایند فراخواننده، ارسال کنید. نخ هدف میتواند در حال اجرای هر کدی (پایتون یا غیرپایتون) باشد. با این حال، اگر نخ هدف در حال اجرای مفسر پایتون باشد، هندلرهای سیگنال پایتون توسط نخ اصلی مفسر اصلی اجرا خواهند شد. بنابراین، تنها هدف از ارسال سیگنال به یک نخ پایتونی مشخص این است که یک فراخوانی سیستمی در حال اجرا را وادار کند تا با
InterruptedErrorشکست بخورد.برای دریافت یک مقدار مناسب برای thread_id، از
threading.get_ident()یا ویژگیidentاشیایthreading.Threadاستفاده کنید.اگر signalnum برابر ۰ باشد، هیچ سیگنالی ارسال نمیشود، اما بررسی خطا همچنان انجام میشود؛ از این میتوان برای بررسی اینکه نخ هدف هنوز در حال اجرا است یا خیر استفاده کرد.
یک رویداد حسابرسی
signal.pthread_killرا با آرگومانهایthread_idوsignalnumپرتاب میکند.دسترسپذیری: Unix.
برای اطلاعات بیشتر، صفحه man مربوط به pthread_kill(3) را ببینید.
همچنین ببینید
os.kill().اضافه شده در نسخهی 3.3.
- signal.pthread_sigmask(how, mask)¶
نقاب سیگنال نخ فراخوان را واکشی و/یا تغییر میدهد. نقاب سیگنال مجموعهای از سیگنالها است که تحویل آنها در حال حاضر برای فراخوان مسدود است. نقاب سیگنال قبلی را بهعنوان مجموعهای از سیگنالها برمیگرداند.
رفتار فراخوانی، بسته به مقدار how، به شرح زیر است.
SIG_BLOCK: مجموعه سیگنالهای مسدودشده، اجتماع مجموعه فعلی و آرگومان mask است.SIG_UNBLOCK: سیگنالهای موجود در mask از مجموعهی فعلی سیگنالهای مسدودشده حذف میشوند. تلاش برای رفع انسداد سیگنالی که مسدود نیست، مجاز است.SIG_SETMASK: مجموعه سیگنالهای مسدودشده به آرگومان mask تنظیم میشود.
mask مجموعهای از شمارههای سیگنال است (مانند {
signal.SIGINT,signal.SIGTERM}). برای یک نقاب کامل شامل تمام سیگنالها، ازvalid_signals()استفاده کنید.برای مثال،
signal.pthread_sigmask(signal.SIG_BLOCK, [])نقاب سیگنال نخ فراخواننده را میخواند.SIGKILLوSIGSTOPنمیتوانند مسدود شوند.دسترسپذیری: Unix.
برای اطلاعات بیشتر، صفحههای راهنمای sigprocmask(2) و pthread_sigmask(3) را ببینید.
همچنین
pause()،sigpending()وsigwait()را ببینید.اضافه شده در نسخهی 3.3.
- signal.setitimer(which, seconds, interval=0.0)¶
تایمر بینابینی مشخصشده توسط which (یکی از
signal.ITIMER_REAL،signal.ITIMER_VIRTUALیاsignal.ITIMER_PROF) را تنظیم میکند تا پس از seconds ثانیه فعال شود (float پذیرفته میشود، برخلافalarm()) و پس از آن هر interval ثانیه فعال شود (اگر interval غیرصفر باشد). تایمر بینابینی مشخصشده توسط which را میتوان با تنظیم seconds روی صفر پاک کرد.وقتی یک زمانسنج بازهای فعال میشود، یک سیگنال به فرایند ارسال میشود. سیگنال ارسالی به زمانسنج مورد استفاده بستگی دارد؛
signal.ITIMER_REALسیگنالSIGALRMرا ارسال میکند،signal.ITIMER_VIRTUALسیگنالSIGVTALRMرا ارسال میکند، وsignal.ITIMER_PROFسیگنالSIGPROFرا ارسال میکند.مقادیر قدیمی بهصورت یک تاپل بازگردانده میشوند: (delay, interval).
تلاش برای ارسال یک زمانسنج بازهای (interval timer) نامعتبر باعث بروز
ItimerErrorمیشود.دسترسپذیری: Unix.
- signal.getitimer(which)¶
مقدار فعلی زمانسنج بازهای مشخصشده توسط which را برمیگرداند.
دسترسپذیری: Unix.
- signal.set_wakeup_fd(fd, *, warn_on_full_buffer=True)¶
توصیفگر پرونده بیدارباش را روی fd تنظیم کنید. هرگاه سیگنالی دریافت شود که برنامهی شما برای آن هندلر سیگنال ثبت کرده است، شماره سیگنال بهصورت یک بایت در آن توصیفگر پرونده نوشته میشود. اگر برای سیگنالهای موردنظرتان مدیر سیگنال ثبت نکرده باشید، هیچ چیزی در توصیفگر پرونده بیدارباش نوشته نخواهد شد. یک کتابخانه میتواند از این قابلیت برای بیدار کردن یک فراخوانی poll یا select استفاده کند تا سیگنال بهطور کامل پردازش شود.
توصیفگر پرونده بیدارباش قبلی بازگردانده میشود (یا -1 اگر بیدارباش توصیفگر پرونده فعال نشده باشد). اگر fd برابر -1 باشد، بیدارباش توصیفگر پرونده غیرفعال میشود. اگر -1 نباشد، fd باید غیرمسدود باشد. بر عهده کتابخانه است که پیش از فراخوانی دوباره poll یا select، هر بایتی را از fd حذف کند.
هنگامی که نخها فعال باشند، این تابع فقط میتواند از نخ اصلی مفسر اصلی فراخوانی شود؛ تلاش برای فراخوانی آن از سایر نخها باعث پرتاب استثنای
ValueErrorمیشود.دو روش رایج برای استفاده از این تابع وجود دارد. در هر دو روش، از fd برای بیدار شدن هنگام رسیدن یک سیگنال استفاده میکنید، اما این دو در نحوهی تعیین اینکه کدام سیگنال یا سیگنالها رسیدهاند، تفاوت دارند.
در رویکرد اول، دادهها را از بافر fd میخوانیم، و مقادیر بایت، شمارههای سیگنال را به شما میدهند. این روش ساده است، اما در موارد نادر ممکن است با مشکلی مواجه شود: معمولاً fd مقدار محدودی فضای بافر دارد، و اگر سیگنالهای زیادی خیلی سریع برسند، ممکن است بافر پر شود و برخی سیگنالها از دست بروند. اگر از این رویکرد استفاده میکنید، باید
warn_on_full_buffer=Trueرا تنظیم کنید، که حداقل باعث میشود هنگام از دست رفتن سیگنالها، هشداری در stderr چاپ شود.در رویکرد دوم، از fd بیدارباش (wakeup fd) فقط برای بیدارباشها استفاده میکنیم و مقادیر واقعی بایتها را نادیده میگیریم. در این حالت، تنها چیزی که اهمیت دارد این است که بافر fd خالی است یا غیرخالی؛ پر بودن بافر بههیچوجه نشاندهندهی مشکل نیست. اگر از این رویکرد استفاده میکنید، باید
warn_on_full_buffer=Falseرا تنظیم کنید تا کاربران شما با پیامهای هشدار کاذب دچار سردرگمی نشوند.تغییر یافته در نسخهی 3.5: در ویندوز، این تابع اکنون از دستههای سوکت نیز پشتیبانی میکند.
تغییر یافته در نسخهی 3.7: پارامتر
warn_on_full_bufferافزوده شد.
- signal.siginterrupt(signalnum, flag)¶
رفتار راهاندازی مجدد فراخوانی سیستم را تغییر میدهد: اگر flag برابر
Falseباشد، فراخوانیهای سیستم هنگامی که با سیگنال signalnum قطع شوند دوباره راهاندازی میشوند، در غیر این صورت فراخوانیهای سیستم قطع میشوند. چیزی برنمیگرداند.دسترسپذیری: Unix.
برای اطلاعات بیشتر، صفحه راهنمای siginterrupt(3) را ببینید.
توجه داشته باشید که نصب یک هندلر سیگنال با
signal()، رفتار شروع مجدد را از طریق فراخوانی ضمنیsiginterrupt()با مقدار درست برای flag مربوط به سیگنال مورد نظر، به حالت قابلقطع بازنشانی میکند.
- signal.signal(signalnum, handler)¶
هندلر سیگنال signalnum را روی تابع handler تنظیم کنید. handler میتواند یک شیء پایتونی فراخوانیپذیر باشد که دو آرگومان میپذیرد (در زیر ببینید)، یا یکی از مقادیر ویژه
signal.SIG_IGNیاsignal.SIG_DFL. هندلر سیگنال پیشین برگردانده خواهد شد (به توضیحgetsignal()در بالا مراجعه کنید). (برای اطلاعات بیشتر، صفحهی man یونیکس signal(2) را ببینید.)هنگامی که نخها فعال باشند، این تابع فقط میتواند از نخ اصلی مفسر اصلی فراخوانی شود؛ تلاش برای فراخوانی آن از سایر نخها باعث پرتاب استثنای
ValueErrorمیشود.هندلر با دو آرگومان فراخوانی میشود: شمارهی سیگنال و فریم پشتهی جاری (
Noneیا یک شیء فریم؛ برای توضیح اشیای فریم، توضیح در سلسلهمراتب نوع یا توضیحات ویژگیها در ماژولinspectرا ببینید).در ویندوز،
signal()را فقط میتوان باSIGABRT،SIGFPE،SIGILL،SIGINT،SIGSEGV،SIGTERMیاSIGBREAKفراخوانی کرد. در هر حالت دیگر، یکValueErrorپرتاب خواهد شد. توجه داشته باشید که همهی سیستمها مجموعهی یکسانی از نامهای سیگنال را تعریف نمیکنند؛ اگر یک نام سیگنال بهعنوان ثابت سطح ماژولSIG*تعریف نشده باشد، یکAttributeErrorپرتاب خواهد شد.
- signal.sigpending()¶
مجموعه سیگنالهایی را که در انتظار تحویل به نخ فراخوان هستند (یعنی سیگنالهایی که در حین مسدود بودن پرتاب شدهاند) بررسی کنید. مجموعه سیگنالهای در انتظار را برگردانید.
دسترسپذیری: Unix.
برای اطلاعات بیشتر، صفحهی man sigpending(2) را ببینید.
همچنین ببینید
pause()،pthread_sigmask()وsigwait().اضافه شده در نسخهی 3.3.
- signal.sigwait(sigset)¶
اجرای نخ فراخوان را تا تحویل یکی از سیگنالهای مشخصشده در مجموعه سیگنال sigset معلق میکند. این تابع سیگنال را میپذیرد (آن را از فهرست سیگنالهای در انتظار حذف میکند) و شماره سیگنال را برمیگرداند.
دسترسپذیری: Unix.
برای اطلاعات بیشتر، صفحهی man sigwait(3) را ببینید.
همچنین ببینید
pause()،pthread_sigmask()،sigpending()،sigwaitinfo()وsigtimedwait().اضافه شده در نسخهی 3.3.
- signal.sigwaitinfo(sigset)¶
اجرای نخ فراخوان را تا تحویل یکی از سیگنالهای مشخصشده در مجموعه سیگنال sigset معلق میکند. تابع سیگنال را میپذیرد و آن را از فهرست سیگنالهای در انتظار حذف میکند. اگر یکی از سیگنالهای درون sigset از قبل برای نخ فراخوان در انتظار باشد، تابع بلافاصله همراه با اطلاعاتی درباره آن سیگنال بازگشت میکند. هندلر سیگنال برای سیگنال تحویلشده فراخوانی نمیشود. اگر تابع توسط سیگنالی که در sigset نیست قطع شود، یک
InterruptedErrorرا پرتاب میکند.مقدار بازگشتی یک شیء است که دادههای موجود در ساختار
siginfo_tرا نشان میدهد، یعنی:si_signo،si_code،si_errno،si_pid،si_uid،si_status،si_band.دسترسپذیری: Unix.
برای اطلاعات بیشتر، صفحهی man sigwaitinfo(2) را ببینید.
همچنین ببینید
pause()،sigwait()وsigtimedwait().اضافه شده در نسخهی 3.3.
تغییر یافته در نسخهی 3.5: این تابع اکنون در صورتی که توسط سیگنالی که در sigset نیست قطع شود و هندلر سیگنال استثنایی پرتاب نکند، دوباره اجرا میشود (برای دلیل این موضوع PEP 475 را ببینید).
- signal.sigtimedwait(sigset, timeout)¶
مانند
sigwaitinfo()، اما یک آرگومان timeout اضافی برای تعیین مهلت زمانی میگیرد. اگر timeout بهصورت0تعیین شود، یک پایش (poll) انجام میشود. در صورت رخ دادن مهلت زمانی،Noneرا برمیگرداند.دسترسپذیری: Unix.
برای اطلاعات بیشتر، صفحهی man sigtimedwait(2) را ببینید.
همچنین ببینید
pause()،sigwait()وsigwaitinfo().اضافه شده در نسخهی 3.3.
تغییر یافته در نسخهی 3.5: این تابع اکنون در صورتی که با سیگنالی خارج از sigset قطع شود و هندلر سیگنال استثنایی پرتاب نکند، با timeout دوباره محاسبهشده مجدداً اجرا میشود (برای دلیل، PEP 475 را ببینید).
مثالها¶
در اینجا یک برنامه نمونه حداقلی آمده است. این برنامه از تابع alarm() برای محدود کردن زمان انتظار برای باز کردن یک پرونده استفاده میکند؛ این کار زمانی مفید است که پرونده مربوط به یک دستگاه سریال باشد که ممکن است روشن نباشد، وضعیتی که در حالت عادی باعث میشود os.open() بهطور نامحدود معلق بماند. راهحل، تنظیم یک زنگ ۵ ثانیهای پیش از باز کردن پرونده است؛ اگر عملیات بیش از حد طول بکشد، سیگنال زنگ ارسال خواهد شد و هندلر استثنایی را پرتاب میکند.
import signal, os
def handler(signum, frame):
signame = signal.Signals(signum).name
print(f'Signal handler called with signal {signame} ({signum})')
raise OSError("Couldn't open device!")
# Set the signal handler and a 5-second alarm
signal.signal(signal.SIGALRM, handler)
signal.alarm(5)
# This open() may hang indefinitely
fd = os.open('/dev/ttyS0', os.O_RDWR)
signal.alarm(0) # Disable the alarm
یادداشتی درباره SIGPIPE¶
هدایت خروجی برنامهتان به ابزارهایی مانند head(1) باعث میشود هنگامی که دریابندهی خروجی استاندارد آن زودتر بسته شود، یک سیگنال SIGPIPE به فرایند شما ارسال شود. این امر به استثنایی مانند BrokenPipeError: [Errno 32] Broken pipe منجر میشود. برای مدیریت این حالت، نقطه ورود خود را بهشکل زیر بپوشانید تا این استثنا را بگیرد:
import os
import sys
def main():
try:
# simulate large output (your code replaces this loop)
for x in range(10000):
print("y")
# flush output here to force SIGPIPE to be triggered
# while inside this try block.
sys.stdout.flush()
except BrokenPipeError:
# Python flushes standard streams on exit; redirect remaining output
# to devnull to avoid another BrokenPipeError at shutdown
devnull = os.open(os.devnull, os.O_WRONLY)
os.dup2(devnull, sys.stdout.fileno())
sys.exit(1) # Python exits with error code 1 on EPIPE
if __name__ == '__main__':
main()
برای اجتناب از BrokenPipeError، رفتار SIGPIPE را روی SIG_DFL تنظیم نکنید. این کار باعث میشود برنامه شما هرگاه اتصال سوکتی در حالی که برنامه هنوز در حال نوشتن روی آن است قطع شود، بهطور غیرمنتظرهای خارج شود.
یادداشتی دربارهی هندلرهای سیگنال و استثناها¶
اگر یک هندلر سیگنال استثنایی را پرتاب کند، آن استثنا به نخ اصلی منتقل میشود و ممکن است پس از هر دستور bytecode پرتاب شود. بهویژه، ممکن است KeyboardInterrupt در هر نقطهای از اجرا رخ دهد. نمیتوان بیشتر کدهای پایتون، از جمله کتابخانه استاندارد، را در برابر این حالت مقاوم کرد، و بنابراین یک KeyboardInterrupt (یا هر استثنای دیگری که از یک مدیر سیگنال ناشی میشود) ممکن است در موارد نادر برنامه را در وضعیت غیرمنتظرهای قرار دهد.
برای روشنکردن این مسئله، کد زیر را در نظر بگیرید:
class SpamContext:
def __init__(self):
self.lock = threading.Lock()
def __enter__(self):
# If KeyboardInterrupt occurs here, everything is fine
self.lock.acquire()
# If KeyboardInterrupt occurs here, __exit__ will not be called
...
# KeyboardInterrupt could occur just before the function returns
def __exit__(self, exc_type, exc_val, exc_tb):
...
self.lock.release()
برای بسیاری از برنامهها، بهویژه آنهایی که صرفاً میخواهند در صورت بروز KeyboardInterrupt خارج شوند، این یک مشکل نیست، اما برنامههای کاربردی پیچیده یا نیازمند قابلیت اطمینان بالا باید از پرتاب استثنا از درون هندلرهای سیگنال خودداری کنند. آنها همچنین باید از گرفتن KeyboardInterrupt بهعنوان وسیلهای برای خاموش شدن ایمن خودداری کنند. در عوض، باید هندلر SIGINT اختصاصی خود را نصب کنند. در زیر مثالی از یک سرور HTTP آمده است که از KeyboardInterrupt اجتناب میکند:
import signal
import socket
from selectors import DefaultSelector, EVENT_READ
from http.server import HTTPServer, SimpleHTTPRequestHandler
interrupt_read, interrupt_write = socket.socketpair()
def handler(signum, frame):
print('Signal handler called with signal', signum)
interrupt_write.send(b'\0')
signal.signal(signal.SIGINT, handler)
def serve_forever(httpd):
sel = DefaultSelector()
sel.register(interrupt_read, EVENT_READ)
sel.register(httpd, EVENT_READ)
while True:
for key, _ in sel.select():
if key.fileobj == interrupt_read:
interrupt_read.recv(1)
return
if key.fileobj == httpd:
httpd.handle_request()
print("Serving on port 8000")
httpd = HTTPServer(('', 8000), SimpleHTTPRequestHandler)
serve_forever(httpd)
print("Shutdown...")