مشاكل إشارة بايثون:هل يؤخر معالج SIGQUIT التنفيذ إذا تم استلام SIGQUIT أثناء تنفيذ معالج إشارة آخر؟

StackOverflow https://stackoverflow.com/questions/109705

  •  02-07-2019
  •  | 
  •  

سؤال

البرنامج التالي بسيط جداً :يخرج نقطة واحدة كل نصف ثانية.إذا تلقى أ سيجكيت, ، فإنه يشرع في إخراج عشرة سس.إذا تلقى أ SIGTSTP (كنترول-ز), ، يخرج عشرة زس.

إذا تلقى أ SIGTSTP أثناء الطباعة سق، فإنه سيتم طباعة عشرة زبعد الانتهاء من العشرة سس.هذا أمر جيد.

ومع ذلك، إذا تلقى أ سيجكيت أثناء الطباعة زs، فشلت في الطباعة سبعدهم.وبدلاً من ذلك، فإنه يطبعها فقط بعد إنهاء التنفيذ يدويًا عبر KeyboardInterrupt.انا اريد ال سليتم طباعتها مباشرة بعد زس.

يحدث هذا باستخدام Python2.3.

ما الخطأ الذي افعله؟شكرا جزيلا.

#!/usr/bin/python

from signal import *
from time import sleep
from sys import stdout

def write(text):
    stdout.write(text)
    stdout.flush()

def process_quit(signum, frame):
    for i in range(10):
        write("Q")
        sleep(0.5)

def process_tstp(signum, frame):
    for i in range(10):
        write("Z")
        sleep(0.5)

signal(SIGQUIT, process_quit)
signal(SIGTSTP, process_tstp)

while 1:
    write('.')
    sleep(0.5)
هل كانت مفيدة؟

المحلول

مشكلتك الأكبر هي حظر معالجات الإشارة.

عادةً ما لا يُنصح بذلك لأنه قد يؤدي إلى ظروف توقيت غريبة.لكن هذا ليس سبب مشكلتك تمامًا نظرًا لأن حالة التوقيت التي تكون عرضة لها موجودة بسبب اختيارك لمعالجات الإشارة.

على أي حال، إليك كيفية تقليل حالة التوقيت على الأقل من خلال تعيين العلامات في المعالجات الخاصة بك وترك حلقة while الرئيسية للقيام بالعمل الفعلي.يتم توضيح سبب تصرف الكود الخاص بك بشكل غريب بعد الكود.

#!/usr/bin/python

from signal import *
from time import sleep
from sys import stdout

print_Qs = 0
print_Zs = 0

def write(text):
    stdout.write(text)
    stdout.flush()

def process_quit(signum, frame):
     global print_Qs
     print_Qs = 10

def process_tstp(signum, frame):
     global print_Zs
     print_Zs = 10

signal(SIGQUIT, process_quit)
signal(SIGTSTP, process_tstp)

while 1:
    if print_Zs:
        print_Zs -= 1
        c = 'Z'
    elif print_Qs:
        print_Qs -= 1
        c = 'Q'
    else:
        c = '.'
    write(c)
    sleep(0.5)

على أية حال، إليك ما يحدث.

SIGTSTP أكثر خصوصية من SIGQUIT.

يقوم SIGTSTP بإخفاء الإشارات الأخرى من التسليم أثناء تشغيل معالج الإشارة الخاص به.عندما تقوم النواة بتسليم SIGQUIT وترى أن معالج SIGTSTP لا يزال قيد التشغيل، فإنها ببساطة تحفظه لوقت لاحق.بمجرد وصول إشارة أخرى للتسليم، مثل SIGINT عندما تقوم بذلك كنترول+ج (ويعرف أيضًا باسم KeyboardInterrupt)، تتذكر النواة أنها لم تقم أبدًا بتسليم SIGQUIT وتقوم بتسليمها الآن.

ستلاحظ إذا قمت بالتغيير while 1: ل for i in range(60): في الحلقة الرئيسية وقم بإجراء حالة الاختبار الخاصة بك مرة أخرى، سيخرج البرنامج دون تشغيل معالج SIGTSTP نظرًا لأن الخروج لا يعيد تشغيل آلية توصيل إشارة kernel.

حظ سعيد!

نصائح أخرى

في Python 2.5.2 على Linux 2.6.24، تعمل التعليمات البرمجية الخاصة بك تمامًا كما تصف النتائج المرغوبة (إذا تم تلقي إشارة أثناء معالجة الإشارة السابقة، تتم معالجة الإشارة الجديدة فورًا بعد انتهاء الإشارة الأولى).

في Python 2.4.4 وفي Linux 2.6.16، أرى سلوك المشكلة الذي تصفه.

لا أعرف ما إذا كان هذا بسبب تغيير في Python أو في Linux kernel.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top