Linux Signal Basics and Mechanisms (as of kernel v5.5)

This article summarizes my research into the basics and mechanics of Linux signals (kernel implementation).

I usually use signals, but I didn't understand how they work, so I looked them up as a subject for studying the kernel. It was more complicated and voluminous than I had imagined, so there are some parts that I couldn't write out (parts that I couldn't investigate), but I think I understood the general flow (mechanism).

This article is mainly composed of "■ Basic Edition" and "■ Kernel Edition (v5.5)". I think that it is necessary to know the basics to understand the mechanism, so it is structured like this. Those who understand the basics of the book level are recommended to read from "■ Kernel (v5.5)".

■ Basic

First, let's take a quick look at the basics of signals.

The detailed usage and error handling of the commands and API (C language) that appear in the examples are omitted. For details, please use the information in man and references.

1. What is a signal?

A kernel feature that notifies processes and process groups of various events (software interrupts). Event notifications can be sent from various locations (your / other processes, kernel), including:

--Send a signal to the hung process to kill it --Send a signal to suspend / resume process processing --Send a signal to terminate the process in the event of a hardware exception (divide by zero, memory access violation, etc.) --Enter a special key to send a signal (such as Ctrl + C) to end the process --Execute a user-defined function (signal handler) when receiving a signal

The signal is processed in the following flow. We will look at the details later.

all_signal (2)-flow.jpg

2. Signal usage example

To make it easier to imagine, here are three examples of signal usage from the user's perspective.

kill command

The kill command sends a signal (SIGTERM) to terminate the sleep process.

$ ps
  PID TTY          TIME CMD
 2662 pts/0    00:00:00 bash
 2679 pts/0    00:00:00 sleep
 2685 pts/0    00:00:00 ps

$ kill 2679
[1]+  Terminated              sleep 1000

$ ps
  PID TTY          TIME CMD
 2662 pts/0    00:00:00 bash
 2696 pts/0    00:00:00 ps

Interrupt key

The interrupt key (Ctrl + C) sends a signal (SIGINT) to end the infinite loop command (process group).

$ while :; do sleep 1; echo "Stop me !"; done
Stop me !
Stop me !
Stop me !
^C

Program (C language API)

You can also terminate the sleep process by sending a signal (SIGTERM) in your own program (send_sig.c) as follows:

#include <signal.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    pid_t pid = atoi(argv[1]);
    kill(pid, SIGTERM);

    return 0;
}

Compile & execution example

$ ps
  PID TTY          TIME CMD 
30285 pts/0    00:00:00 bash
30597 pts/0    00:00:00 sleep
30631 pts/0    00:00:00 ps

$ gcc -o send_sig send_sig.c
$ ./send_sig 30597
[1]+  Terminated              sleep 1000

$ ps
  PID TTY          TIME CMD
30285 pts/0    00:00:00 bash
30663 pts/0    00:00:00 ps

3. Signal number and signal name

Signals are numbered and named according to their intended use.

$ kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX	

However, some of the signal numbers are architecture-dependent and may differ from the example run above (x86_64) [^ 7signal].

[^ 7 signal]: See man 7 signal for details.

4. Standard signal, real-time signal

Signals can be broadly divided into two types: standard signals and real-time signals.

The rough difference is

--Standard signal ... Traditionally used signals such as SIGKILL (1 to 31) --Real-time signal ... Extended version of standard signal (Nos. 32 to 64)

is. The slightly more subtle differences are:

Standard signal Real-time signal
new/Old Old new
Signal name Various for each signal SIGRTMIN(+n), SIGRTMAX(-n)
Signal number 1 〜 31 32 〜 64
Standard operation(Details will be described later) Various for each signal End of process(All signals)
Use Various for each signal User-defined(All signals)
Multiple same signals
Behavior at the time of reception
Receive only one Receive all
Multiple same signals
Order when sending
No regulation Arrived in the order they were sent
Multiple different signals
Order when sending
No regulation Arrived in ascending order of signal number

This article does not go deeper into real-time signals [^ 7signal].

5. Signal action

There are three types of actions (signal actions) when receiving a signal. This behavior can be changed from sigaction () described later. However, ** SIGKILL and SIGSTOP can only be used as standard operation. ** **

all_signal-sigaction.jpg

It does nothing when it receives a signal.

(To set, specify SIG_IGN for sa_handler with sigaction () etc. described later)

-** Standard operation **

When a signal is received, the standard operation defined for each signal (Term, Ign, Core, Stop, Cont described later) is executed.

(To set, specify SIG_DFL for sa_handler with sigaction () described later (default))

-** Signal handler **

When it receives a signal, it performs a user-defined action.

(To set, specify a user-defined function to sa_handler with sigaction () etc. described later)

Standard operation

The standard operation is defined for each signal, and there are the following five types.

Operation name meaning
Term End of process
Ign do nothing(sigaction()SIG that can be set with_Same as IGN)
Core Process termination and core dump generation[^5core]
Stop Process pause(TASK_Transition to STOPPED state)
Cont Resuming a suspended process(TASK_Return from STOPPED state)

However, this is for standard signals only. Term only for real-time signals (see figure below).

all_signal-kernel_handler.jpg

[^ 5 core]: See man 5 core for details.

Standard signal and corresponding standard behavior

The standard signals 1 to 31 have the standard behavior associated with each.

Signal name Signal number(x86_64) Standard operation meaning
SIGHUP 1 Term Control terminal hang detection or control process death
SIGINT 2 Term Keyboard interrupt(Ctrl + C)
SIGQUIT 3 Core Exit from keyboard(Ctrl + Q)
SIGILL 4 Core Illegal order
SIGTRAP 5 Core Trace for debugging/Breakpoint signal
SIGABRT 6 Core abort(3)Suspension signal from
SIGBUS 7 Core Bus error(Illegal memory access)
SIGFPE 8 Core Floating point exception
SIGKILL 9 Term Kill signal
SIGUSR1 10 Term User-defined signal(Part 1)
SIGSEGV 11 Core Illegal memory reference
SIGUSR2 12 Term User-defined signal(Part 2)
SIGPIPE 13 Term Pipe destruction(Export to a pipe without a reader) [^7pipe]。
SIGALRM 14 Term alarm(2)Timer signal from
SIGTERM 15 Term End signal
SIGSTKFLT 16 Term Numerical processor(Coprocessor)Stack fault in(unused)
SIGCHLD 17 Ign Pause child process(Resume)Or end
SIGCONT 18 Cont Resuming the pause process
SIGSTOP 19 Stop Pause process
SIGTSTP 20 Stop Stop from the control terminal(Ctrl + Z)
SIGTTIN 21 Stop Background process read control terminal
SIGTTOU 22 Stop Background process wrote to control terminal
SIGURG 23 Ign Out-of-band data and emergency data exist in the socket
SIGXCPU 24 Core CPU time limit(RLIMIT_CPU)Beyond[^2setrlimit]。
SIGXFSZ 25 Core File size limit(RLIMIT_FSIZE)Beyond[^2setrlimit]。
SIGVTALRM 26 Term Virtual timer(CPU time in user mode for the process)Timed out
SIGPROF 27 Term Profiling timer timed out
SIGWINCH 28 Ign The window size of the control terminal has been changed
SIGIO 29 Term Asynchronous I/O event
SIGPWR 30 Term Power supply error
SIGSYS 31 Core Made an illegal system call[^2seccomp]。

[^ 7 pipe]: See man 7 pipe for details. [^ 2 setrlimit]: See man 2 setrlimit for details. [^ 2 seccomp]: See man 2 seccomp for details.

Signal action modification example

To change signal actions other than ** SIGKILL ** and ** SIGSTOP **, use APIs (C) such as the trap command and sigaction ().

trap command

trap is a type of built-in command built into Bash that allows you to set signal actions [^ 1bash].

Here, try setting (first argument'') to do nothing (ignore the signal) when receiving SIGINT.

[^ 1 bash]: See man 1 bash for details.

Example of setting the signal to ignore


$ trap '' SIGINT

In this state, even if SIGINT is received, it does not respond, so the interrupt key (Ctrl + C) does not work as shown in the following execution example.

$ while true; do sleep 1; echo "Stop me !"; done
Stop me !
Stop me !
^CStop me !                             <--- Ctrl +Pressing C does not work and the command continues
Stop me !
Stop me !
^Z                                      <--- Ctrl +Press Z to stop(SIGTSTP transmission)
[1]+  Stopped                 sleep 1

sigaction (C language API)

sigaction () is an API (C language) that allows you to set the behavior of signals [^ 2 sigaction].

The following sample program (set_sigh.c) tries to set the handler function to be executed when SIGINT is received.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>

static void handler(int signo)
{
	/* 
	 *Originally you should use asynchronous signal safe functions in handlers,
	 *Here is not printf()、exit()I am using a function such as.
	 *About asynchronous signal safe$See man 7 signal.
	 */
	printf(" ... Caught the SIGINT (%d)\n", signo);
	exit(EXIT_SUCCESS);
}

int main(void)
{
	unsigned int sec = 1;
	struct sigaction act;

	//Handler when receiving SIGINT()Set to run.
	memset(&act, 0, sizeof act);
	act.sa_handler = handler;
	sigaction(SIGINT, &act, NULL);

	// Ctrl +Output a message every second until it ends with C etc.
	for (;;) {
		sleep(sec);
		printf("Stop me !\n");
	}
	return 0;
}

In the following execution example, after receiving SIGINT, handler () outputs a message (... Caught the SIGINT (2)) and terminates the program.

$ gcc -o set_sigh set_sigh.c
$ ./set_sigh 
Stop me !
Stop me !
Stop me !
^C ... Caught the SIGINT (2)         <--- Ctrl +Press C to execute the handler function

Another API is signal (), which is deprecated for portability (old API). Unless you have a specific reason, you should avoid using it [^ 2 signal].

[^ 2 sigaction]: See man 2 sigaction for details.

[^ 2 signal]: See man 2 signal for details.

6. Signal block

Signals other than ** SIGKILL and SIGSTOP ** can be blocked on a per-process basis.

For example, if a process receives a SIGINT, the normal behavior is to terminate the process, but if it blocks the SIGINT, it will be unresponsive, like ignoring a signal.

The difference from ignoring the signal is whether or not to withhold the received signal. If the signal is ignored, the signal is not held, but if it is a signal block, it can be held. Therefore, when unblocked, it can be processed without having to receive the signal again [^ sigign].

all_signal-sigblock.jpg

[^ sigign]: It can be canceled even if the signal is ignored, but the signal received in the signal ignored state is not held, so it will not be processed even after it is canceled. You need to clear the ignore and receive the signal again.

Signal block setting example

Use an API (C language) such as sigprocmask () to set the signal block [^ 2 sigprocmask].

The following sample program (block_sig.c) tries to block SIGINT and then unblock SIGINT 5 seconds later.

[^ 2 sigprocmask]: See man 2 sigprocmask for details.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

int main(void)
{
	unsigned int sec = 5;
	sigset_t set;

	//Empty the signal set and add SIGINT
	sigemptyset(&set);
	sigaddset(&set, SIGINT);

	//Block SIGINT(on hold)To do
	sigprocmask(SIG_BLOCK, &set, NULL);
	printf("Block the SIGINT for %d sec\n", sec);

	sleep(sec);

	//Unblock SIGINT
	printf("\nPassed %d sec, unblock the SIGINT\n", sec);
	sigprocmask(SIG_UNBLOCK, &set, NULL);

	printf("Done !\n");

	return 0;
}

In the execution example, SIGINT is sent after the SIGINT block, but you can see that the operation when receiving SIGNINT is executed after the block is released.

$ gcc -o block_sig block_sig.c
$ ./block_sig
Block the SIGINT for 5 sec
^C^C^C^C^C^C^C^C^C^C                <--- Ctrl +Does not respond when C is pressed
Passed 5 sec, unblock the SIGINT    <---Since it is terminated by SIGINT, after that"Done !"Is no output

7. Check the signal status

You can check the status such as signal block (pending) and ignore from / proc / <PID> / status.

$ cat /proc/29498/status
Name:	bash
--- snip ---
SigQ:	0/29305                //A queued signal to the real UID of this process/Upper limit of queued signal
SigPnd:	0000000000000000     //Specific process(thread)Number of pending signals addressed
ShdPnd:	0000000000000000     //Number of pending signals destined for the entire thread group
SigBlk:	0000000000010000     //Signal to be blocked(Bit mask value)
SigIgn:	0000000000380004     //Ignored signal(Bit mask value)
SigCgt:	000000004b817efb     //Signal waiting to be captured(Bit mask value)
--- snip ---

Of these, SigBlk, SigIgn, and SigCgt are a little confusing to read because they are managed by a mask value called ** signal set ** to represent multiple signals together. For example, SigIgn (0000000000380004) has the following meaning (convert hexadecimal to binary, determine the corresponding signal from position 1): This means that SigIgn (ignored signal) with PID 29498 means four signals corresponding to 3, 20, 21, 22.

0000000000380004 (hex) -> 1110000000000000000100 (bit)Signal name number
                          |||                *---------> SIGQUIT (03)
                          ||*--------------------------> SIGTSTP (20)
                          |*---------------------------> SIGTTIN (21)
                          *----------------------------> SIGTTOU (22)

You can also check the same information with ps s.

$ ps s
  UID   PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
 1000 29498 0000000000000000 0000000000010000 0000000000380004 000000004b817efb Ss   pts/0      0:00 /bin/bash
 1000 29517 0000000000000000 0000000000010000 0000000000380004 000000004b817efb Ss   pts/1      0:00 /bin/bash
 1000 29572 0000000000000000 0000000000000000 0000000000003000 00000001ef804eff S+   pts/0      0:00 vim kernel/signal.c
 1000 29581 0000000000000000 0000000000000000 0000000000000000 0000000000000000 S    pts/1      0:00 sleep 100
 1000 29588 0000000000000000 0000000000000000 0000000000000000 00000001f3d1fef9 R+   pts/1      0:00 ps s

■ Kernel (v5.5)

From here on, it's from a kernel perspective. Let's take a look at the flow from sending a signal to processing the signal action.

First, let's look at the data structure. A data structure is a structure that appears in the kernel implementation. Many structures are involved in signals, so it's helpful to know them first to help your code understand.

Next, let's look at the kernel implementation [^ kernel_ver]. Signals have a stepwise processing configuration in the order of signal generation (first step) and signal delivery (second step) as shown below, so let's take a look at each.

--Signal generation (1st stage) ... Record the signal transmission in the data structure of the destination process (hold the signal) --Signal delivery (second stage) ... Perform signal action in response to signal

[^ kernel_ver]: As mentioned in the title, the kernel version uses v5.5, which is the latest at the time of investigation.

1. Data structure

Signals are associated with many structures such as task_struct, signal_struct, and sighand_struct, as follows:

--task_struct (process descriptor) -signal_struct -sighand_struct (Signal Handler Descriptor)

I think it's very easy to get confused, so I've summarized the parts that I personally think are important for each purpose as follows.

Flag indicating a pending signal

Signal generation puts the process that receives the signal in signal hold (waiting for signal delivery) until it is processed by signal delivery. This state is indicated by the flag TIF_SIGPENDING and is recorded in the thread flags (flags).

all_signal-thread_flag.jpg

Queue for signal hold (for specific processes, for entire thread groups)

The received signal is managed by the sigqueue structure and has some related information (* 1). They are queued for pending (attached to the sigpending list). In doing so, one of two queues (for a specific process or for the entire thread group) is used, depending on where the signal is sent.

Also, the held signal is recorded in signal. You can find this value in / proc / <PID> / status (see" Signal Status Check "above and" Bonus "see below).

--For a specific process

![all_signal-sig_pending.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283234/aea41c44-8e3c-873e-5ecc-35ed45d56717.jpeg)

--For the entire thread group

![all_signal-sig_share_pending.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283234/13ad925f-3ba5-d410-0e47-da8a251b2ea3.jpeg)

(* 1) Some information refers to the members of the following kernel_siginfo structure.

member meaning
si_signo Signal number
si_code Code indicating the source of the signal(※2)
si_errno Error code of the instruction that caused the signal to occur
__sifilelds Si depending on the condition because of union_pid (Destination pid)、si_uid (Destination uid)Change to

(* 2) The following values are entered in the code that indicates the source of the signal.

Excerpt from include / uapi / asm-generic / siginfo.h

Definition name value meaning
SI_USER 0 kill()、sigsend()、raise()Signal transmission by
SI_KERNEL 0x80 Signaling by kernel
SI_QUEUE -1 sigqueue()Signal transmission by
SI_TIMER -2 Signal transmission over time of POSIX timer
SI_MESGQ -3 Signal transmission due to change of state of POSIX message queue
SI_ASYNCIO -4 Asynchronous I/O (AIO)Signal transmission upon completion
SI_SIGIO -5 Signal transmission by SIGIO queuing
SI_TKILL -6 tkill()、tgkill()Signal transmission by
SI_DETHREAD -7 sent by execve() killing subsidiary threads [^si_dethread]
SI_ASYNCNL -60 getaddrinfo_a()Signal transmission by completing name search in

The above values are common to all signals. Depending on the particular signal, another value may be entered as follows [^ 2 sigaction].

Signal name si_Value to enter code
SIGBUG BUS_*
SIGCHLD CLD_*
SIGFPE FPE_*
SIGILL ILL_*
SIGPOLL/SIGIO POLL_*
SIGSEGV SEGV_*
SIGTRAP TRAP_*
SIGSYS SYS_SECCOMP

[^ signalset]: For the signal set, refer to "Signal status check (signal set)" above.

[^ si_dethread]: man 2 execve I checked the kernel source etc., but I could not find any information beyond this original text and could not understand the meaning well, so I will describe it as it is.

Signal block information

Signal numbers blocked by sigprocmask () etc. are recorded in the block information (blocked, readl_blocked [^ real_blocked]). You can find this value in / proc / <PID> / status (see" Signal Status Check "above and" Bonus "see below).

all_signal-sig_block.jpg

[^ real_blocked]: real_blocked seems to be set when using the rt_sigtimedwait (), rt_sigtimedwait_time32 (), rt_sigtimedwait_time64 () system call.

Information related to signal actions

If you change the signal action settings with sigaction () etc., some information (*) is stored in the signal handler descriptor (sighand_struct). Also, the signal handler descriptor action [x] is the signal number (_NSIG) ) Since it is an array (action [signal number-1]), it is set for each signal.

all_signal-sig_handle.jpg

(*) Some information refers to the members of the following sigaction structure.

member meaning
sa_flags SA showing how to use signals_*flag[^2sigaction]
sa_handler SIG_IGN、SIG_Types of signal actions such as DFL, pointers to signal handlers, etc.
sa_mask Block when executing handler(Prohibit reception)signal

2. Implementation

Let's take a look at the flow from sending a signal to processing the signal action (processing when kill -TERM 1234 is executed).

From now on, we will look at each of "Signal generation" and "Signal delivery" separately (the "..." part is omitted).

Signal generation

The main task at this stage is to record in the data structure of the destination process that "a signal has been sent (waiting for signal delivery)" in the data structure of the destination process and notify it (such as standard operation). Signal actions are handled by signal delivery).

As a summary of the code reading that follows, the overall picture of the processing in signal generation is illustrated. Since there are many function calls, in code reading, we will start with __send_signal (), which is the most important function (core function).

all_signal-signal generate.jpg

__send_signal()

This function decides whether to deliver the transmitted signal (prepare_signal ()) and puts the signal on the hold queue (__sigqueue_alloc (), list_add_tail ()), which records the number of the hold signal ( sigaddset ()). Also, when delivering, set TIF_SIGPENDING in the thread flag of the process descriptor to wake up the deliverable process (complete_signal ()).

See below for details.

kernel/signal.c (L1065)

1065 static int __send_signal(int sig, struct kernel_siginfo *info, struct task_struct *t,
1066 			enum pid_type type, bool force)
1067 {
1068 	struct sigpending *pending;
1069 	struct sigqueue *q;
1070 	int override_rlimit;
1071 	int ret = 0, result;
.... 
1076 	if (!prepare_signal(sig, t, force))
1077 		goto ret;

Line 1076 prepare_signal () checks if the signal needs to be delivered To do. For example, if the signal handler (t-> sighand-> action [sig -1] .sa.sa_handler) has SIG_IGN set, or if the standard behavior is an Ign signal, it does not need to be delivered. So jump to the ret label. If the signal is blocked, you need to hold the signal, so do the rest.

It also performs the following processing for the Stop signal and SIGCONT.

--Stop signal for standard operation

Removes SIGCONT from the signal hold queue (*).

Remove all stop signals from the signal hold queue (*), and use wake_up_state () to wake up the thread in the __TASK_STOPPED state (restart the process).

If the process (thread group) is in the process of ending (SIGNAL_STOP_STOPPED), it is considered that the process is completed, and SIGNAL_CLD_CONTINUED and SIGNAL_STOP_CONTINUED are set in the flags of the process descriptor. If group_stop_count is counted even if it is not in the process of termination, it means that the process of termination has already been completed (probably), so set SIGNAL_CLD_STOPPED and SIGNAL_STOP_CONTINUED in the flags of the process descriptor.

(*) As explained in "Data structure", there are two types of signal hold queues: "for a specific process (t-> pending)" and "for the entire thread group (t-> signal-> shared_pending)". there is. For now, we'll remove the appropriate signal from both queues.

1078 
1079 	pending = (type != PIDTYPE_PID) ? &t->signal->shared_pending : &t->pending;

Line 1079 determines whether to use the "for a specific process" or "for the entire thread group" signal hold queue for pending. This time the kill command passes PIDTYPE_TGID to type, so the pending is used for the entire thread group.

1080 	/*
1081 	 * Short-circuit ignored signals and support queuing
1082 	 * exactly one non-rt signal, so that we can get more
1083 	 * detailed information about the cause of the signal.
1084 	 */
1085 	result = TRACE_SIGNAL_ALREADY_PENDING;
1086 	if (legacy_queue(pending, sig))
1087 		goto ret;

In line 1086 legacy_queue (), sig is a" standard signal "and" already a signal. If it is in the hold queue, it jumps to the ret label (a standard signal can receive more than one identical signal, but only one).

1088 
1089 	result = TRACE_SIGNAL_DELIVERED;
1090 	/*
1091 	 * Skip useless siginfo allocation for SIGKILL and kernel threads.
1092 	 */
1093 	if ((sig == SIGKILL) || (t->flags & PF_KTHREAD))
1094 		goto out_set;

As commented, if the signal is SIGKILL ** OR ** destination is a kernel thread (PF_KTHREAD flag), it jumps to the ʻout_set` label and does not perform subsequent signal hold queue registration processing.

1096 	/*
1097 	 * Real-time signals must be queued if sent by sigqueue, or
1098 	 * some other real-time mechanism.  It is implementation
1099 	 * defined whether kill() does so.  We attempt to do so, on
1100 	 * the principle of least surprise, but since kill is not
1101 	 * allowed to fail with EAGAIN when low on memory we just
1102 	 * make sure at least one signal gets delivered and don't
1103 	 * pass on the info struct.
1104 	 */
1105 	if (sig < SIGRTMIN)
1106 		override_rlimit = (is_si_special(info) || info->si_code >= 0);
1107 	else
1108 		override_rlimit = 0;
1109

Since SIGRTMIN on line 1105 is 32, processing branches depending on whether sig is a standard signal.

In the true root (line 1106) is_si_special (), ʻinfo is SEND_SIG_NOINFO (in user mode). Returns true if the process sent a signal) or SEND_SIGPRIV (a signal was sent from the kernel). It also returns true if ʻinfo-> si_code> = 0 (SI_USER or SI_KERNEL) on the right.

Note that this time, SI_USER is set in ʻinfo-> si_code by executing the kill command, so ʻoverride_rlimit is filled with true.

1110 	q = __sigqueue_alloc(sig, t, GFP_ATOMIC, override_rlimit);
1111 	if (q) {
1112 		list_add_tail(&q->list, &pending->list);
1113 		switch ((unsigned long) info) {
1114 		case (unsigned long) SEND_SIG_NOINFO:
1115 			clear_siginfo(&q->info);
1116 			q->info.si_signo = sig;
1117 			q->info.si_errno = 0;
1118 			q->info.si_code = SI_USER;
1119 			q->info.si_pid = task_tgid_nr_ns(current,
1120 							task_active_pid_ns(t));
1121 			rcu_read_lock();
1122 			q->info.si_uid =
1123 				from_kuid_munged(task_cred_xxx(t, user_ns),
1124 						 current_uid());
1125 			rcu_read_unlock();
1126 			break;
1127 		case (unsigned long) SEND_SIG_PRIV:
1128 			clear_siginfo(&q->info);
1129 			q->info.si_signo = sig;
1130 			q->info.si_errno = 0;
1131 			q->info.si_code = SI_KERNEL;
1132 			q->info.si_pid = 0;
1133 			q->info.si_uid = 0;
1134 			break;
1135 		default:
1136 			copy_siginfo(&q->info, info);
1137 			break;
1138 		}
....

In line 1110, __sigqueue_alloc () has a new sigqueue type q for signal management. Attempts to allocate. The internal processing of this function checks whether the number of signals held by the owner (user) of the destination process exceeds the upper limit, and if not, attempts to allocate. However, if ʻoverride_rlimit` is true, it will try to allocate without checking the upper limit.

Lines 1112 to 1138 are the processing when the allocation is successful.

In line 1112 list_add_tail (), the q is allocated to the signal hold queue. Is registered (connected to the list), and in the processing from the 1113th line onward, the signal information is stored in q according to the argument ʻinfo`.

1157 out_set:
....
1159 	sigaddset(&pending->signal, sig);
....

Add the current signal number to the signal set in the signal hold queue (see Signal Status and Data Structures above).

1175 	complete_signal(sig, t, type);
1176 ret:
1177 	trace_signal_generate(sig, info, t, type != PIDTYPE_PID, result);
1178 	return ret;
1179 }

The complete_signal () on line 1175 will be discussed later.

Line 1177 trace_signal_generate () is a tracepoint named signal_generate in the TRACE_EVENT macro. Seems to be creating. It is used (reported) to output kernel trace information such as:

kill-5371  [003] 1058202.036613: signal_generate:      sig=15 errno=0 code=0 comm=sleep pid 5359 grp=1  res=0

complete_signal()

This function is called when you decide to deliver the transmitted signal with __send_signal ().

Then use this function to find the process that is ready for signal delivery. If found, set TIF_SIGPENDING in the thread flag of the corresponding process and wake up (notify).

See below for details.

kernel/signal.c (L984)

 984 static void complete_signal(int sig, struct task_struct *p, enum pid_type type)
 985 {
 986 	struct signal_struct *signal = p->signal;
 987 	struct task_struct *t;
 988 
 989 	/*
 990 	 * Now find a thread we can wake up to take the signal off the queue.
 991 	 *
 992 	 * If the main thread wants the signal, it gets first crack.
 993 	 * Probably the least surprising to the average bear.
 994 	 */
 995 	if (wants_signal(sig, p))
 996 		t = p;
 ...

Line 995 wants_signal () allows signal delivery (ready) with the following pattern: Look for a process.

--Signal is not blocked (SIGKILL, SIGSTOP cannot be blocked) ** AND ** --Process is not terminating (p-> flags does not have PF_ EXITING) ** AND ** --Signal (sig) is SIGKILL (force without looking at process state or flag presence)

--Signal is not blocked (SIGKILL, SIGSTOP cannot be blocked) ** AND ** --Process is not terminating (p-> flags does not have PF_ EXITING) ** AND ** --The process state (p-> state) is not paused (__TASK_STOPPED) or stopped by a debugger (__TASK_TRACED) ** AND ** --Process is running on CPU (current process) ** OR ** Process thread flags (p-> thread_info-> flags) do not have TIF_SIGPENDING

1021 	/*
1022 	 * Found a killable thread.  If the signal will be fatal,
1023 	 * then start taking the whole group down immediately.
1024 	 */
1025 	if (sig_fatal(p, sig) &&
1026 	    !(signal->flags & SIGNAL_GROUP_EXIT) &&
1027 	    !sigismember(&t->real_blocked, sig) &&
1028 	    (sig == SIGKILL || !p->ptrace)) {

As you can see in the comments, look for a killable thread. If all of the following conditions are met, the processing of lines 1039 to 1042 is performed.

-sig_fatal () is true (*) ** AND ** --Thread group is not ending (SIGNAL_GROUP_EXIT is a flag that "thread group is ending") ** AND ** --Signals are not blocked (sigismember () is t-> real_blocked (blocked) Info) True if sig is included) ** AND ** --When the signal does not have the SIGKILL ** OR ** ptrace related flag (p-> ptrace)

(*) Sig_fatal () returns true in the following two patterns.

--Signal is standard signal ** AND ** --Standard behavior is not an Ign / Stop signal ** AND ** --Signal action is SIG_DFL

--The signal is a real-time signal ** AND ** --Signal action is SIG_DFL

1029 		/*
1030 		 * This signal will be fatal to the whole group.
1031 		 */
1032 		if (!sig_kernel_coredump(sig)) {

sig_kernel_coredump () returns true when the standard behavior is not a core dump (Core) signal.

1033 			/*
1034 			 * Start a group exit and wake everybody up.
1035 			 * This way we don't have other threads
1036 			 * running and doing things after a slower
1037 			 * thread has the fatal signal pending.
1038 			 */
1039 			signal->flags = SIGNAL_GROUP_EXIT;
1040 			signal->group_exit_code = sig;
1041 			signal->group_stop_count = 0;
1042 			t = p;
1043 			do {
1044 				task_clear_jobctl_pending(t, JOBCTL_PENDING_MASK);
1045 				sigaddset(&t->pending.signal, SIGKILL);
1046 				signal_wake_up(t, 1);
1047 			} while_each_thread(p, t);
1048 			return;
1049 		}
1050 	}

After processing lines 1039 to 1042, the following processing is looped until the do-while scans in the thread group.

--Clear the pending job control flag (t-> jobctl) with task_clear_jobctl_pending () on line 1044. --Add SIGKILL (number) to the signal set of the signal hold queue with sigaddset () on line 1045. --In signal_wake_up () on line 1046, set TIF_SIGPENDING to the thread flag (t-> flags) and wake up (notify) the process of TASK_WAKEKILL (can receive important signals such as SIGKILL) or TASK_INTERRUPTIBLE (waiting for some event).

When you exit the loop, the processing in this function ends.

1052 	/*
1053 	 * The signal is already in the shared-pending queue.
1054 	 * Tell the chosen thread to wake up and dequeue it.
1055 	 */
1056 	signal_wake_up(t, sig == SIGKILL);
1057 	return;
1058 }

If you can reach this point, you can use the thread flag (t- Set TIF_SIGPENDING in> flags) to wake up the process in TASK_INTERRUPTIBLE state (notify hold signal). It also wakes up processes in the TASK_WAKEKILL state if the signal is SIGKILL.

This is the end of the "signal generation" process. Next, let's look at "Signal Delivery".

Signal delivery

The main task at this stage is to perform signal actions such as signal handlers and standard behavior. However, this is only possible if the process has a pending signal (with the TIF_SIGPENDING flag described in "Signal Generation" above).

As a summary of the code reading that follows, the overall picture of the processing in signal delivery is illustrated. Note that some code for signal delivery depends on the architecture, but here we will look at the code for x86 (under arch / x86).

all_signal-signal delivery.jpg

exit_to_usermode_loop()

This function is called every time you return from kernel mode to user mode (interrupt / exception handler, after system call processing, etc.) to check the thread flag. At that time, if there is TIF_SIGPENDING, do_signal () is called (line 160).

See below for details.

arch/x86/entry/common.c (L136)

136 static void exit_to_usermode_loop(struct pt_regs *regs, u32 cached_flags)
137 {
138 	/*
139 	 * In order to return to user mode, we need to have IRQs off with
140 	 * none of EXIT_TO_USERMODE_LOOP_FLAGS set.  Several of these flags
141 	 * can be set at any time on preemptible kernels if we have IRQs on,
142 	 * so we need to loop.  Disabling preemption wouldn't help: doing the
143 	 * work to clear some of the flags can sleep.
144 	 */
145 	while (true) {
146 		/* We have work to do. */
147 		local_irq_enable();
... 
158 		/* deal with pending signal delivery */
159 		if (cached_flags & _TIF_SIGPENDING)
160 			do_signal(regs);
... 
171 		/* Disable IRQs and retry */
172 		local_irq_disable();
173 
174 		cached_flags = READ_ONCE(current_thread_info()->flags);
175 
176 		if (!(cached_flags & EXIT_TO_USERMODE_LOOP_FLAGS))
177 			break;
178 	}
179 }

do_signal()

This function performs standard actions (get_signal ()) and signal handler execution (handle_signal ()). It also re-executes system calls and restores signal block information that has been temporarily rewritten by a specific system call, if necessary.

See below for details.

arch/x86/kernel/signal.c (L806)

806 /*
807  * Note that 'init' is a special process: it doesn't get signals it doesn't
808  * want to handle. Thus you cannot kill init even with a SIGKILL even by
809  * mistake.
810  */
811 void do_signal(struct pt_regs *regs)
812 {
813 	struct ksignal ksig;
814 
815 	if (get_signal(&ksig)) {
816 		/* Whee! Actually deliver the signal.  */
817 		handle_signal(&ksig, regs);
818 		return;
819 	}
820

Get_signal () on line 815 performs standard operation and retrieves the signal from the hold queue. If the result is true (if retrieved), execute handle_signal () to execute the signal handler. If it is false (could not be retrieved), the following processing is performed (get_signal () and handle_signal () are important functions, so details will be described later).

821 	/* Did we come from a system call? */
822 	if (syscall_get_nr(current, regs) >= 0) {
823 		/* Restart the system call - no handlers present */
824 		switch (syscall_get_error(current, regs)) {
825 		case -ERESTARTNOHAND:
826 		case -ERESTARTSYS:
827 		case -ERESTARTNOINTR:
828 			regs->ax = regs->orig_ax;
829 			regs->ip -= 2;
830 			break;
831 
832 		case -ERESTART_RESTARTBLOCK:
833 			regs->ax = get_nr_restart_syscall(regs);
834 			regs->ip -= 2;
835 			break;
836 		}
837 	}

This is the process related to the re-execution of the system call (this is because if a signal is received while the system call is being processed, an error may be thrown and the process may be interrupted).

Line 822 syscall_get_nr () is the user mode register ( Get the system call number from regs). If it is greater than or equal to 0, then line 824 syscall_get_erro () It gets the error number and the kernel reissues the system call according to the error number.

The re-execution of system calls was described in detail in the article "Re-execution of Linux system calls". Click here for details such as the meaning of regs-> ip-= 2.

838 
839 	/*
840 	 * If there's no signal to deliver, we just put the saved sigmask
841 	 * back.
842 	 */
843 	restore_saved_sigmask();
844 }

In restore_saved_sigmask (), by system calls such as ppoll, pselect, epoll, sigsuspend Restores the temporarily rewritten signal block information (task_struct-> blocked) from the previously saved signal block information (task_struct-> saved_sigmask).

get_signal()

This function takes a signal from the signal hold queue and performs standard actions in response to the signal.

See below for details.

kernel/signal.c (L2521)

2521 bool get_signal(struct ksignal *ksig)
2522 {
2523 	struct sighand_struct *sighand = current->sighand;
2524 	struct signal_struct *signal = current->signal;
2525 	int signr;
.... 
2540 relock:
....
2542 	/*
2543 	 * Every stopped thread goes here after wakeup. Check to see if
2544 	 * we should notify the parent, prepare_signal(SIGCONT) encodes
2545 	 * the CLD_ si_code into SIGNAL_CLD_MASK bits.
2546 	 */
2547 	if (unlikely(signal->flags & SIGNAL_CLD_MASK)) {
2548 		int why;
2549 
2550 		if (signal->flags & SIGNAL_CLD_CONTINUED)
2551 			why = CLD_CONTINUED;
2552 		else
2553 			why = CLD_STOPPED;
2554 
2555 		signal->flags &= ~SIGNAL_CLD_MASK;
2556 
2557 		spin_unlock_irq(&sighand->siglock);
2558 
2559 		/*
2560 		 * Notify the parent that we're continuing.  This event is
2561 		 * always per-process and doesn't make whole lot of sense
2562 		 * for ptracers, who shouldn't consume the state via
2563 		 * wait(2) either, but, for backward compatibility, notify
2564 		 * the ptracer of the group leader too unless it's gonna be
2565 		 * a duplicate.
2566 		 */
2567 		read_lock(&tasklist_lock);
2568 		do_notify_parent_cldstop(current, false, why);
2569 
2570 		if (ptrace_reparented(current->group_leader))
2571 			do_notify_parent_cldstop(current->group_leader,
2572 						true, why);
2573 		read_unlock(&tasklist_lock);
2574 
2575 		goto relock;
2576 	}
....

As mentioned in the comment on line 2543, the flag on line 2547 (SIGNAL_CLD_MASK == SIGNAL_CLD_STOPPED or SIGNAL_CLD_CONTINUED) is used when processing SIGCONT with prepare_signal () called by signal generation (__send_signal ()). Will be set. If this flag is set, do_notify_parent_cldstop () sends a signal (SIGCHLD) to the parent.

2588 	for (;;) {
2589 		struct k_sigaction *ka;
....
2616 		/*
2617 		 * Signals generated by the execution of an instruction
2618 		 * need to be delivered before any other pending signals
2619 		 * so that the instruction pointer in the signal stack
2620 		 * frame points to the faulting instruction.
2621 		 */
2622 		signr = dequeue_synchronous_signal(&ksig->info);
2623 		if (!signr)
2624 			signr = dequeue_signal(current, &current->blocked, &ksig->info);
2625 
2626 		if (!signr)
2627 			break; /* will return 0 */
.... 

Line 2622dequeue_synchronous_signal()Synchronizedsignalwith(SIG[SEGV|BUS|ILL|TRAP|FPE|SYS])Fromthequeue.Ifnot,line2624dequeue_signal()Dequeues an asynchronous signal with. If not, break out of the loop and end processing with this function.

2635 		ka = &sighand->action[signr-1];
2636 
2637 		/* Trace actually delivered signals. */
2638 		trace_signal_deliver(signr, &ksig->info, ka);

Line 2638 trace_signal_deliver () is a TRACE_EVENT macro named signal_deliver. It seems to create a trace point. It is used (reported) to output kernel trace information such as: Looking at the comment from the definition source, it seems that the signal was delivered when we got here.

trace-cmd-17636 [001] 607498.455844: signal_deliver:       sig=2 errno=0 code=128 sa_handler=5567b90d1540 sa_flags=1400000

The following is processing related to signal actions such as signal ignore, signal handler, and standard behavior. Branches according to the contents of ka (line 2635).

2639 
2640 		if (ka->sa.sa_handler == SIG_IGN) /* Do nothing.  */
2641 			continue;

If the signal action is set to ignore (SIG_IGN) in the C language API sigaction () etc., nothing is done.

2642 		if (ka->sa.sa_handler != SIG_DFL) {
2643 			/* Run the handler.  */
2644 			ksig->ka = *ka;
2645 
2646 			if (ka->sa.sa_flags & SA_ONESHOT)
2647 				ka->sa.sa_handler = SIG_DFL;
2648 
2649 			break; /* will return non-zero "signr" value */
2650 		}

Line 2642 puts the address of ka in ksig-> ka on line 2644 if it is not SIG_DFL (that is, if you have set a signal handler). This is used when setting to the register for user processing with handle_signal () after exiting get_signal ().

SA_ONESHOT (a flag that can be set with sigaction ()) on line 2646 means that the signal action is returned to the default (SIG_DFL) after the signal handler is executed. In other words, the signal handler executes only the first time a signal is received.

Finally, break breaks the loop and finishes the process with this function. In this case, signr should contain the retrieved signal number, so handle_signal () described below is executed.

2651 
2652 		/*
2653 		 * Now we are doing the default action for this signal.
2654 		 */
2655 		if (sig_kernel_ignore(signr)) /* Default is nothing. */
2656 			continue;
....

From here, it is the process related to the execution of standard operation.

Line 2655 sig_kernel_ignore () ignores the standard behavior of signr (Ign) Returns true. In that case, do nothing.

2671 
2672 		if (sig_kernel_stop(signr)) {
2673 			/*
2674 			 * The default action is to stop all threads in
2675 			 * the thread group.  The job control signals
2676 			 * do nothing in an orphaned pgrp, but SIGSTOP
2677 			 * always works.  Note that siglock needs to be
2678 			 * dropped during the call to is_orphaned_pgrp()
2679 			 * because of lock ordering with tasklist_lock.
2680 			 * This allows an intervening SIGCONT to be posted.
2681 			 * We need to check for that and bail out if necessary.
2682 			 */
2683 			if (signr != SIGSTOP) {
2684 				spin_unlock_irq(&sighand->siglock);
2685 
2686 				/* signals can be posted during this window */
2687 
2688 				if (is_current_pgrp_orphaned())
2689 					goto relock;
2690 
2691 				spin_lock_irq(&sighand->siglock);
2692 			}
2693 
2694 			if (likely(do_signal_stop(ksig->info.si_signo))) {
2695 				/* It released the siglock.  */
2696 				goto relock;
2697 			}
2698 
2699 			/*
2700 			 * We didn't actually stop, due to a race
2701 			 * with SIGCONT or something like that.
2702 			 */
2703 			continue;
2704 		}

In line 2672, sig_kernel_stop (), the standard operation of signr is process pause ( Returns true if the signal is Stop). If true, on line 2694 do_signal_stop (), set SIGNAL_STOP_STOPPED to the process descriptor flags. Stand up and suspend the process (TASK_STOPPED).

However, if the signal is other than SIGSTOP and the process group is orphan (parentless), do_signal_stop () is not executed (does not pause).

2705 
2706 	fatal:
2707 		spin_unlock_irq(&sighand->siglock);
.... 
2711 		/*
2712 		 * Anything else is fatal, maybe with a core dump.
2713 		 */
2714 		current->flags |= PF_SIGNALED;
2715 
2716 		if (sig_kernel_coredump(signr)) {
2717 			if (print_fatal_signals)
2718 				print_fatal_signal(ksig->info.si_signo);
2719 			proc_coredump_connector(current);
2720 			/*
2721 			 * If it was able to dump core, this kills all
2722 			 * other threads in the group and synchronizes with
2723 			 * their demise.  If we lost the race with another
2724 			 * thread getting here, it set group_exit_code
2725 			 * first and our do_group_exit call below will use
2726 			 * that value and ignore the one we pass it.
2727 			 */
2728 			do_coredump(&ksig->info);
2729 		}
....

In line 2716 sig_kernel_coredump (), the standard behavior of signr is core dump (Core). Returns true. If true, set SIGNAL_GROUP_COREDUMP in the signal descriptor flags (flags) on line 2728 do_coredump (). , Performs core dump generation processing.

2731 		/*
2732 		 * Death signals, no core dump.
2733 		 */
2734 		do_group_exit(ksig->info.si_signo);
2735 		/* NOTREACHED */
2736 	}

If the standard behavior of signr is process termination (Term) or core dump (Core), line 2724 [do_group_exit ()](https://github.com/torvalds/linux/blob/v5.5/kernel/ Set SIGNAL_GROUP_EXIT in the signal descriptor flags (flags) in exit.c # L876) to terminate the process (thread group).

Note that } on line 2736 is a pair of for (;;) { on line 2588. You can break this loop with a break on line 2627 or a break on line 2649. The former is when there is nothing to retrieve from the signal hold queue, and the latter is when the signal handler is set in the signal action (sa_handler).

2737 	spin_unlock_irq(&sighand->siglock);
2738 
2739 	ksig->sig = signr;
2740 	return ksig->sig > 0;
2741 }

Determines if the last signal number dequeued is greater than 0. If it is large, it means that the signal has been retrieved and returns true, and proceeds to the next handle_signal ().

handle_signal()

This function sets the stack frame in user mode for re-execution of system calls (if necessary) and signal handler execution.

arch/x86/kernel/signal.c (L71)

710 static void
711 handle_signal(struct ksignal *ksig, struct pt_regs *regs)
712 {
713 	bool stepping, failed;
714 	struct fpu *fpu = &current->thread.fpu;
715
... 
719 	/* Are we from a system call? */
720 	if (syscall_get_nr(current, regs) >= 0) {
721 		/* If so, check system call restarting.. */
722 		switch (syscall_get_error(current, regs)) {
723 		case -ERESTART_RESTARTBLOCK:
724 		case -ERESTARTNOHAND:
725 			regs->ax = -EINTR;
726 			break;
727 
728 		case -ERESTARTSYS:
729 			if (!(ksig->ka.sa.sa_flags & SA_RESTART)) {
730 				regs->ax = -EINTR;
731 				break;
732 			}
733 		/* fallthrough */
734 		case -ERESTARTNOINTR:
735 			regs->ax = regs->orig_ax;
736 			regs->ip -= 2;
737 			break;
738 		}
739 	}
740
...

Although it was also issued by do_signal () [^ restart_syscall], there is also processing related to system call re-execution here. However, there are only two patterns in which the kernel re-executes system calls:

--If the error is -ERESTARTSYS (line 728) and sa_flags has SA_RESTART (a flag that can be set with sigaction ()). --If the error is -ERESTARTNOINTR (line 734) on line 734

Otherwise, the kernel will not re-execute the system call and will set regs-> ax to -EINTR (error that the function call was interrupted). From this error, the user (user program condition) decides whether to re-execute the system call.

[^ restart_syscall]: The system call re-execution with do_signal () is the path (when get_signal () is false) that is passed when handle_signal () is not executed, so it does not overlap with the system call re-execution process here. ..

750 	failed = (setup_rt_frame(ksig, regs) < 0);
...
751 	if (!failed) {
752 		/*
753 		 * Clear the direction flag as per the ABI for function entry.
754 		 *
755 		 * Clear RF when entering the signal handler, because
756 		 * it might disable possible debug exception from the
757 		 * signal handler.
758 		 *
759 		 * Clear TF for the case when it wasn't set by debugger to
760 		 * avoid the recursive send_sigtrap() in SIGTRAP handler.
761 		 */
762 		regs->flags &= ~(X86_EFLAGS_DF|X86_EFLAGS_RF|X86_EFLAGS_TF);
763 		/*
764 		 * Ensure the signal handler starts with the new fpu state.
765 		 */
766 		fpu__clear(fpu);
767 	}
768 	signal_setup_done(failed, ksig, stepping);
769 }

In line 750 setup_rt_frame (), in user mode for signal handler execution Set the stack frame for. The stack frame contains the information required to execute the signal handler (signal number, siginfo structure information, and information for calling the rt_sigreturn () system call from the user mode process. Rt_sigreturn () is after the signal handler is executed. Required to return to kernel mode.

Also, if the setup_rt_frame () is set successfully, the flags (regs-> flags) and FPU state (floating point register) are cleared on lines 762 and 766 (for unknown reasons).

And in the last (line 768) signal_setup_done (), the setting success or failure in setup_rt_frame () The process branches.

--If successful: sigorsets () with current process block information (current-> blocked) and sigaction () with user Takes the logical sum (OR) of the block information (sigaction-> sa_mask) set by and sets it to the block information of the current process. Also, if the thread flag is set to TIF_SINGLESTEP (used by the debugger), then [ptrace_notify (SIGTRAP)](https://github.com/torvalds/linux/blob/v5.5/include/linux/tracehook. Execute h # L146).

--If it fails: Send SIGSEGV to the current process with force_sigsegv ().

This is the end of the "signal delivery" process.

3. Bonus

Signal-related code in / proc / <PID> / status

I searched the code to find out where the values such as SigPnd, ShdPnd, etc. in / proc / <PID> / status came from.

There was an answer in task_sig (). For example, SigBlk (blocked) contains p-> blocked on line 283.

fs/proc/array.c#L266 (L266)

266 static inline void task_sig(struct seq_file *m, struct task_struct *p)
267 {
268 	unsigned long flags;
269 	sigset_t pending, shpending, blocked, ignored, caught;
270 	int num_threads = 0;
271 	unsigned int qsize = 0;
272 	unsigned long qlim = 0;
273 
274 	sigemptyset(&pending);
275 	sigemptyset(&shpending);
276 	sigemptyset(&blocked);
277 	sigemptyset(&ignored);
278 	sigemptyset(&caught);
279 
280 	if (lock_task_sighand(p, &flags)) {
281 		pending = p->pending.signal;
282 		shpending = p->signal->shared_pending.signal;
283 		blocked = p->blocked;
284 		collect_sigign_sigcatch(p, &ignored, &caught);
285 		num_threads = get_nr_threads(p);
286 		rcu_read_lock();  /* FIXME: is this correct? */
287 		qsize = atomic_read(&__task_cred(p)->user->sigpending);
288 		rcu_read_unlock();
289 		qlim = task_rlimit(p, RLIMIT_SIGPENDING);
290 		unlock_task_sighand(p, &flags);
291 	}
292 
293 	seq_put_decimal_ull(m, "Threads:\t", num_threads);
294 	seq_put_decimal_ull(m, "\nSigQ:\t", qsize);
295 	seq_put_decimal_ull(m, "/", qlim);
296 
297 	/* render them all */
298 	render_sigset_t(m, "\nSigPnd:\t", &pending);
299 	render_sigset_t(m, "ShdPnd:\t", &shpending);
300 	render_sigset_t(m, "SigBlk:\t", &blocked);
301 	render_sigset_t(m, "SigIgn:\t", &ignored);
302 	render_sigset_t(m, "SigCgt:\t", &caught);
303 }

Why SIGKILL and SIGSTOP cannot block

I tried to find out in the code why SIGKILL and SIGSTOP couldn't block.

There was an answer to sys_rt_sigprocmask (), which is called at runtime sigprocmask () used for signal blocks. Removed SIGKILL and SIGSTOP from the newly set block information (new_set) in sigdelsetmask () on line 3025 (that is, setting to block SIGKILL and SIGSTOP does not actually set it).

kernel/signal.c (L3004)

3003 /**
3004  *  sys_rt_sigprocmask - change the list of currently blocked signals
3005  *  @how: whether to add, remove, or set signals
3006  *  @nset: stores pending signals
3007  *  @oset: previous value of signal mask if non-null
3008  *  @sigsetsize: size of sigset_t type
3009  */
3010 SYSCALL_DEFINE4(rt_sigprocmask, int, how, sigset_t __user *, nset,
3011 		sigset_t __user *, oset, size_t, sigsetsize)
3012 {
3013 	sigset_t old_set, new_set;
3014 	int error;
.... 
3022 	if (nset) {
3023 		if (copy_from_user(&new_set, nset, sizeof(sigset_t)))
3024 			return -EFAULT;
3025 		sigdelsetmask(&new_set, sigmask(SIGKILL)|sigmask(SIGSTOP));
3026 
3027 		error = sigprocmask(how, &new_set, NULL);
3028 		if (error)
3029 			return error;
3030 	}
.... 
3037 	return 0;
3038 }

You can also set the signal block with sa_mask in sigaction (), so let's take a look here as well.

In the case of sigaction (), the same processing was performed on lines 3967 and 3968 of sys_rt_sigaction ()-> do_sigaction ().

3949 int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)
3950 {
3951 	struct task_struct *p = current, *t;
3952 	struct k_sigaction *k;
3953 	sigset_t mask;
....
3966 	if (act) {
3967 		sigdelsetmask(&act->sa.sa_mask,
3968 			      sigmask(SIGKILL) | sigmask(SIGSTOP));
....
3988 	}
....
3991 	return 0;
3992 }

Ignore SIGKILL, SIGSTOP or why you can't set a signal handler

I also investigated the reason why SIGKILL and SIGSTOP cannot be ignored or changed from the code.

There was an answer on line 3955 of sys_rt_sigaction ()-> do_sigaction (), which is called when sigaction () is run. In the rightmost condition, ʻact` is a valid value and sig_kernel_only () is true. Returning an error (invalid argument) with -EINVAL on (if the signal is SIGKILL or SIGSTOP). That is, passing SIGKILL and SIGSTOP to sigaction () will fail with -EINVAL. In this case, of course, neither ignore (SIG_IGN) nor signal handler (user-defined function) can be set.

3949 int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)
3950 {
3951 	struct task_struct *p = current, *t;
3952 	struct k_sigaction *k;
3953 	sigset_t mask;
3954 
3955 	if (!valid_signal(sig) || sig < 1 || (act && sig_kernel_only(sig)))
3956 		return -EINVAL;
....
3966 	if (act) {
....
3969 		*k = *act;
3970 		/*
3971 		 * POSIX 3.3.1.3:
3972 		 *  "Setting a signal action to SIG_IGN for a signal that is
3973 		 *   pending shall cause the pending signal to be discarded,
3974 		 *   whether or not it is blocked."
3975 		 *
3976 		 *  "Setting a signal action to SIG_DFL for a signal that is
3977 		 *   pending and whose default action is to ignore the signal
3978 		 *   (for example, SIGCHLD), shall cause the pending signal to
3979 		 *   be discarded, whether or not it is blocked"
3980 		 */
3981 		if (sig_handler_ignored(sig_handler(p, sig), sig)) {
3982 			sigemptyset(&mask);
3983 			sigaddset(&mask, sig);
3984 			flush_sigqueue_mask(&mask, &p->signal->shared_pending);
3985 			for_each_thread(p, t)
3986 				flush_sigqueue_mask(&mask, &t->pending);
3987 		}
3988 	}
....
3991 	return 0;
3992 }

■ References

-Linux System Programming (Chapter 9 Signals) -Linux Programming Interface (Chapter 20 Signals: Basics, Chapter 21 Signals: Signal Handlers, Chapter 22 Signals: Applications) -Detailed Linux Kernel 3rd Edition (Chapter 11 Signal) -Linux Kernel Decoding Room (Chapter 8 Signal Processing)

Recommended Posts

Linux Signal Basics and Mechanisms (as of kernel v5.5)
[Must-see for beginners] Basics of Linux
[Linux] Learn the basics of shell commands
Linux basics
Linux basics
[Python] Chapter 02-01 Basics of Python programs (operations and variables)
Confirmation of basics of competition professionals ~ gcd and lcm ~