Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the Signal mechanism of Linux?

2025-04-08 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/01 Report--

This article mainly introduces the relevant knowledge of what the Signal mechanism of Linux is, the content is detailed and easy to understand, the operation is simple and fast, and it has a certain reference value. I believe you will gain something after reading this article on the Signal mechanism of Linux. Let's take a look at it.

Signal mechanism is a very common inter-process communication mechanism in Linux, and many people will not consider how to implement this mechanism when using it. Signal mechanism can be understood as a soft interrupt of a process, so it is relatively high in real-time.

Overview of signal

The name and number of the signal: each signal has a name and number, and these names begin with "SIG", such as "SIGIO", "SIGCHLD", and so on. The signal is defined in the signal.h header file, and the signal names are defined as positive integers. For the specific signal name, you can use kill-l to check the name and sequence number of the signal. The signal is numbered from 1, and there is no signal 0. Kill has a special application for signal 0.

The name of the signal

Signal processing: there are three methods of signal processing, namely: ignore, capture and default action

Ignore signals, most signals can be processed in this way, but there are two signals that cannot be ignored (SIGKILL and SIGSTOP, respectively). Because they provide the kernel and superusers with reliable methods for terminating and stopping processes, if ignored, the process becomes a process that no one can manage, obviously a scene capture signal that kernel designers do not want to see. You need to tell the kernel how to handle a certain kind of signal, to put it bluntly, write a signal handling function, and then tell the kernel that function. When the signal is generated, the user-defined function is called by the kernel to realize some kind of signal processing. The default action of the system, for each signal, the system corresponds to the default processing action, when the signal occurs, the system will automatically execute. However, for the system, most of the processing is rough, which is to kill the process directly. For specific signal default actions, you can use man 7 signal to view the specific definition of the system. Here, I will not expand in detail, need to check, you can check it yourself. You can also refer to the P251--P256 section of Advanced programming in the UNIX Environment (part III) for a detailed description of each signal.

Now that you have an overview of signals, how are signals used?

In fact, for the commonly used kill command is a signaling tool, kill 9 PID to kill the process. For example, I ran a top tool in the background, whose PID can be viewed through the ps command, and sent a signal to terminate the top process through kill 9. If you look at the signal number and name, you can see that 9 corresponds to 9) SIGKILL, which is the signal that kills the process. The following execution process actually performs the default action of signal 9-killing the process.

Kill kill process

For the signal, the greatest significance is not to kill the signal, but to achieve some means of asynchronous communication, so how to define the signal processing function?

Registration of signal processing functions

There is more than one way to register signal processing functions, which can be divided into entry version and advanced version

Entry version: function signal Advanced Edition: function sigaction signal processing transmission function

There is also more than one signal transmission function, which is also divided into entry version and advanced version 1. Entry: kill 2. Advanced version: sigqueue

Signal registration function-- entry version

Before you officially start to understand these two functions, you can think about what you need to deal with interrupts. According to our previous thinking, there are a variety of signal types that can be sent, and the processing of each signal may not be the same, so we definitely need to know what signal is happening. In addition, although we know what kind of signal the system is sending, it is also important that the system generates a signal. Who responds to it? If the system generates a SIGINT (interrupt signal) through ctrl+c, it is clear that not all programs end at the same time, then the signal must have a receiver. For a program that processes a signal, the receiver is itself.

At the beginning, let's take a look at the entry-level version of the signal registration function. Its function prototype is as follows: signal's function prototype

# include typedef void (* sighandler_t) (int); sighandler_t signal (int signum, sighandler_t handler)

According to the function prototype, we can see that it is composed of two parts, one is the real signal processing function, the other is the registration function. For the sighandler_t signal (int signum, sighandler_t handler); function, signum is obviously the number of the signal, and handler is the pointer to the interrupt function. Similarly, the prototype of the typedef void (* sighandler_t) (int); interrupt function has an argument of type int, which is obviously also the type of signal generated, making it easy to use a function to process multiple signals. Let's first take a look at a simple code example of signal registration.

# include#include#include / / typedef void (* sighandler_t) (int); voidhandler (int signum) {if (signum = = SIGIO) printf ("SIGIO signal:% d\ n", signum); else if (signum = = SIGUSR1) printf ("SIGUSR1 signal:% d\ n", signum); else printf ("error\ n");} intmain (void) {/ / sighandler_t signal (int signum, sighandler_t handler); signal (SIGIO, handler) Signal (SIGUSR1, handler); printf ("% d% d\ n", SIGIO, SIGUSR1); for (;;) {sleep (10000);} return 0;}

Let's first use the kill command to send a signal to the program we wrote earlier, and we'll talk about this command later.

Send a signal by kill command

The processing result of the signal received by the program

To sum up, we register a signal processing function through the signal function, registering two signals (SIGIO and SIGUSER1) respectively; then the main program has been "sleeping". Before sending a signal through the kill command, we need to look at the recipient, check the PID of the previously written program through the ps command, and send it through the kill function. Registered signals can be received normally using kill transmission, but if unregistered signals are sent, it will cause the application to terminate the process.

Then, you can set the signal processing function, signal processing there are two states, namely default processing and ignore, these two settings are very simple, only need to set handler to SIG_IGN (ignore signal) or SIG_DFL (default action).

There are two more issues that need to be explained:

When executing a program, the state of all signals is the default or ignored state of the system. Unless the calling exec process ignores some signals. The exec function changes the signals that were originally set to capture to the default action, and the state of the other signals does not change. two。 When a process invokes the fork function, the child process inherits the signal processing of the parent process.

The entry-level version of the signal registration is relatively simple, only need a registration and a processing function, then, let's see how to send a signal.

Signal transmission function-- entry version

The function prototype of kill

# include # include int kill (pid_t pid, int sig)

As I said before, signal processing requires a receiver, and obviously the sender must know who to send. According to the distance of the kill function, you can see that pid is the recipient's pid,sig is the type of signal sent. From the perspective of the prototype, it is easier to send a signal than to receive it, so let's go straight to the code. Show me the code!!!

# include#include # include#include int main (int argc, char** argv) {if (3! = argc) {printf ("[Arguments ERROR!]\ n"); printf ("\ tUsage:\ n"); printf ("\ t\ t% s\ n", argv [0]); return-1;} int pid = atoi (argv [1]); int sig = atoi (argv [2]) / / int kill (pid_t pid, int sig); if (pid > 0 & & sig > 0) {kill (pid, sig);} else {printf ("Target_PID or Signal_Number MUST bigger than 0!\ n");} return 0;} img

Send a signal

The result of the received signal

To sum up: according to the above results, we can see that the signal can basically be sent, although the signal name can not be sent directly, but through the number of the signal, we can send the signal to the program normally. It is also a preliminary realization of the signal transmission process.

About the kill function, there is one additional point to note. The above program limits that pid must be a positive integer greater than 0. In fact, the pid passed in by the kill function can be an integer less than or equal to 0. Pid > 0: the process that will send the pid pid = = 0: the signal will be sent to all processes that belong to the same process group as the sending process, and the sending process has permission to send signals to these processes. Pid

There are more topics about signals, such as whether signals can be accurately delivered to the target process. The answer is not necessarily, so there are reliable signals and unreliable signals.

Reliable and unreliable signals

Unreliable signal: the signal may be lost, and once the signal is lost, the process does not know that the signal is lost: it is also a blocking signal, and the system defaults to act or capture the signal when a blocking signal is sent, if the signal is sent, it will remain in a pending state until the process unblocks the signal, or changes the action of the signal to ignore. For the signal, the signal whose signal number is less than or equal to 31 is an unreliable signal, and the subsequent signal is a stuck signal, and the system will block the signal before it is delivered according to the signal queue.

The blocking and pending of the signal are managed by the status word of the signal, which manages the state of the signal bit by bit. Each signal has an independent blocking word, which specifies the set of signals that are currently blocked to reach the process.

Signal blocking status word (block), 1 for blocking, 0 for non-blocking; 1 for pending, 0 for signal arrival; they are each bit representing a signal.

How do blockages and suspensions work? For example, if a SIGINT signal is sent to a process, the kernel will first determine whether the signal blocking state word of the process is blocking state. If the signal is set to the blocking state, that is, the corresponding bit of the blocking state word is 1, then the corresponding bit of the signal pending status word (pending) will be set by the kernel to 1. If the signal blocking is released, that is, the blocking status word is set to 0, then the corresponding bit of the signal pending status word (pending) will be set to 0 by the kernel, indicating that the signal can arrive at this time, that is, it can receive the signal. Blocking status words users can read and write, pending status words users can only read, is set by the kernel to represent the signal delivery state. PS: the following is an additional note: only systems that support POSIX.1 real-time extension support queuing (that is, when the same signal is sent to a process multiple times in blocking state, it can be sent multiple times instead of once). The setting of the blocking status word of the process about the signal can be obtained or set through the int sigprocmask (int how, const sigset_t * set, sigset_t * oldset) function.

This function manages the signal through the data structure of the signal set, and the signal set can be managed by the following functions. Signal set operation function (status word representation)

# include int sigemptyset (sigset_t * set); / / initialize the signal set passed in the set and empty all signal int sigfillset (sigset_t * set); / / fill the signal set with 1 so that the set contains all the signal int sigaddset (sigset_t * set, int signum); / / the signal set corresponds to 1 int sigdelset (sigset_t * set, int signum) / / the corresponding position of the signal set is 0 int sigismember (const sigset_t * set, int signum); / / determine whether the signal is in the signal set.

For the signal set to allocate memory space, you need to use the initialization function to initialize. After initialization is complete, you can add and remove specific signals from the collection. Int sigprocmask (int how, const sigset_t * set, sigset_t * oldset); where the how variable determines how the status word is manipulated. SIG_BLOCK:set contains the signal we want to add to the current signal blocking word, which is equivalent to mask=mask | set SIG_UNBLOCK:set contains the signal we want to unblock from the current signal blocking word, which is equivalent to mask=mask&~set SIG_SETMASK: set the current signal blocking word to the value referred to by set, equivalent to mask=set

Pending is set by the kernel according to block, and only the data can be read to determine whether the signal will be delivered. You can block the signal you want to block by setting block, and the corresponding pending will be set by the kernel

To set up signal blocking and missed steps:

Allocate memory space sigset sigset bset; empty sigemptyset (& bset); add signal sigaddset (& bset, SIGINT); add other signals that need to be managed. . Set the signal processing scheme in the signal set (here for de-blocking) sigprocmask (SIG_UNBLOCK, & bset, NULL); the simplified version sets the blocking status word # include int sigpending (sigset_t * set)

This function is easy to use, and for the process that calls it, the signal in the signal set is blocked and cannot be delivered, so it must be currently pending.

Atomic operation signal blocks the recovery of words and enters the dormant state # include int sigsuspend (const sigset_t * mask)

Why is there an atomic unblocking function? Because, when the signal is blocked, the signal is generated, and the delivery of the signal is delayed until the signal is unblocked. If the application happens to be between unblocking the SIGINT and the pause at this point, there will be a problem, and the pause may never be able to wait for the SIGINT signal to interrupt him, causing the program to permanently block at the pause. In order to solve this problem, it is necessary to restore the shielded word of the signal in an atomic operation, and then put the process into a dormant state to ensure that the above problems do not occur.

The signal mask word for the process is set to

Signal Registration function-Premium Edition

We have successfully completed the sending and receiving of the signal, so why is there an advanced version? In fact, there is a problem with the previous signal, that is, although the signal has been sent and received, it always feels less. Since the signal has been sent, why can't we carry some more data? Exactly, we need another function to carry some data in the process of signal transmission. Let's first take a look at the function sent.

The function prototype of sigaction

# include int sigaction (int signum, const struct sigaction * act, struct sigaction * oldact); struct sigaction {void (* sa_handler) (int); / / signal processor, do not accept additional data, SIG_IGN is ignored, SIG_DFL is the default action void (* sa_sigaction) (int, siginfo_t *, void *); / / signal processor, can accept additional data with sigqueue to use sigset_t sa_mask / / the signal set of the blocking keyword, you can add the signal to the signal blocking word before calling the capture function, and the signal capture function returns to its original value before it is returned. The behavior of int sa_flags;// affecting the signal SA_SIGINFO indicates that data can be accepted}; / / callback function handles sa_handler or sa_sigaction can only be selected

The original help information for this function can be viewed through man sigaction.

Sigaction is a system call, according to this function prototype, it is not difficult to see that in the function prototype, the first parameter signum should be the number of the registered signal; the second parameter act if not null means that the signal needs to be configured new; if the third parameter oldact is not empty, then you can back up the previous signal configuration to facilitate later recovery.

In addition to the sa_mask member in the struct sigaction structure, the signal set in its signal set is set to block before the capture function is called, and the default settings are restored when the capture function returns. The purpose is that the dictation signal can be blocked when the signal handler is called. When the signal processing function is called, the operating system creates new signal blocking words, including the signal being delivered. Therefore, it can be guaranteed that when processing a given signal, if the signal occurs again, it will be blocked until the end of the processing of the previous signal.

Timeliness of sigaction: when a specified action is set on a signal, it will remain in effect until sigaction is explicitly called again and the action is changed.

The detailed configuration of the flag attribute in the structure will not be explained in detail here, but only one point. If it is set to the SA_SIGINFO property, the signal handler has additional information, that is, the signal handling function pointed to by the function pointer sa_sigaction will be called. Otherwise, the system defaults to the signal handling function that sa_handler points to. Here, I would like to note that sa_sigaction and sa_handler use the same memory space, which is equivalent to union, so you can only set one of them, not both at the same time.

There are still some instructions about void (* sa_sigaction) (int, siginfo_t *, void *); handlers. Void* is the additional data carried by the received signal, while the structure of struct siginfo is mainly suitable for recording some relevant information of the received signal.

Siginfo_t {int si_signo; / * Signal number * / int si_errno; / * An errno value * / int si_code; / * Signal code * / int si_trapno / * Trap number that caused hardware-generated signal (unused on most architectures) * / pid_t si_pid; / * Sending process ID * / uid_t si_uid; / * Real user ID of sending process * / int si_status / * Exit value or signal * / clock_t si_utime; / * User time consumed * / clock_t si_stime; / * System time consumed * / sigval_t si_value; / * Signal value * / int si_int; / * POSIX.1b signal * / void * si_ptr / * POSIX.1b signal * / int si_overrun; / * Timer overrun count; POSIX.1b timers * / int si_timerid; / * Timer ID; POSIX.1b timers * / void * si_addr; / * Memory location which caused fault * / int si_band; / * Band event * / int si_fd / * File descriptor * /}

There are many members, and si_signo and si_code are the two members that must be implemented. The relevant information of the signal can be obtained through this structure. There are two places for the sent data, the sigval_t si_value member has saved the sent information, and the corresponding data is also saved in the si_int or si_ptr member.

So, the signal sent by the kill function cannot carry data, and we can't verify the sending and receiving part yet, so let's take a look at the advanced use of sending signals, and then let's look at how to carry data through signals.

Signaling function-Premium # include int sigqueue (pid_t pid, int sig, const union sigval value); union sigval {int sival_int; void * sival_ptr;}

There must be several operations that need to be done before using this function

When you use the sigaction function to install the signal processor, the SA_SIGINFO flag is established. The sa_sigaction member in the sigaction structure provides a signal capture function. If it is implemented as a sa_handler member, you will not be able to get the extra data you carry.

The sigqueue function can only send a signal to a single process, and you can use the value parameter to pass an integer or pointer value to the signal handler.

The sigqueue function can not only send additional data, but also queue the signal (the operating system must implement the real-time extension of POSIX.1). For the signal with blocking, use sigqueue to send multiple the same signal. When unblocking, the receiver will receive the signal in the transmitted signal queue instead of directly receiving it once.

However, the signal cannot be queued indefinitely, and the maximum value of the signal queue is limited by SIGQUEUE_MAX. After reaching the maximum limit, sigqueue will fail and errno will be set to EAGAIN.

So let's give it a try and send a signal with extra data. Show me the code!! Receiving end

# include#include#include / / void (* sa_sigaction) (int, siginfo_t *, void *); void handler (int signum, siginfo_t * info, void * context) {if (signum = = SIGIO) printf ("SIGIO signal:% d\ n", signum); else if (signum = = SIGUSR1) printf ("SIGUSR1 signal:% d\ n", signum); else printf ("error\ n") If (context) {printf ("content:% d\ n", info- > si_int); printf ("content:% d\ n", info- > si_value.sival_int);} int main (void) {/ / int sigaction (int signum, const struct sigaction * act, struct sigaction * oldact); struct sigaction act; / * struct sigaction {void (* sa_handler) (int) Void (* sa_sigaction) (int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags;}; * / act.sa_sigaction = handler; act.sa_flags = SA_SIGINFO; sigaction (SIGIO, & act, NULL); sigaction (SIGUSR1, & act, NULL); for (;) {sleep (10000);} return 0;}

Sending end

# include#include # include#include int main (int argc, char** argv) {if (4! = argc) {printf ("[Arguments ERROR!]\ n"); printf ("\ tUsage:\ n"); printf ("\ t\ t% s\ n", argv [0]); return-1;} int pid = atoi (argv [1]); int sig = atoi (argv [2]) If (pid > 0 & & sig > 0) {/ / int sigqueue (pid_t pid, int sig, const union sigval value); union sigval val; val.sival_int = atoi (argv [3]); printf ("send:% d\ n", atoi (argv [3])); sigqueue (pid, sig, val);} else {printf ("Target_PID or Signal_Number MUST bigger than 0!\ n") } return 0;}

Received signals and information

Signals and data sent

This is the end of the article on "what is the Signal mechanism of Linux?" Thank you for reading! I believe you all have a certain understanding of "what is the Signal mechanism of Linux". If you want to learn more, you are welcome to follow the industry information channel.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 257

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report