Signal
UNIX / Linux systems provide special mechanisms to communicate between processes. One of them is called signals.
In short, a signal is like a notification of an event. When an event occurs in the system, a signal will be generated to notify other programs about this event.
A simple example: When you are running a command in the terminal. The command is running, but you use ctrl + C
At that time, a signal called SIGINT
was generated. And the terminal will read that signal and execute the stop command.
On UNIX-based systems, there are three main types of signals:
- System signals:
- SIGILL
- SIGTRAP
- SIGBUS
- SIGFPE,
- SIGKILL
- SIGSEGV
- SIGXCPU
- SIGXFSZ
- SIGIO
- Device signals:
- SIGHUP
- SIGINT
- SIGPIPE
- SIGALRM
- SIGCHLD
- SIGCONT
- SIGSTOP
- SIGTTIN
- SIGTTOU
- SIGURG
- SIGWINCH
- SIGIO
- User-defined signals:
- SIGQUIT
- SIGABRT
- SIGUSR1
- SIGUSR2
- SIGTERM
Each signal will be represented by an integer value.
On Ubuntu, you can check this signal list:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | man 7 signal .... First the signals described in the original POSIX.1-1990 standard. Signal Value Action Comment ────────────────────────────────────────────────────────────────────── SIGHUP 1 Term Hangup detected on controlling terminal or death of controlling process SIGINT 2 Term Interrupt from keyboard SIGQUIT 3 Core Quit from keyboard SIGILL 4 Core Illegal Instruction SIGABRT 6 Core Abort signal from abort(3) SIGFPE 8 Core Floating point exception SIGKILL 9 Term Kill signal SIGSEGV 11 Core Invalid memory reference SIGPIPE 13 Term Broken pipe: write to pipe with no readers SIGALRM 14 Term Timer signal from alarm(2) SIGTERM 15 Term Termination signal SIGUSR1 30,10,16 Term User-defined signal 1 SIGUSR2 31,12,17 Term User-defined signal 2 SIGCHLD 20,17,18 Ign Child stopped or terminated SIGCONT 19,18,25 Cont Continue if stopped SIGSTOP 17,19,23 Stop Stop process SIGTSTP 18,20,24 Stop Stop typed at terminal SIGTTIN 21,21,26 Stop Terminal input for background process SIGTTOU 22,22,27 Stop Terminal output for background process The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored. .... |
When you execute the man 7 signal
command, you will also see how to use the signal in Ubuntu:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | Sending a signal The following system calls and library functions allow the caller to send a signal: raise(3) Sends a signal to the calling thread. kill(2) Sends a signal to a specified process, to all members of a specified process group, or to all processes on the system. killpg(2) Sends a signal to all of the members of a specified process group. pthread_kill(3) Sends a signal to a specified POSIX thread in the same process as the caller. tgkill(2) Sends a signal to a specified thread within a specific process. (This is the system call used to implement pthread_kill(3).) sigqueue(3) Sends a real-time signal with accompanying data to a specified process. Waiting for a signal to be caught The following system calls suspend execution of the calling process or thread until a signal is caught (or an unhandled signal terminates the process): pause(2) Suspends execution until any signal is caught. sigsuspend(2) Temporarily changes the signal mask (see below) and suspends execution until one of the unmasked signals is caught. Synchronously accepting a signal Rather than asynchronously catching a signal via a signal handler, it is possible to synchronously accept the signal, that is, to block execution until the signal is deliv‐ ered, at which point the kernel returns information about the signal to the caller. There are two general ways to do this: * sigwaitinfo(2), sigtimedwait(2), and sigwait(3) suspend execution until one of the signals in a specified set is delivered. Each of these calls returns information about the delivered signal. * signalfd(2) returns a file descriptor that can be used to read information about signals that are delivered to the caller. Each read(2) from this file descriptor blocks until one of the signals in the set specified in the signalfd(2) call is delivered to the caller. The buffer returned by read(2) contains a structure describing the sig‐ nal. |
Python signal
Since Python 1.4, the signal
library has been integrated and updated regularly into the core.
Python Basic
A small example of using the SIGINT
signal:
1 2 3 4 5 6 7 8 9 10 11 12 13 | import signal import time def handler(a, b): print("Signal Number:", a, " Frame: ", b) signal.signal(signal.SIGINT, handler) while True: print("Press ctrl + c") time.sleep(10) |
Lines 1, 2 will import lib signal
and lib time
.
Line 5, 6 define function handle signal. In this function, I will print the integer value of the signal and frame it receives along with the signal.
Line 8 uses the signal.signal()
function to assign handle SIGINT
signal. Every time the CPU receives ctrl + c
, the function handler
is executed.
Lines 10, 11, 12 use while True
to keep the program running.
Save the above code to test it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | % python test_signal.py Press ctrl + c ^C('Signal Number:', 2, ' Frame: ', <frame object at 0x7f573f59c050>) Press ctrl + c ^C('Signal Number:', 2, ' Frame: ', <frame object at 0x7f573f59c050>) Press ctrl + c ^C('Signal Number:', 2, ' Frame: ', <frame object at 0x7f573f59c050>) Press ctrl + c ^C('Signal Number:', 2, ' Frame: ', <frame object at 0x7f573f59c050>) Press ctrl + c ^C('Signal Number:', 2, ' Frame: ', <frame object at 0x7f573f59c050>) Press ctrl + c ^C('Signal Number:', 2, ' Frame: ', <frame object at 0x7f573f59c050>) Press ctrl + c ^C('Signal Number:', 2, ' Frame: ', <frame object at 0x7f573f59c050>) Press ctrl + c |
The above is a very simple example of using sinal in Python basic.
Django
Django provides signal dispatcher
. It allows separate apps to be notified when actions take place elsewhere in the framework.
Signal built-in in Django:
django.db.models.signals.pre_save & django.db.models.signals.post_save
: Send before or after the save () method is executed in the model.django.db.models.signals.pre_delete & django.db.models.signals.post_delete
: Send before or after the delete () method is implemented in the model.django.db.models.signals.m2m_changed
: Sent whenManyToManyField
on a model is changed.django.core.signals.request_started & django.core.signals.request_finished
: Sent when Django starts or finishes an HTTP request.
In django, use the Signal.connect()
method to register a receiver
function. receiver
function is called when a signal is sent. All signal’s receiver functions are called at the same time and based on the order of registration.
1 2 | Signal.connect(receiver, sender=None, weak=True, dispatch_uid=None) |
The problem below is printing out the words Request finished!
on the console after each request is completed.
Receiver functions
First, how is the receiver
defined?
1 2 3 | def my_callback(sender, **kwargs): print("Request finished!") |
Note, the receiver
function must have sender
argument. Other arguments will need to be pushed into **kwargs
.
Connecting receiver functions
There are 2 syntaxes that allow connect a receiver to a signal.
- Option1: use
request_finished.connect()
.
1 2 3 4 | from django.core.signals import request_finished request_finished.connect(my_callback) |
- Option2: use
receiver()
decorator.
1 2 3 4 5 6 7 | from django.core.signals import request_finished from django.dispatch import receiver @receiver(request_finished) def my_callback(sender, **kwargs): print("Request finished!") |
After setup is complete, the my_callback
function will be called every time the request finishes.
With the above problem, all the requests will end up firing a signal. So with the problem, I will only shoot signal with a specific case, why?
Connecting to signals sent by specific senders
In user registration problem. Suppose you have a User model. After saving the user info to the model, I want to send a notification that “You have successfully registered” for example.
I will need to use signal built-in: from django.db.models.signals import post_save
1 2 3 4 5 6 7 8 9 10 | from django.db.models.signals import post_save from django.dispatch import receiver from myapp.models import User @receiver(post_save, sender=User) def my_handler(sender, **kwargs): ... # Todo: Send email |
Summary
In this article, I showed you what a signal is? How to use signal in Python in general and in Django in particular.
Thanks for reading!