وبلاگ رسانگار
با ما حرفه ای باشید

سرور مجازی NVMe

مدیریت سیگنال های یونیکس در پایتون

0 19
زمان لازم برای مطالعه: 6 دقیقه


سیستم های یونیکس/لینوکس مکانیسم های خاصی را برای برقراری ارتباط بین هر فرد ارائه می دهند 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

امتیاز شما به این مطلب
دیدگاه شما در خصوص مطلب چیست ؟

آدرس ایمیل شما منتشر نخواهد شد.

لطفا دیدگاه خود را با احترام به دیدگاه های دیگران و با توجه به محتوای مطلب درج کنید