SIGTERM सिग्नल को इनायत से कैसे प्रोसेस करें?


197

आइए मान लेते हैं कि हमारे पास अजगर में लिखा गया ऐसा तुच्छ दान है:

def mainloop():
    while True:
        # 1. do
        # 2. some
        # 3. important
        # 4. job
        # 5. sleep

mainloop()

और हम इसे start-stop-daemonडिफ़ॉल्ट रूप से SIGTERM( TERM) सिग्नल पर भेजकर उपयोग करते हैं --stop

मान लीजिए कि प्रदर्शन किया गया वर्तमान चरण है #2। और इसी क्षण हम TERMसंकेत भेज रहे हैं ।

क्या होता है कि निष्पादन तुरंत समाप्त हो जाता है।

मैंने पाया है कि मैं सिग्नल इवेंट का उपयोग कर संभाल सकता हूं signal.signal(signal.SIGTERM, handler)लेकिन बात यह है कि यह अभी भी वर्तमान निष्पादन को बाधित करता है और नियंत्रण को पास करता है handler

तो, मेरा सवाल यह है - क्या वर्तमान निष्पादन को बाधित नहीं करना संभव है, लेकिन TERMएक अलग थ्रेड (?) में सिग्नल को संभालना है ताकि मैं सेट कर पाऊं shutdown_flag = Trueताकि mainloop()कृपा से रोकने का मौका मिले?


2
मैंने वह किया जो आप प्रक्रिया signalfdके वितरण के लिए उपयोग और मास्किंग करके पहले पूछ रहे हैं SIGTERM
एरिक अर्बन

जवाबों:


278

एक वर्ग समाधान का उपयोग करने के लिए स्वच्छ आधारित:

import signal
import time

class GracefulKiller:
  kill_now = False
  def __init__(self):
    signal.signal(signal.SIGINT, self.exit_gracefully)
    signal.signal(signal.SIGTERM, self.exit_gracefully)

  def exit_gracefully(self,signum, frame):
    self.kill_now = True

if __name__ == '__main__':
  killer = GracefulKiller()
  while not killer.kill_now:
    time.sleep(1)
    print("doing something in a loop ...")

  print("End of the program. I was killed gracefully :)")

1
विचार के लिए धन्यवाद! मैंने रिबूट-गार्ड में एक संशोधित दृष्टिकोण का उपयोग किया। github.com/ryran/reboot-guard/blob/master/rguard#L284:L304
rsaw

7
यह सबसे अच्छा जवाब है (कोई थ्रेड्स की आवश्यकता नहीं है), और पसंदीदा पहला प्रयास होना चाहिए।
jose.angel.jimenez

2
@ Mausy5043 पायथन आपको कक्षाओं को परिभाषित करने के लिए कोष्ठक नहीं होने देता है। हालांकि यह अजगर 3.x के लिए पूरी तरह से ठीक है, लेकिन अजगर 2.x के लिए, सबसे अच्छा अभ्यास "वर्ग XYZ (ऑब्जेक्ट):" का उपयोग करना है। कारण बताया जा रहा है: docs.python.org/2/reference/datamodel.html#newstyle
मयंक जायसवाल

2
आपका अनुसरण करने के लिए, आपको प्रेरित करने के लिए, धन्यवाद। यह मेरे लिए हर समय उपयोगी है।
क्रिस्चौएरबैच

2
बदतर स्थिति में, इसका मतलब यह होगा कि शान से बंद करने से पहले एक और पुनरावृत्ति करना। Falseमूल्य केवल एक बार सेट कर दिया जाता है, और फिर यह केवल को असत्य से सत्य जा सकते हैं तो मल्टीपल एक्सेस कोई मुद्दा नहीं है।
अल्केस्ट_ जूल 25'18

52

सबसे पहले, मैं निश्चित नहीं हूं कि आपको सेट करने के लिए दूसरे धागे की आवश्यकता है shutdown_flag
इसे सीधे SIGTERM हैंडलर में सेट क्यों नहीं किया जाता है?

एक विकल्प SIGTERMहैंडलर से एक अपवाद को उठाना है , जिसे स्टैक तक प्रचारित किया जाएगा। मान लें कि आपको उचित अपवाद हैंडलिंग मिली है (जैसे with/ contextmanagerऔर try: ... finally:ब्लॉक के साथ) यह एक बहुत ही सुंदर शटडाउन होना चाहिए, यदि आप इसे पसंद करते हैंCtrl+C अपने कार्यक्रम के ।

उदाहरण कार्यक्रम signals-test.py:

#!/usr/bin/python

from time import sleep
import signal
import sys


def sigterm_handler(_signo, _stack_frame):
    # Raises SystemExit(0):
    sys.exit(0)

if sys.argv[1] == "handle_signal":
    signal.signal(signal.SIGTERM, sigterm_handler)

try:
    print "Hello"
    i = 0
    while True:
        i += 1
        print "Iteration #%i" % i
        sleep(1)
finally:
    print "Goodbye"

अब Ctrl+Cव्यवहार देखें :

$ ./signals-test.py default
Hello
Iteration #1
Iteration #2
Iteration #3
Iteration #4
^CGoodbye
Traceback (most recent call last):
  File "./signals-test.py", line 21, in <module>
    sleep(1)
KeyboardInterrupt
$ echo $?
1

इस बार मैं इसे SIGTERM4 पुनरावृत्तियों के साथ भेजता हूं kill $(ps aux | grep signals-test | awk '/python/ {print $2}'):

$ ./signals-test.py default
Hello
Iteration #1
Iteration #2
Iteration #3
Iteration #4
Terminated
$ echo $?
143

इस बार मैंने अपने कस्टम SIGTERMहैंडलर को सक्षम किया और इसे भेजा SIGTERM:

$ ./signals-test.py handle_signal
Hello
Iteration #1
Iteration #2
Iteration #3
Iteration #4
Goodbye
$ echo $?
0

3
"इसे SIGTERM हैंडलर में सीधे सेट क्यों नहीं किया जाता है" --- क्योंकि श्रमिक धागा एक यादृच्छिक स्थान पर बाधित होता है। यदि आप अपने वर्कर लूप में कई स्टेटमेंट डालते हैं, तो आप देखेंगे कि आपका समाधान एक रैंडम पोजीशन पर एक वर्कर को समाप्त कर देता है, जो किसी अज्ञात स्थिति में नौकरी छोड़ देता है।
ज़ूल

मेरे लिए अच्छी तरह से काम करता है, एक डॉकर संदर्भ में भी। धन्यवाद!
मैरियन

4
यदि आप केवल एक झंडा लगाते हैं और अपवाद नहीं उठाते हैं तो यह धागे के साथ भी ऐसा ही होगा। इसलिए धागे का उपयोग करना यहां बहुत ही अच्छा है।
Suor

28

मुझे लगता है कि आप एक संभावित समाधान के निकट हैं।

mainloopएक अलग धागे में निष्पादित करें और इसे संपत्ति के साथ विस्तारित करें shutdown_flag। सिग्नल signal.signal(signal.SIGTERM, handler)को मुख्य थ्रेड (एक अलग थ्रेड में नहीं) के साथ पकड़ा जा सकता है । सिग्नल हैंडलर shutdown_flagको ट्रू पर सेट करना चाहिए और थ्रेड के खत्म होने का इंतजार करना चाहिएthread.join()


4
हां, एक अलग धागा है कि मैंने आखिरकार इसे कैसे हल किया है, धन्यवाद
zerkms

7
यहां थ्रेड्स की आवश्यकता नहीं है। एक एकल थ्रेडेड प्रोग्राम में, आप पहले एक सिग्नल हैंडलर रजिस्टर कर सकते हैं (सिग्नल हैंडलर को पंजीकृत करना गैर अवरुद्ध है) और इसके बाद वीसीओपी लिखें। सिग्नल हैंडलर फ़ंक्शन को एक ध्वज सेट करना चाहिए जब और इस ध्वज के लिए लूप की जांच होनी चाहिए। मैंने उसी के लिए एक वर्ग आधारित समाधान चिपकाया है
मयंक जायसवाल

2
ऐसा कोई तरीका नहीं है कि दूसरा धागा होना आवश्यक है। रजिस्टर सिग्नल हैंडलर।
oneloop


26

यहां थ्रेड्स या कक्षाओं के बिना एक सरल उदाहरण है।

import signal

run = True

def handler_stop_signals(signum, frame):
    global run
    run = False

signal.signal(signal.SIGINT, handler_stop_signals)
signal.signal(signal.SIGTERM, handler_stop_signals)

while run:
    pass # do stuff including other IO stuff

11

पिछले उत्तरों के आधार पर, मैंने एक संदर्भ प्रबंधक बनाया है, जो सिगंट और सिग्मटर से बचाता है।

import logging
import signal
import sys


class TerminateProtected:
    """ Protect a piece of code from being killed by SIGINT or SIGTERM.
    It can still be killed by a force kill.

    Example:
        with TerminateProtected():
            run_func_1()
            run_func_2()

    Both functions will be executed even if a sigterm or sigkill has been received.
    """
    killed = False

    def _handler(self, signum, frame):
        logging.error("Received SIGINT or SIGTERM! Finishing this block, then exiting.")
        self.killed = True

    def __enter__(self):
        self.old_sigint = signal.signal(signal.SIGINT, self._handler)
        self.old_sigterm = signal.signal(signal.SIGTERM, self._handler)

    def __exit__(self, type, value, traceback):
        if self.killed:
            sys.exit(0)
        signal.signal(signal.SIGINT, self.old_sigint)
        signal.signal(signal.SIGTERM, self.old_sigterm)


if __name__ == '__main__':
    print("Try pressing ctrl+c while the sleep is running!")
    from time import sleep
    with TerminateProtected():
        sleep(10)
        print("Finished anyway!")
    print("This only prints if there was no sigint or sigterm")

4

मेरे लिए सबसे आसान रास्ता मिल गया। यहाँ स्पष्टता के लिए कांटा के साथ एक उदाहरण है कि यह तरीका प्रवाह नियंत्रण के लिए उपयोगी है।

import signal
import time
import sys
import os

def handle_exit(sig, frame):
    raise(SystemExit)

def main():
    time.sleep(120)

signal.signal(signal.SIGTERM, handle_exit)

p = os.fork()
if p == 0:
    main()
    os._exit()

try:
    os.waitpid(p, 0)
except (KeyboardInterrupt, SystemExit):
    print('exit handled')
    os.kill(p, 15)
    os.waitpid(p, 0)

0

सबसे सरल समाधान जो मैंने पाया है, ऊपर की प्रतिक्रियाओं से प्रेरणा लेना

class SignalHandler:

    def __init__(self):

        # register signal handlers
        signal.signal(signal.SIGINT, self.exit_gracefully)
        signal.signal(signal.SIGTERM, self.exit_gracefully)

        self.logger = Logger(level=ERROR)

    def exit_gracefully(self, signum, frame):
        self.logger.info('captured signal %d' % signum)
        traceback.print_stack(frame)

        ###### do your resources clean up here! ####

        raise(SystemExit)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.