কীভাবে সাইনটারএম সিগন্যালকে কৌতুকপূর্ণভাবে প্রক্রিয়া করবেন?


197

আসুন ধরে নেওয়া যাক আমাদের অজগরে এমন একটি তুচ্ছ ডেমন লেখা আছে:

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

mainloop()

এবং আমরা তা ব্যবহার daemonize start-stop-daemonযা ডিফল্টভাবে পাঠায় SIGTERM( TERM) এ সংকেত --stop

ধরা যাক বর্তমান পদক্ষেপটি সম্পাদিত হয় #2। এবং এই মুহুর্তে আমরা TERMসংকেত প্রেরণ করছি ।

যা ঘটে তা হ'ল ফলশ্রুতি অবিলম্বে শেষ হয়।

আমি দেখেছি যে, আমি ব্যবহার সংকেত ঘটনা সব ব্যবস্থা করতে সক্ষম signal.signal(signal.SIGTERM, handler)কিন্তু জিনিস যে এটি এখনও বর্তমান সঞ্চালনের ব্যাঘাত এবং নিয়ন্ত্রণ পাসের হয় handler

সুতরাং, আমার প্রশ্ন হ'ল - বর্তমান মৃত্যুদন্ড কার্যকর করতে বা TERMবিচ্ছিন্ন থ্রেডে (?) সিগন্যালটি পরিচালনা করা কি সম্ভব যাতে আমি সেট করতে সক্ষম হয়েছি shutdown_flag = Trueযাতে mainloop()করুণাময় থামার সুযোগ হয়েছিল?


2
প্রক্রিয়াটি সরবরাহ করার সময় ব্যবহার করে signalfdএবং মুখোশের মাধ্যমে আপনি যা জিজ্ঞাসা করেছেন তা আমি করেছি SIGTERM
এরিক আরবান

উত্তর:


275

সমাধানের জন্য ক্লাস ভিত্তিক ক্লিন:

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
রএসউ

7
এটি সেরা উত্তর (কোনও থ্রেডের প্রয়োজন নেই), এবং প্রথম চেষ্টা করার পছন্দটি হওয়া উচিত।
jose.angel.jimenez

2
@ Mausy5043 পাইথন আপনাকে ক্লাস নির্ধারণের জন্য প্রথম বন্ধনী না দেওয়ার অনুমতি দেয়। যদিও এটি পাইথন ৩.x এর পক্ষে পুরোপুরি ঠিক আছে তবে পাইথন ২.x এর ক্ষেত্রে "ক্লাস এক্সওয়াইজেড (অবজেক্ট):" ব্যবহার করা সবচেয়ে ভাল অনুশীলন। কারণ হওয়ার কারণ: docs.python.org/2/references/datamodel.html#newstyle
মায়াঙ্ক

2
আপনাকে অনুপ্রাণিত রাখার জন্য অনুসরণ করুন, আপনাকে ধন্যবাদ। আমি এই সব সময় ব্যবহার।
chrisfauerbach

2
আরও খারাপ ক্ষেত্রে, এর অর্থ হ'ল কৃপণভাবে নিচে নামার আগে অন্য পুনরাবৃত্তি করা। Falseমান শুধুমাত্র একবার সেট করা হয়, এবং তারপর এটি শুধুমাত্র মিথ্যা থেকে সত্যতে যেতে পারেন, যাতে একাধিক এক্সেস কোনো সমস্যা হয় না।
Alceste_

52

প্রথমত, আমি নিশ্চিত না যে সেটটি স্থাপন করতে আপনার দ্বিতীয় থ্রেডের প্রয়োজন shutdown_flag
এটিকে কেন সরাসরি সিগটারএম হ্যান্ডলারে সেট করবেন না?

একটি বিকল্প 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

এবার আমি এটি SIGTERMদিয়ে 4 টি পুনরাবৃত্তির পরে পাঠাব 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
"কেন এটিকে সরাসরি সিগটারএম হ্যান্ডলারে সেট করা যায় না" --- কারণ কর্মী থ্রেড এলোমেলো জায়গায় বাধা দেয়। আপনি যদি আপনার কর্মী লুপে একাধিক বক্তব্য রাখেন তবে আপনি দেখতে পাবেন যে আপনার সমাধান কোনও শ্রমিককে এলোমেলো অবস্থানে সমাপ্ত করে, যা কাজটি অজানা অবস্থায় ফেলে দেয়।
zerkms

আমার পক্ষে ডকার প্রসঙ্গেও ভাল কাজ করে। ধন্যবাদ!
মারিয়ান

4
আপনি যদি কেবল একটি পতাকা সেট করেন এবং ব্যতিক্রম না বাড়ান তবে এটি থ্রেডের মতোই হবে। সুতরাং থ্রেড ব্যবহার এখানে অতিরিক্ত প্রয়োজন।
সোর

28

আমি মনে করি আপনি একটি সম্ভাব্য সমাধানের কাছাকাছি রয়েছেন।

সম্পাদন mainloopএকটি পৃথক থ্রেডে এবং এটা সম্পত্তি সঙ্গে প্রসারিত shutdown_flagsignal.signal(signal.SIGTERM, handler)মূল থ্রেড (পৃথক থ্রেডে নয়) দিয়ে সিগন্যালটি ধরা যেতে পারে । সিগন্যাল হ্যান্ডলারটি shutdown_flagসত্য হিসাবে সেট করা উচিত এবং থ্রেডটি শেষ হওয়ার জন্য অপেক্ষা করা উচিতthread.join()


4
হ্যাঁ, একটি পৃথক থ্রেড হ'ল আমি শেষ পর্যন্ত কীভাবে সমাধান করেছি, ধন্যবাদ
জের্কামস

7
থ্রেড এখানে প্রয়োজন হয় না। নিজেই একটি থ্রেডেড প্রোগ্রামে, আপনি প্রথমে একটি সিগন্যাল হ্যান্ডলার নিবন্ধন করতে পারেন (একটি সিগন্যাল হ্যান্ডলার নিবন্ধন করা অবরুদ্ধ নয়) এবং তারপরে মেইনলুপ লিখতে পারেন। সিগন্যাল হ্যান্ডলার ফাংশনটি কখন একটি পতাকা সেট করে এবং কখন এই পতাকাটির জন্য লুপটি পরীক্ষা করা উচিত। আমি এখানে একই জন্য একটি বর্গ ভিত্তিক সমাধান পেস্ট করেছি ।
মায়াঙ্ক জয়সওয়াল

2
দ্বিতীয় থ্রেড থাকা কোনওভাবেই প্রয়োজনীয় নয়। সিগন্যাল হ্যান্ডলার নিবন্ধন করুন।
অনেলুপ

সহায়ক পৃষ্ঠা: g-loaded.eu/2016/11/24/…
কামিল সিন্ধি

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)
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.