title: مدیریت سیگنالها در پایتون (Python Signal Handling) slug: python-signal description: آموزش جامع ماژول signal برای مدیریت وقایع سیستمعامل، مدیریت وقفهها (Interrupts)، ایجاد Timeout و کار با Threadها. date: 1404/10/18 order: 121
مدیریت سیگنالها (Signal Handling) یکی از مباحث کلیدی در تعامل برنامه با سیستمعامل است. سیگنالها وقفههایی نرمافزاری هستند که به یک پروسه ارسال میشوند تا رخداد خاصی را اطلاع دهند (مانند فشردن Ctrl+C توسط کاربر یا درخواست بسته شدن برنامه). در این مقاله یاد میگیریم چگونه با ماژول signal در پایتون، کنترل کاملی بر این وقایع داشته باشیم.
سطح مقدماتی (Beginner Level)
در این سطح با مفاهیم اولیه سیگنالها، نحوه تعریف هندلر (Handler) و مدیریت بستن برنامه آشنا میشویم.
سیگنال چیست و ماژول signal
سیگنال یک مکانیسم ارتباطی در سطح سیستمعامل یونیکس (و با محدودیتهایی در ویندوز) است. وقتی شما Ctrl+C را میزنید، سیستمعامل سیگنال SIGINT را به برنامه میفرستد. اگر برنامه برای این سیگنال "هندلر" نداشته باشد، به طور پیشفرض بسته میشود.
ماژول signal به ما اجازه میدهد تابعی تعریف کنیم که هنگام دریافت سیگنال اجرا شود.
ساختار کلی (Static)
برای تعریف یک هندلر، از تابع signal.signal استفاده میکنیم که دو آرگومان میگیرد: شماره سیگنال و تابعی که باید اجرا شود.
# Static code (Syntax explanation)
import signal
def my_handler(signum, frame):
print("سیگنال دریافت شد!")
# اتصال سیگنال به تابع هندلر
# signal.signal(signal.SIGINT, my_handler)
مدیریت SIGINT (فشردن Ctrl+C)
رایجترین استفاده از سیگنالها، مدیریت خروج تمیز (Graceful Shutdown) هنگام فشردن کلیدهای ترکیبی Ctrl+C است.
مثال اول: هندلر ساده
در این مثال یک برنامه مینویسیم که با دریافت سیگنال قطع، به جای کرش کردن، یک پیام خداحافظی چاپ میکند.
مثال دوم: جلوگیری از بسته شدن
شما میتوانید کاری کنید که برنامه با Ctrl+C بسته نشود (هرچند باید مراقب باشید برنامه فریز نشود).
سیگنال SIGTERM (درخواست پایان)
این سیگنال معمولاً توسط دستوراتی مثل kill در ترمینال یا سرویسهای مدیریت پروسه (مثل systemd یا Docker) ارسال میشود تا به برنامه بگویند "لطفاً خودت را ببند".
# Static code (Daemon logic snippet)
import signal
import sys
def cleanup_and_exit(signum, frame):
print("در حال پاکسازی منابع و دیتابیس...")
# عملیات ذخیرهسازی...
sys.exit(0)
# هندل کردن درخواست بسته شدن از طرف سیستمعامل
signal.signal(signal.SIGTERM, cleanup_and_exit)
سطح پیشرفته (Professional Level)
در این بخش به مباحث عمیقتر مانند ایجاد Timeout برای عملیات طولانی، تفاوت رفتار در سیستمعاملها، کار با Threadها و استفاده از SIG_IGN میپردازیم.
ایجاد Timeout با SIGALRM (مخصوص یونیکس/لینوکس)
یکی از کاربردهای قدرتمند سیگنالها، متوقف کردن عملیاتی است که بیش از حد طول کشیده است. با استفاده از signal.alarm() میتوانیم سیستمعامل را تنظیم کنیم تا پس از چند ثانیه سیگنال SIGALRM را بفرستد.
نکته مهم: این قابلیت فقط در سیستمهای مبتنی بر یونیکس (لینوکس و مک) کار میکند و در ویندوز موجود نیست.
مثال اول: محدود کردن زمان اجرا
در این مثال اگر تابعی بیش از ۲ ثانیه طول بکشد، متوقف میشود.
# Static code (Requires Unix environment to run properly)
import signal
import time
# تعریف کلاس خطا برای Timeout
class TimeoutError(Exception):
pass
def timeout_handler(signum, frame):
raise TimeoutError("زمان شما تمام شد!")
signal.signal(signal.SIGALRM, timeout_handler)
try:
# تنظیم آلارم برای ۲ ثانیه
signal.alarm(2)
print("شروع کار سنگین...")
time.sleep(5) # این خط ۵ ثانیه طول میکشد
# غیرفعال کردن آلارم اگر کار زودتر تمام شد
signal.alarm(0)
except TimeoutError as e:
print(f"خطا: {e}")
نادیده گرفتن و رفتار پیشفرض (SIG_IGN و SIG_DFL)
گاهی لازم است یک سیگنال را کاملاً نادیده بگیریم یا آن را به رفتار پیشفرض سیستمعامل برگردانیم.
signal.SIG_IGN: سیگنال را نادیده بگیر (Ignore).signal.SIG_DFL: رفتار پیشفرض (Default) را انجام بده (مثلاً بستن برنامه).
مثال دوم: نادیده گرفتن موقت سیگنال
استفاده از این روش برای محافظت از بخشهای بحرانی کد (Critical Sections) مفید است.
سیگنالها و چندنخی (Multi-threading)
یک قانون طلایی در پایتون وجود دارد: سیگنالها فقط در ترد اصلی (Main Thread) دریافت و هندل میشوند. حتی اگر سیگنال در حین اجرای یک ترد فرعی ارسال شود، مفسر پایتون آن را در ترد اصلی اجرا میکند.
مثال سوم: رفتار سیگنال در Thread
این مثال نشان میدهد که هندلر در ترد اصلی اجرا میشود نه در ترد فرعی.
Context Manager برای مدیریت تمیز سیگنالها
برای جلوگیری از بهم ریختن هندلرهای سراسری (Global)، حرفهایها از Context Manager استفاده میکنند تا هندلر فقط در یک بلوک خاص تغییر کند و سپس به حالت قبل برگردد.
مثال چهارم: پیادهسازی کلاس محافظت شده
این کلاس اطمینان حاصل میکند که کد داخل بلوک with توسط سیگنال قطع نمیشود (به تاخیر انداختن سیگنال).
نکات فنی نهایی
- ویندوز vs لینوکس: بسیاری از سیگنالها مثل
SIGALRM,SIGUSR1,SIGUSR2در ویندوز وجود ندارند. در ویندوز عمدتاً باSIGINT,SIGTERM,SIGBREAKسروکار دارید. - تابع
signal.pause(): این تابع برنامه را متوقف میکند تا زمانی که یک سیگنال دریافت شود. برای برنامههایی که منتظر ایونت هستند بسیار بهینهتر ازwhile True: passاست. - Deadlock: هرگز در داخل تابع هندلر سیگنال، از قفلها (Locks) استفاده نکنید یا منتظر I/O طولانی نباشید، زیرا ممکن است باعث ایجاد بنبست شود.