Column address: One Python module per week

At the same time, you are also welcome to follow my wechat public account AlwaysBeta, more exciting content waiting for you.

Signal is a common IPC in Unix systems. For example, we often operate the KILL -9 PID, where -9 corresponds to the SIGKILL signal, 9 is the number of the signal, SIGKILL is its name. Because implementations of * NUX vary from version to version, see the system API for details. You can use man 7 Signal to see the definition of all signals.

So, what are the use scenarios for signals? Compared to other methods of interprocess communication (such as pipes, shared memory, etc.), the amount of information that can be transmitted by a signal is crude and is only an integer. However, due to the small amount of information transmitted, signals are easier to manage and use, and can be used for tasks related to system management. For example, notify process termination, abort, or resume. Each signal is represented by an integer constant macro starting with SIG, such as SIGCHLD, SIGINT, etc.

Received signal

Python uses the Signal module to handle signal-related operations, defined as follows:

signal.signal(signalnum, handler)
Copy the code

Signalnum indicates a signal, and handler indicates the handler function of the signal. Processes can ignore signals, take default actions, and customize actions. When handler is signal.SIG_IGN, the signal is ignored. When handler is singal.SIG_DFL, the process takes the default action. When handler is a function name, the process takes the action defined in the function.

Write a small program to handle CTRL + C events and SIGHUP, i.e. 1 and 2 signals.

#coding:utf-8

import signal
import time
import sys
import os

def handle_int(sig, frame):
    print "get signal: %s, I will quit"%sig
    sys.exit(0)

def handle_hup(sig, frame):
    print "get signal: %s"%sig


if __name__ == "__main__":
    signal.signal(2, handle_int)
    signal.signal(1, handle_hup)
    print "My pid is %s"%os.getpid()
    while True:
        time.sleep(3)
Copy the code

To test this, start the program (according to the printed PID), enter kill -1 21838 and kill -hup 21838 in another window, and use CTRL + C to close the program. The output of the program is as follows:

# python recv_signal.py
My pid is 21838
get signal: 1
get signal: 1
^Cget signal: 2, I will quit
Copy the code

Let’s look at another function to get a better understanding of the signal:

signal.getsignal(signalnum)
Copy the code

Depending on the handler that signalnum returns, it could be a Python object that can be called, Signal. SIG_IGN (to be ignored), signal.SIG_DFL (the default behavior is already used), or None (Python’s handler has not yet been defined).

Take a look at the following example to get the num and name of the signal defined in signal and what its handler is.

#coding:utf-8

import signal

def handle_hup(sig, frame):
    print "get signal: %s"%sig

signal.signal(1, handle_hup)

if __name__ == "__main__":

    ign = signal.SIG_IGN
    dfl = signal.SIG_DFL
    print "SIG_IGN", ign
    print "SIG_DFL", dfl
    print "*"* 40for name in dir(signal):
        if name[:3] == "SIG"and name[3] ! ="_":
            signum = getattr(signal, name)
            gsig = signal.getsignal(signum)

            print name, signum, gsig
Copy the code

Result: You can see that most of the signals have default behavior.

SIG_IGN 1
SIG_DFL 0
****************************************
SIGABRT 6 0
SIGALRM 14 0
SIGBUS 10 0
SIGCHLD 20 0
SIGCONT 19 0
SIGEMT 7 0
SIGFPE 8 0
SIGHUP 1 <function handle_hup at 0x109371c80>
SIGILL 4 0
SIGINFO 29 0
SIGINT 2 <built-in function default_int_handler>
SIGIO 23 0
SIGIOT 6 0
SIGKILL 9 None
SIGPIPE 13 1
SIGPROF 27 0
SIGQUIT 3 0
SIGSEGV 11 0
SIGSTOP 17 None
SIGSYS 12 0
SIGTERM 15 0
SIGTRAP 5 0
SIGTSTP 18 0
SIGTTIN 21 0
SIGTTOU 22 0
SIGURG 16 0
SIGUSR1 30 0
SIGUSR2 31 0
SIGVTALRM 26 0
SIGWINCH 28 0
SIGXCPU 24 0
SIGXFSZ 25 1
Copy the code

Several commonly used signals:

Serial number The name of the role
1 SIGHUP The terminal suspends or terminates the process. The default action is to terminate the process
2 SIGINT The keyboard interrupt<ctrl+c>It’s used all the time. The default action is to terminate the process
3 SIGQUIT The keyboard exit key is pressed. Generally used in response<ctrl+d>. The default action terminates the process
9 SIGKILL Forcibly exit. Often used in shells
14 SIGALRM If the timer expires, the process is terminated by default
15 SIGTERM Program end signal, the program will usually clean up its state before exiting, which is what we call an elegant exit

Send a signal

The core of the signal package is to set up the signal handlers. There is no signaling capability other than signal.alarm() to send a signal to itself. However, in the OS package, there are functions similar to the Linux kill command, which are:

os.kill(pid, sid)
os.killpg(pgid, sid)
Copy the code

Signals are sent to processes and process groups respectively. Sid is the integer or singal.sig * corresponding to the signal.

Emit SIGALRM signal regularly

It is used to send a SIGALRM signal to the process itself after a certain amount of time, which is useful to avoid blocking I/O operations or other system calls indefinitely.

import signal
import time


def receive_alarm(signum, stack):
    print('Alarm :', time.ctime())


# Call receive_alarm in 2 seconds
signal.signal(signal.SIGALRM, receive_alarm)
signal.alarm(2)

print('Before:', time.ctime())
time.sleep(4)
print('After :', time.ctime())

# output
# Before: Sat Apr 22 14:48:57 2017
# Alarm : Sat Apr 22 14:48:59 2017
# After : Sat Apr 22 14:49:01 2017
Copy the code

In this example, the call to sleep() is interrupted, but continues after signal processing, so that the message printed after sleep() returns shows that the program is executing for as long as the sleep lasts.

Ignore the signal

To ignore signals, register SIG_IGN as the handler.

The following example registers two programs, SIGINT and SIGUSR1, and uses signal.pause() to wait for the signal to be received.

import signal
import os
import time


def do_exit(sig, stack):
    raise SystemExit('Exiting')


signal.signal(signal.SIGINT, signal.SIG_IGN)
signal.signal(signal.SIGUSR1, do_exit)

print('My PID:', os.getpid())

signal.pause()

# output
# My PID: 72598
# ^C^C^C^CExiting
Copy the code

Typically SIGINT (a signal sent to a program by the shell when the user presses Ctrl-C) raises KeyboardInterrupt. This example just ignores it when it sees SIGINT. Each ^C in the output represents an attempt to terminate the script from the terminal.

Exit the script from another terminal using kill -usR1 72598.

Signals and threads

In multithreaded environments, only the main thread can set the handler for the signal, and only it can receive the signal. Let’s see the effect with an example of waiting for a signal in one thread and sending it from another.

#coding:utf-8
# orangleliu py2.7
#thread_signal.py

import signal
import threading
import os
import time

def usr1_handler(num, frame):
    print "received signal %s %s"%(num, threading.currentThread())

signal.signal(signal.SIGUSR1, usr1_handler)

def thread_get_signal(a):
    Signal handler in a child thread
    #ValueError: signal only works in main thread
    #signal.signal(signal.SIGUSR2, usr1_handler)

    print "waiting for signal in", threading.currentThread()
    # Sleep process until a signal is received
    signal.pause()
    print "waiting done"

receiver = threading.Thread(target=thread_get_signal, name="receiver")
receiver.start()
time.sleep(0.1)

def send_signal(a):
    print "sending signal in ", threading.currentThread()
    os.kill(os.getpid(), signal.SIGUSR1)

sender = threading.Thread(target=send_signal, name="sender")
sender.start()
sender.join()

print 'pid', os.getpid()
This is to let the program end and wake up pause
signal.alarm(2)
receiver.join()

# output
# waiting for signal in <Thread(receiver, started 123145306509312)>
# sending signal in 
      (sender,>
# received signal 30 <_MainThread(MainThread, started 140735138967552)>
# pid 23188
# [1] 23188 alarm python thread_signal.py
Copy the code

Python’s Signal module requires that all handlers be registered with the main Thread, even if the underlying platform supports mixed thread and signal programming. No signal is received even though the receiving thread calls signal.pause(). The signal.alarm(2) at the end of the code is to wake up pause() on the receiving thread, otherwise the receiving thread will never exit.

Although alarms can be set in any thread, they can only be received on the main Thread.

import signal
import time
import threading


def signal_handler(num, stack):
    print(time.ctime(), 'Alarm in',
          threading.currentThread().name)


signal.signal(signal.SIGALRM, signal_handler)


def use_alarm(a):
    t_name = threading.currentThread().name
    print(time.ctime(), 'Setting alarm in', t_name)
    signal.alarm(1)
    print(time.ctime(), 'Sleeping in', t_name)
    time.sleep(3)
    print(time.ctime(), 'Done with sleep in', t_name)


# Start a thread that will not receive the signal
alarm_thread = threading.Thread(
    target=use_alarm,
    name='alarm_thread',
)
alarm_thread.start()
time.sleep(0.1)

# Wait for the thread to see the signal (not going to happen!)
print(time.ctime(), 'Waiting for', alarm_thread.name)
alarm_thread.join()

print(time.ctime(), 'Exiting normally')

# output
# Sat Apr 22 14:49:01 2017 Setting alarm in alarm_thread
# Sat Apr 22 14:49:01 2017 Sleeping in alarm_thread
# Sat Apr 22 14:49:01 2017 Waiting for alarm_thread
# Sat Apr 22 14:49:02 2017 Alarm in MainThread
# Sat Apr 22 14:49:04 2017 Done with sleep in alarm_thread
# Sat Apr 22 14:49:04 2017 Exiting normally
Copy the code

Alarm does not interrupt the sleep in use_alarm(). Related documents:

Pymotw.com/3/signal/in…

Orangleliu. Info / 2016/03/06 /…

www.cnblogs.com/vamei/archi…