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)".
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.
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.
To make it easier to imagine, here are three examples of signal usage from the user's perspective.
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
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
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
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.
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].
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. ** **
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)
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).
[^ 5 core]: See man 5 core
for details.
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.
To change signal actions other than ** SIGKILL ** and ** SIGSTOP **, use APIs (C) such as the trap command and sigaction ().
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 () 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.
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].
[^ 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.
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
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
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.
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.
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).
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 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).
[^ real_blocked]: real_blocked seems to be set when using the rt_sigtimedwait (), rt_sigtimedwait_time32 (), rt_sigtimedwait_time64 () system call.
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.
(*) 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 |
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).
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).
__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.
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.
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".
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).
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.
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, ¤t->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 = ¤t->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.
/ 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.
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 }
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).
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 }
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 }
-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)
man 1 bash
man 2 setrlimit
man 2 seccomp
man 2 signal
man 2 sigaction
man 3 getaddrinfo_a
man 5 proc
man 7 signal
man 7 pipe
Recommended Posts