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.SIGABRT

سیگنال خاتمه (Abort) از abort(3).

signal.SIGALRM

سیگنال زمان‌سنج از alarm(2).

signal.SIGBREAK

وقفه از صفحه‌کلید (CTRL + BREAK).

signal.SIGBUS

خطای گذرگاه (دسترسی نامعتبر به حافظه).

signal.SIGCHLD

فرایند فرزند متوقف شد یا خاتمه یافت.

signal.SIGCLD

نام مستعار برای SIGCHLD.

signal.SIGCONT

اگر فرآیند در حال حاضر متوقف است، آن را ادامه دهید

signal.SIGFPE

استثنای ممیز شناور. برای مثال، تقسیم بر صفر.

همچنین ملاحظه نمائید

ZeroDivisionError زمانی پرتاب می‌شود که دومین آرگومان یک عملیات تقسیم یا باقی‌مانده صفر باشد.

signal.SIGHUP

قطع ارتباط (Hangup) در پایانه کنترل‌کننده یا مرگ فرایند کنترل‌کننده شناسایی شد.

signal.SIGILL

دستورالعمل غیرمجاز.

signal.SIGINT

وقفه از صفحه‌کلید (CTRL + C).

اقدام پیش‌فرض، پرتاب KeyboardInterrupt است.

signal.SIGKILL

سیگنال کشتن (Kill signal).

نمی‌توان آن را گرفت، مسدود کرد یا نادیده گرفت.

signal.SIGPIPE

پایپ‌ی شکسته: نوشتن در پایپ‌ای بدون خواننده.

اقدام پیش‌فرض، نادیده گرفتن سیگنال است.

signal.SIGPROF

زمان‌سنج پروفایل‌سازی منقضی شد.

signal.SIGQUIT

سیگنال خروج پایانه.

signal.SIGSEGV

خطای قطعه‌بندی: ارجاع نامعتبر به حافظه.

signal.SIGSTOP

توقف اجرا (قابل گرفتن یا نادیده‌گرفتن نیست).

signal.SIGSTKFLT

خطای پشته در هم‌پردازنده. هسته‌ی لینوکس این سیگنال را پرتاب نمی‌کند: فقط می‌تواند در فضای کاربر پرتاب شود.

دسترس‌پذیری: Linux.

در معماری‌هایی که سیگنال در دسترس است. برای اطلاعات بیشتر، صفحه‌ی man signal(7) را ببینید.

اضافه شده در نسخه‌ی 3.11.

signal.SIGTERM

سیگنال خاتمه.

signal.SIGUSR1

سیگنال ۱ تعریف‌شده توسط کاربر.

signal.SIGUSR2

سیگنال ۲ تعریف‌شده توسط کاربر.

signal.SIGVTALRM

زمان‌سنج مجازی منقضی شد.

signal.SIGWINCH

سیگنال تغییر اندازه‌ی پنجره.

signal.SIGXCPU

از محدودیت زمان CPU فراتر رفت.

SIG*

همه شماره‌های سیگنال به‌صورت نمادین تعریف شده‌اند. برای مثال، سیگنال قطع ارتباط به‌صورت signal.SIGHUP تعریف شده است؛ نام متغیرها دقیقاً همان نام‌هایی هستند که در برنامه‌های C استفاده می‌شوند، همان‌طور که در <signal.h> آمده است. صفحه راهنمای یونیکس برای «signal» سیگنال‌های موجود را فهرست می‌کند (در برخی سیستم‌ها این signal(2) است، در برخی دیگر فهرست در signal(7) قرار دارد). توجه داشته باشید که همه سیستم‌ها مجموعه یکسانی از نام‌های سیگنال را تعریف نمی‌کنند؛ این ماژول تنها نام‌هایی را تعریف می‌کند که سیستم آن‌ها را تعریف کرده است.

signal.CTRL_C_EVENT

سیگنال متناظر با رویداد فشردن کلید Ctrl+C. این سیگنال را فقط می‌توان با os.kill() استفاده کرد.

اضافه شده در نسخه‌ی 3.2.

signal.CTRL_BREAK_EVENT

سیگنال متناظر با رویداد فشردن کلید Ctrl+Break. این سیگنال فقط می‌تواند با os.kill() استفاده شود.

اضافه شده در نسخه‌ی 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 است.

اضافه شده در نسخه‌ی 3.3: این خطا پیش‌تر زیرنوعی از IOError بود که اکنون نام مستعار 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-time API 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 می‌شود.

signal.getitimer(which)

مقدار فعلی زمان‌سنج بازه‌ای مشخص‌شده توسط which را برمی‌گرداند.

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...")