تتوقف عمليات Python عن الاستجابة لـ SIGTERM / SIGINT بعد إعادة تشغيلها
سؤال
أواجه مشكلة غريبة في تشغيل بعض عمليات بايثون باستخدام عملية المراقبة.
عملية المراقبة مكتوبة بلغة بايثون وهي الأصل ولها وظيفة تسمى start_child(الاسم) الذي يستخدم عملية فرعية.فتح لفتح عملية الطفل.يتم تسجيل كائن Popen حتى تتمكن هيئة المراقبة من مراقبة العملية باستخدامه تصويت() وإنهاء الأمر في النهاية إنهاء () عند الاحتياج.إذا مات الطفل بشكل غير متوقع، تتصل الوكالة الرقابية start_child(الاسم) مرة أخرى ويسجل كائن Popen الجديد.
هناك 7 عمليات فرعية، جميعها أيضًا بيثون.إذا قمت بتشغيل أي من الأطفال يدويًا، فيمكنني إرسال SIGTERM أو SIGINT باستخدام قتل والحصول على النتائج التي أتوقعها (تنتهي العملية).
ومع ذلك، عند الهروب من عملية المراقبة، لن ينتهي الطفل إلا بعد أولاً الإشارة.عندما تقوم جهة المراقبة بإعادة تشغيل الطفل، لم تعد العملية الفرعية الجديدة تستجيب لـ SIGTERM أو SIGINT.ليس لدي أي فكرة عما يسبب هذا.
الوكالة الدولية للطاقة.py
class watchdog:
# <snip> various init stuff
def start(self):
self.running = true
kids = ['app1', 'app2', 'app3', 'app4', 'app5', 'app6', 'app7']
self.processes = {}
for kid in kids:
self.start_child(kid)
self.thread = threading.Thread(target=self._monitor)
self.thread.start()
while self.running:
time.sleep(10)
def start_child(self, name):
try:
proc = subprocess.Popen(name)
self.processes[name] = proc
except:
print "oh no"
else:
print "started child ok"
def _monitor(self):
while self.running:
time.sleep(1)
if self.running:
for kid, proc in self.processes.iteritems():
if proc.poll() is not None: # process ended
self.start_child(kid)
إذن ما يحدث هو الوكالة الدولية للطاقة.بدء() يطلق جميع العمليات السبع، وإذا أرسلت أي عملية SIGTERM، فإنها تنتهي، ويبدأ خيط المراقبة تشغيلها مرة أخرى.ومع ذلك، إذا قمت بعد ذلك بإرسال العملية الجديدة SIGTERM، فإنها تتجاهلها.
يجب أن أكون قادرًا على الاستمرار في إرسال kill -15 إلى العمليات المعاد تشغيلها مرارًا وتكرارًا.لماذا يتجاهلونها بعد إعادة التشغيل؟
المحلول
كما هو موضح هنا : http://blogs.gentoo.org/agaffney/2005/03/18/python_sucks ، عندما تنشئ بايثون سلسلة رسائل جديدة، فإنها تحظر جميع الإشارات الخاصة بهذا السلسلة (ولأي عمليات تنتج سلسلة المحادثات).
لقد أصلحت هذه المشكلة باستخدام sigprocmask، الذي تم استدعاؤه من خلال ctypes.قد تكون هذه هي أو لا تكون الطريقة "الصحيحة" للقيام بذلك، ولكنها ناجحة.
في عملية الطفل، أثناء __init__
:
libc = ctypes.cdll.LoadLibrary("libc.so")
mask = '\x00' * 17 # 16 byte empty mask + null terminator
libc.sigprocmask(3, mask, None) # '3' on FreeBSD is the value for SIG_SETMASK
نصائح أخرى
ألن يكون من الأفضل استعادة معالجات الإشارة الافتراضية داخل بايثون بدلاً من استخدام ctypes؟في عملية طفلك، استخدم وحدة الإشارة:
import signal
for sig in range(1, signal.NSIG):
try:
signal.signal(sig, signal.SIG_DFL)
except RuntimeError:
pass
يظهر خطأ RuntimeError عند محاولة تعيين إشارات مثل SIGKILL والتي لا يمكن التقاطها.