از طریق منوی جستجو مطلب مورد نظر خود در وبلاگ را به سرعت پیدا کنید
مدیریت سیگنال های یونیکس در پایتون
سرفصلهای مطلب
سیستم های یونیکس/لینوکس مکانیسم های خاصی را برای برقراری ارتباط بین هر فرد ارائه می دهند process. یکی از این مکانیسم ها هستند سیگنال ها، و متعلق به روش های مختلف ارتباط بین فرآیندها هستند (Inter Process Communication، به اختصار IPC).
به طور خلاصه، سیگنال ها وقفه های نرم افزاری هستند که به برنامه (یا process) برنامه را از رویدادهای مهم یا درخواست های برنامه به منظور اجرای یک دنباله کد خاص مطلع کند. برنامه ای که سیگنال دریافت می کند یا اجرای دستورات خود را متوقف می کند یا ادامه می دهد، با یا بدون حافظه خاتمه می یابد. dump، یا حتی به سادگی سیگنال را نادیده می گیرد.
اگر چه در تعریف شده است استاندارد POSIX، واکنش در واقع بستگی دارد روی چگونه توسعه دهنده اسکریپت را نوشت و مدیریت سیگنال ها را پیاده سازی کرد.
در این مقاله توضیح می دهیم که سیگنال ها چیست، به شما نشان می دهیم که چگونه سیگنال را به دیگری ارسال کنید process از خط فرمان و همچنین پردازش سیگنال دریافتی. در میان ماژول های دیگر، کد برنامه عمدتا مبتنی است روی را ماژول سیگنال. این ماژول هدرهای C سیستم عامل شما را به دنیای پایتون متصل می کند.
مقدمه ای بر سیگنال ها
در سیستم های مبتنی بر یونیکس، سه دسته سیگنال وجود دارد:
-
سیگنال های سیستم (سخت افزار و خطاهای سیستم): SIGILL، SIGTRAP، SIGBUS، SIGFPE، SIGKILL، SIGSEGV، SIGXCPU، SIGXFSZ، SIGIO
-
سیگنال های دستگاه: SIGHUP، SIGINT، SIGPIPE، SIGALRM، SIGCHLD، SIGCONT، SIGSTOP، SIGTTIN، SIGTTOU، SIGURG، SIGWINCH، SIGIO
-
سیگنال های تعریف شده توسط کاربر: SIGQUIT، SIGABRT، SIGUSR1، SIGUSR2، SIGTERM
هر سیگنال با یک مقدار صحیح نشان داده می شود و لیست سیگنال های موجود نسبتا طولانی است و بین انواع مختلف یونیکس/لینوکس سازگار نیست. در دبیان GNU/سیستم لینوکس، فرمان kill -l
لیست سیگنال ها را به صورت زیر نمایش می دهد:
$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
سیگنال های 1 تا 15 تقریباً استاندارد شده اند و معنای زیر را دارند روی اکثر سیستم های لینوکس:
- 1 (SIGHUP): یک اتصال را خاتمه دهید یا پیکربندی را برای دیمون ها بارگذاری مجدد کنید
- 2 (SIGINT): جلسه را از ایستگاه گفتگو قطع کنید
- 3 (SIGQUIT): جلسه را از ایستگاه گفتگو خاتمه دهید
- 4 (SIGILL): دستور غیرقانونی اجرا شد
- 5 (SIGTRAP): انجام یک دستور (تله)
- 6 (SIGABRT): خاتمه غیر طبیعی
- 7 (SIGBUS): خطا روی گذرگاه سیستم
- 8 (SIGFPE): خطای ممیز شناور
- 9 (SIGKILL): فوراً خاتمه دهید process
- 10 (SIGUSR1): سیگنال تعریف شده توسط کاربر
- 11 (SIGSEGV): خطای بخش بندی به دلیل دسترسی غیرقانونی به بخش حافظه
- 12 (SIGUSR2): سیگنال تعریف شده توسط کاربر
- 13 (SIGPIPE): نوشتن در لوله، و هیچ کس از آن نمی خواند
- 14 (SIGALRM): تایمر خاتمه یافت (زنگ هشدار)
- 15 (SIGTERM): پایان دادن به process به روشی نرم
به منظور ارسال سیگنال به a process در یک لینوکس terminal شما را فرا می خوانید kill
دستور با شماره سیگنال (یا نام سیگنال) از لیست بالا و شناسه آن process (پید). دستور مثال زیر سیگنال 15 (SIGTERM) را به process که دارای pid 12345 است:
$ kill -15 12345
یک راه معادل استفاده از نام سیگنال به جای شماره آن است:
$ kill -SIGTERM 12345
اینکه کدام راه را انتخاب کنید بستگی دارد روی چه چیزی برای شما راحت تر است هر دو راه تأثیر یکسانی دارند. در نتیجه process سیگنال SIGTERM را دریافت می کند و بلافاصله خاتمه می یابد.
استفاده از کتابخانه سیگنال پایتون
از پایتون 1.4، signal
کتابخانه جزء منظم هر نسخه پایتون است. به منظور استفاده از signal
کتابخانه، import کتابخانه را در برنامه پایتون خود به صورت زیر، ابتدا:
import signal
گرفتن و عکس العمل مناسب روی سیگنال دریافتی توسط یک تابع تماس – به اصطلاح کنترل کننده سیگنال – انجام می شود. یک کنترل کننده سیگنال نسبتاً ساده به نام receiveSignal()
را می توان به صورت زیر نوشت:
def receiveSignal(signalNumber, frame):
print('Received:', signalNumber)
return
این کنترل کننده سیگنال هیچ کار دیگری جز گزارش شماره سیگنال دریافتی انجام نمی دهد. مرحله بعدی ثبت سیگنال هایی است که توسط کنترل کننده سیگنال گرفته می شود. برای برنامه های پایتون، همه سیگنال ها (به جز 9، SIGKILL) را می توان در اسکریپت شما گرفت:
if __name__ == '__main__':
signal.signal(signal.SIGHUP, receiveSignal)
signal.signal(signal.SIGINT, receiveSignal)
signal.signal(signal.SIGQUIT, receiveSignal)
signal.signal(signal.SIGILL, receiveSignal)
signal.signal(signal.SIGTRAP, receiveSignal)
signal.signal(signal.SIGABRT, receiveSignal)
signal.signal(signal.SIGBUS, receiveSignal)
signal.signal(signal.SIGFPE, receiveSignal)
signal.signal(signal.SIGUSR1, receiveSignal)
signal.signal(signal.SIGSEGV, receiveSignal)
signal.signal(signal.SIGUSR2, receiveSignal)
signal.signal(signal.SIGPIPE, receiveSignal)
signal.signal(signal.SIGALRM, receiveSignal)
signal.signal(signal.SIGTERM, receiveSignal)
بعد، ما اضافه می کنیم process اطلاعات برای جریان process، و شناسایی کنید process شناسه با استفاده از متد getpid()
از os
مدول. در یک بی پایان while
حلقه ما منتظر سیگنال های دریافتی هستیم. ما این را با استفاده از دو ماژول پایتون دیگر پیاده سازی می کنیم – سیستم عامل و زمان. ما import آنها در ابتدای اسکریپت پایتون ما نیز هستند:
import os
import time
در while
حلقه برنامه اصلی ما print خروجی عبارت “در انتظار…”. را time.sleep()
فراخوانی تابع باعث می شود برنامه سه ثانیه منتظر بماند.
print('My PID is:', os.getpid())
while True:
print('Waiting...')
time.sleep(3)
در نهایت، ما باید اسکریپت خود را آزمایش کنیم. پس از ذخیره اسکریپت به عنوان signal-handling.py
می توانیم آن را در الف فراخوانی کنیم terminal به شرح زیر است:
$ python3 signal-handling.py
My PID is: 5746
Waiting...
...
در یک ثانیه terminal پنجره ما یک سیگنال به process. ما اولین خود را شناسایی می کنیم process – اسکریپت پایتون – توسط process شناسه به صورت چاپ شده روی صفحه نمایش، بالا
$ kill -1 5746
کنترل کننده رویداد سیگنال در برنامه پایتون ما سیگنالی را که ما به آن ارسال کرده ایم دریافت می کند process. بر این اساس واکنش نشان می دهد و به سادگی سیگنال دریافتی را تأیید می کند:
...
Received: 1
...
نادیده گرفتن سیگنال ها
ماژول سیگنال راه هایی را برای نادیده گرفتن سیگنال های دریافتی تعریف می کند. برای انجام این کار، سیگنال باید به تابع از پیش تعریف شده متصل شود signal.SIG_IGN
. مثال زیر این را نشان می دهد و در نتیجه برنامه پایتون نمی تواند توسط آن قطع شود CTRL+C
دیگر برای متوقف کردن اسکریپت پایتون یک راه جایگزین در اسکریپت مثال پیاده سازی شده است – سیگنال SIGUSR1 اسکریپت پایتون را خاتمه می دهد. علاوه بر این، به جای یک حلقه بی پایان از روش استفاده می کنیم signal.pause()
. فقط منتظر می ماند تا سیگنالی دریافت شود.
import signal
import os
import time
def receiveSignal(signalNumber, frame):
print('Received:', signalNumber)
raise SystemExit('Exiting')
return
if __name__ == '__main__':
signal.signal(signal.SIGUSR1, receiveSignal)
signal.signal(signal.SIGINT, signal.SIG_IGN)
print('My PID is:', os.getpid())
signal.pause()
کنترل صحیح سیگنال ها
کنترل کننده سیگنالی که ما تاکنون استفاده کرده ایم بسیار ساده است و فقط سیگنال دریافتی را گزارش می دهد. این به ما نشان می دهد که رابط اسکریپت پایتون ما به خوبی کار می کند. بهترش کنیم
گرفتن سیگنال در حال حاضر مبنای خوبی است، اما برای انطباق با قوانین استاندارد POSIX به بهبودی نیاز دارد. برای دقت بالاتر، هر سیگنال نیاز به واکنش مناسب دارد (لیست بالا را ببینید). این بدان معناست که کنترل کننده سیگنال در اسکریپت پایتون ما باید توسط یک روال خاص در هر سیگنال گسترش یابد. اگر بفهمیم یک سیگنال چه کاری انجام می دهد و واکنش رایج چیست، این بهترین کار می کند. آ process که سیگنال 1، 2، 9 یا 15 را دریافت می کند، خاتمه می یابد. در هر حالت دیگری انتظار می رود که یک هسته بنویسد dump، هم.
تا به حال ما یک روال واحد را پیاده سازی کرده ایم که تمام سیگنال ها را پوشش می دهد و آنها را به همان روش مدیریت می کند. گام بعدی اجرای یک روال فردی در هر سیگنال است. کد مثال زیر این را برای سیگنال های 1 (SIGHUP) و 15 (SIGTERM) نشان می دهد.
def readConfiguration(signalNumber, frame):
print ('(SIGHUP) reading configuration')
return
def terminateProcess(signalNumber, frame):
print ('(SIGTERM) terminating the process')
sys.exit()
دو تابع فوق به صورت زیر با سیگنال ها مرتبط می شوند:
signal.signal(signal.SIGHUP, readConfiguration)
signal.signal(signal.SIGTERM, terminateProcess)
اجرای اسکریپت پایتون و ارسال سیگنال 1 (SIGHUP) و به دنبال آن سیگنال 15 (SIGTERM) توسط دستورات یونیکس kill -1 16640
و kill -15 16640
خروجی زیر حاصل می شود:
$ python3 daemon.py
My PID is: 16640
Waiting...
Waiting...
(SIGHUP) reading configuration
Waiting...
Waiting...
(SIGTERM) terminating the process
اسکریپت سیگنال ها را دریافت می کند و آنها را به درستی مدیریت می کند. برای وضوح، این کل اسکریپت است:
import signal
import os
import time
import sys
def readConfiguration(signalNumber, frame):
print ('(SIGHUP) reading configuration')
return
def terminateProcess(signalNumber, frame):
print ('(SIGTERM) terminating the process')
sys.exit()
def receiveSignal(signalNumber, frame):
print('Received:', signalNumber)
return
if __name__ == '__main__':
signal.signal(signal.SIGHUP, readConfiguration)
signal.signal(signal.SIGINT, receiveSignal)
signal.signal(signal.SIGQUIT, receiveSignal)
signal.signal(signal.SIGILL, receiveSignal)
signal.signal(signal.SIGTRAP, receiveSignal)
signal.signal(signal.SIGABRT, receiveSignal)
signal.signal(signal.SIGBUS, receiveSignal)
signal.signal(signal.SIGFPE, receiveSignal)
signal.signal(signal.SIGUSR1, receiveSignal)
signal.signal(signal.SIGSEGV, receiveSignal)
signal.signal(signal.SIGUSR2, receiveSignal)
signal.signal(signal.SIGPIPE, receiveSignal)
signal.signal(signal.SIGALRM, receiveSignal)
signal.signal(signal.SIGTERM, terminateProcess)
print('My PID is:', os.getpid())
while True:
print('Waiting...')
time.sleep(3)
بیشتر خواندن
با استفاده از signal
ماژول و یک کنترل کننده رویداد مطابق با آن، گرفتن سیگنال ها نسبتا آسان است. دانستن معنای سیگنالهای مختلف و واکنش مناسب همانطور که در استاندارد POSIX تعریف شده است قدم بعدی است. این مستلزم آن است که کنترل کننده رویداد بین سیگنال های مختلف تمایز قائل شود و یک روال جداگانه برای همه آنها داشته باشد.
(برچسبها به ترجمه)# python
منتشر شده در 1403-01-26 00:08:05