Home > System Tutorial > LINUX > Signal mechanism under Linux: How to use signals for inter-process communication and control

Signal mechanism under Linux: How to use signals for inter-process communication and control

WBOY
Release: 2024-02-12 12:40:03
forward
1175 people have browsed it

Linux 下的信号机制:如何使用信号进行进程间通信和控制

Signal is a common method of inter-process communication and control in Linux systems. It allows one process to send a simple message to another process to notify it that something has happened. event or status. The function of signals is to improve the responsiveness and flexibility of the system to cope with some abnormal or emergency situations. In the Linux system, there are many kinds of signals, such as SIGINT, SIGTERM, SIGKILL, etc. Each of them has its own meaning and function, and is suitable for different scenarios and needs. But, do you really understand the signal mechanism under Linux? Do you know how to use signals for inter-process communication and control under Linux? Do you know how to handle and ignore signals in Linux? This article will introduce you to the relevant knowledge of the signal mechanism under Linux in detail, allowing you to better use and understand this powerful inter-process communication and control method under Linux.

1. Basic concepts of signals

This section first introduces some basic concepts of signals, and then gives some basic signal types and events corresponding to signals. Basic concepts are particularly important for understanding and using signals, and for understanding signaling mechanisms. Let’s take a look at what a signal is.

1. Basic concepts

Soft interrupt signal (signal, also referred to as signal) is used to notify the process that an asynchronous event has occurred. Processes can send soft interrupt signals to each other through the system call kill. The kernel can also send signals to the process due to internal events, notifying the process that an event has occurred. Note that signals are only used to notify a process of what events have occurred and do not pass any data to the process.

The process that receives the signal has different processing methods for various signals. The processing methods can be divided into three categories: the first is an interrupt-like handler. For the signal that needs to be processed, the process can specify a processing function, which will handle it. The second method is to ignore a signal and do nothing with the signal, just as if it never happened. The third method is to retain the system's default value for processing the signal. This default operation, the default operation for most signals is to terminate the process. The process uses the system call signal to specify the process's processing behavior for a certain signal.

There is a soft interrupt signal field in the entry of the process table. Each bit in this field corresponds to a signal. When a signal is sent to the process, the corresponding bit is set. It can be seen from this that the process can retain different signals at the same time, but for the same signal, the process does not know how many have come before processing.

2. Signal type

There are many reasons for sending signals. Here are simple classifications according to the reasons for sending signals to understand various signals:

(1) Signals related to process termination. This type of signal is emitted when the process exits or the child process terminates.
(2) Signals related to process exception events. For example, the process crosses the boundary, or attempts to write to a read-only memory area (such as the program text area), or executes a privileged instruction and various other hardware errors.
(3) Signals related to encountering unrecoverable conditions during system calls. For example, when executing the system call exec, the original resources have been released, and the current system resources have been exhausted.
(4) Signals related to encountering non-predictive error conditions while executing system calls. Such as executing a system call that does not exist.
(5) Signals sent by processes in user mode. For example, a process calls the system call kill to send signals to other processes.
(6) Signals related to terminal interaction. For example, the user closes a terminal or presses the break key.
(7) Track the signals of process execution.

The list of signals supported by Linux is as follows. Many signals are related to the machine's architecture. The first ones listed are the signals listed in POSIX.1:

Signal value processing action reason for sending signal
——————————————————————-

SIGHUP 1 A 终端挂起或者控制进程终止 
SIGINT 2 A 键盘中断(如break键被按下) 
SIGQUIT 3 C 键盘的退出键被按下 
SIGILL 4 C 非法指令 
SIGABRT 6 C 由abort(3)发出的退出指令 
SIGFPE 8 C 浮点异常 
SIGKILL 9 AEF Kill信号 
SIGSEGV 11 C 无效的内存引用 
SIGPIPE 13 A 管道破裂: 写一个没有读端口的管道 
SIGALRM 14 A 由alarm(2)发出的信号 
SIGTERM 15 A 终止信号 
SIGUSR1 30,10,16 A 用户自定义信号1 
SIGUSR2 31,12,17 A 用户自定义信号2 
SIGCHLD 20,17,18 B 子进程结束信号 
SIGCONT 19,18,25 进程继续(曾被停止的进程) 
SIGSTOP 17,19,23 DEF 终止进程 
SIGTSTP 18,20,24 D 控制终端(tty)上按下停止键 
SIGTTIN 21,21,26 D 后台进程企图从控制终端读 
SIGTTOU 22,22,27 D 后台进程企图从控制终端写 
Copy after login

The following signals are not listed in POSIX.1, but are listed in SUSv2

Signal value processing action reason for sending signal
——————————————————————–

SIGBUS 10,7,10 C 总线错误(错误的内存访问) 
SIGPOLL A Sys V定义的Pollable事件,与SIGIO同义 
SIGPROF 27,27,29 A Profiling定时器到 
SIGSYS 12,-,12 C 无效的系统调用 (SVID) 
SIGTRAP 5 C 跟踪/断点捕获 
SIGURG 16,23,21 B Socket出现紧急条件(4.2 BSD) 
SIGVTALRM 26,26,28 A 实际时间报警时钟信号(4.2 BSD) 
SIGXCPU 24,24,30 C 超出设定的CPU时间限制(4.2 BSD) 
SIGXFSZ 25,25,31 C 超出设定的文件大小限制(4.2 BSD) 

(对于SIGSYS,SIGXCPU,SIGXFSZ,以及某些机器体系结构下的SIGBUS,Linux缺省的动作是A (terminate),SUSv2 是C (terminate and dump core))。 
Copy after login

Here are some other signals

Signal value processing action reason for sending signal
——————————————————————-

SIGIOT 6 C IO捕获指令,与SIGABRT同义 
SIGEMT 7,-,7 
SIGSTKFLT -,16,- A 协处理器堆栈错误 
SIGIO 23,29,22 A 某I/O操作现在可以进行了(4.2 BSD) 
SIGCLD -,-,18 A 与SIGCHLD同义 
SIGPWR 29,30,19 A 电源故障(System V) 
SIGINFO 29,-,- A 与SIGPWR同义 
SIGLOST -,-,- A 文件锁丢失 
SIGWINCH 28,28,20 B 窗口大小改变(4.3 BSD, Sun) 
SIGUNUSED -,31,- A 未使用的信号(will be SIGSYS) 
Copy after login

(Here, - means that the signal is not implemented; there are three values ​​​​given the meaning, the first value is usually valid on Alpha and Sparc, the middle value corresponds to i386 and ppc and sh, and the last value corresponds to mips. Signal 29 is SIGINFO/SIGPWR on Alpha and SIGLOST on Sparc.)

The letters in the processing action item have the following meanings
A The default action is to terminate the process
B The default action is to ignore this signal
The default action of C is to terminate the process and perform a core image dump (dump core)
D The default action is to stop the process
E signal cannot be captured
F signal cannot be ignored

The signals introduced above are supported by common systems. The names, functions and processing actions of various signals by default are introduced in table form. The meanings of various default processing actions are: terminating the program means that the process exits; ignoring the signal means discarding the signal without processing it; stopping the program means that the program hangs and can be restarted after entering the stopped state, usually in During debugging (such as ptrace system call); kernel image dump refers to dumping the image of process data in memory and part of the content stored in the kernel structure of the process to the file system in a certain format, and the process exits execution. In this way The benefit is that it provides programmers with the convenience of obtaining data values ​​while the process is executing, allowing them to determine the cause of the dump, and to debug their programs.

Note that the signals SIGKILL and SIGSTOP can neither be caught nor ignored. The signals SIGIOT and SIGABRT are one signal. It can be seen that the same signal may have different values ​​in different systems, so it is recommended to use the name defined for the signal rather than directly using the value of the signal.

2. Signaling mechanism

The basic concept of signals was introduced in the previous section. In this section, we will introduce how the kernel implements the signal mechanism. That is, how the kernel sends a signal to a process, how the process receives a signal, how the process controls its response to the signal, when and how the kernel processes the signal received by the process. I would also like to introduce the role setjmp and longjmp play in signals.

1. Basic signal processing methods by the kernel

The way the kernel sends a soft interrupt signal to a process is to set the bit corresponding to the signal in the signal field of the process table entry where the process is located. What should be added here is that if the signal is sent to a sleeping process, then it depends on the priority of the process entering sleep. If the process sleeps at a priority that can be interrupted, wake up the process; otherwise, only set the signal in the process table. field corresponding bit without waking up the process. This is important because the time for a process to check whether a signal has been received is when a process is about to return from kernel mode to user mode; or when a process is about to enter or leave an appropriately low-priority sleep state.

The timing for the kernel to process a signal received by a process is when a process returns to user mode from kernel mode. Therefore, when a process is running in kernel mode, the soft interrupt signal does not take effect immediately and must wait until it returns to user mode. The process will return to user mode only after processing the signal. The process will not have unprocessed signals in user mode.

The kernel handles the soft interrupt signal received by a process in the context of the process, therefore, the process must be in a running state. As mentioned before when introducing the concept, there are three types of signal processing: the process exits after receiving the signal; the process ignores the signal; and after the process receives the signal, it executes the function set by the user to call signal through the system. When a process receives a signal that it ignores, the process discards the signal and continues as if the signal had not been received. If the process receives a signal to be captured, the user-defined function is executed when the process returns from kernel mode to user mode. Moreover, the method of executing user-defined functions is very clever. The kernel creates a new layer on the user stack. In this layer, the value of the return address is set to the address of the user-defined processing function, so that when the process returns from the kernel and pops the top of the stack It returns to the user-defined function, and when it returns from the function and then pops the top of the stack, it returns to the place where it originally entered the kernel. The reason for this is that user-defined processing functions cannot and are not allowed to be executed in kernel mode (if the user-defined function runs in kernel mode, the user can obtain any permissions).

There are several points that should be paid special attention to in the signal processing method. First, in some systems, when a process handles an interrupt signal and returns to user mode, the kernel clears the address of the signal processing routine set in the user area, that is, the next time the process changes the signal processing method, The default value unless the signal system call is used again before the next signal arrives. This may cause the process to get the signal before calling signal, causing it to exit. In BSD, the kernel no longer clears this address. But not clearing this address may cause the process to get a signal too quickly and cause a stack overflow. In order to avoid the above situation. In the BSD system, the kernel simulates the processing method of hardware interrupts, that is, when processing an interrupt, it prevents the reception of new interrupts of this type.

The second thing to note is that if the signal to be captured occurs when the process is in a system call, and the process sleeps at an interruptible priority level, then the signal causes the process to do a longjmp and jump out Sleep state, return to user mode and execute signal processing routines. When returning from a signal handling routine, the process behaves as if it had returned from a system call, but returns an error code indicating that the system call was interrupted. It should be noted that in BSD systems the kernel can automatically restart system calls.

The third thing to note: If the process sleeps at an interruptible priority level, when it receives a signal to be ignored, the process is awakened, but does not do longjmp, and usually continues to sleep. But the user does not feel that the process has been awakened, but it is as if the signal has not occurred.

The fourth thing to note: The kernel handles the child process termination (SIGCLD) signal differently from other signals. When a process checks that it has received a signal to terminate a child process, by default, the process acts as if it has not received the signal. If the parent process executes the system call wait, the process will wake up from the system call wait and Return to the wait call, perform a series of follow-up operations of the wait call (find the zombie child process, release the process table entry of the child process), and then return from wait. The function of the SIGCLD signal is to wake up a process sleeping at an interruptible priority level. If the process catches the signal, it goes to the handler routine just like normal signal handling. If the process ignores the signal, the action of the system call wait is different, because the function of SIGCLD is only to wake up a process sleeping at the interruptible priority level, then the parent process that executes the wait call is awakened and continues to execute the wait call. Subsequent operations, and then wait for other child processes.

If a process calls the signal system call and sets the SIGCLD processing method, and the process has a child process in a zombie state, the kernel will send a SIGCLD signal to the process.

2. The functions of setjmp and longjmp

When introducing the signal processing mechanism earlier, setjmp and longjmp were mentioned many times, but their functions and implementation methods were not explained in detail. Here is a brief introduction to this.

When introducing signals, we saw that many places require the process to return directly from the original system call after checking that the signal is received, rather than waiting for the call to complete. This situation where a process suddenly changes its context is the result of using setjmp and longjmp. setjmp stores the saved context in the user area and continues execution in the old context. That is to say, the process executes a system call. When it goes to sleep due to resources or other reasons, the kernel makes a setjmp for the process. If it is awakened by a signal during sleep and the process cannot go to sleep again, the kernel calls longjmp for the process. This operation is for the kernel to restore the context saved in the user area of ​​the process by the original setjmp call to the current context, so that the process can return to the state before waiting for resources, and the kernel returns 1 for setjmp, so that the process knows that the system call failed. . That's what they do.

3. System calls related to signals

Most of the knowledge about signals has been introduced in the previous two sections. In this section we will learn about these system calls. Among them, the system call signal is used by the process to set the processing method of a certain signal, and the system call kill is used to send a signal to the specified process. These two calls form the basic operation of the signal. The last two calls, pause and alarm, are process pauses and timers implemented through signals. Calling alarm is used to notify the process of the timer expiration through signals. So here, we also introduce these two calls.

1. signal system call

The system call signal is used to set the processing method of a certain signal. The format of the call declaration is as follows:
void (*signal(int signum, void (*handler)(int)))(int);
Add the following header file to the process using this call:
#include

The above declaration format is relatively complicated. If you don’t know how to use it, you can also use it through the following type definition format (POSIX definition):
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
However, this format has different type definitions in different systems, so to use this format, it is best to refer to the online manual.

In the call, the parameter signum indicates the signal to be set to handle the method. The second parameter handler is a processing function, or
SIG_IGN: Ignore the signal pointed to by the parameter signum.
SIG_DFL: Restore the processing method of the signal pointed by parameter signum to the default value.

The integer parameter passed to the signal processing routine is the signal value, which allows one signal processing routine to process multiple signals. The return value of the system call signal is the previous processing routine of the specified signal signum or the error code SIG_ERR returned in case of error. Let’s look at a simple example:

#include 
\#include 
\#include 
void sigroutine(int dunno) { /* 信号处理例程,其中dunno将会得到信号的值 */ 
switch (dunno) { 
case 1: 
printf("Get a signal -- SIGHUP "); 
break; 
case 2: 
printf("Get a signal -- SIGINT "); 
break; 
case 3: 
printf("Get a signal -- SIGQUIT "); 
break; 
} 
return; 
} 

int main() { 
printf("process id is %d ",getpid()); 
signal(SIGHUP, sigroutine); //* 下面设置三个信号的处理方法 
signal(SIGINT, sigroutine); 
signal(SIGQUIT, sigroutine); 
for (;;) ; 
} 
Copy after login

其中信号SIGINT由按下Ctrl-C发出,信号SIGQUIT由按下Ctrl-发出。该程序执行的结果如下:

localhost:~$ ./sig_test 
process id is 463 
Get a signal -SIGINT //按下Ctrl-C得到的结果 
Get a signal -SIGQUIT //按下Ctrl-得到的结果 
//按下Ctrl-z将进程置于后台 
[1]+ Stopped ./sig_test 
localhost:~$ bg 
[1]+ ./sig_test & 
localhost:~$ kill -HUP 463 //向进程发送SIGHUP信号 
localhost:~$ Get a signal – SIGHUP 
kill -9 463 //向进程发送SIGKILL信号,终止进程 
localhost:~$ 
Copy after login

2、kill 系统调用

系统调用kill用来向进程发送一个信号。该调用声明的格式如下:
int kill(pid_t pid, int sig);
在使用该调用的进程中加入以下头文件:

\#include 
\#include 
Copy after login

该 系统调用可以用来向任何进程或进程组发送任何信号。如果参数pid是正数,那么该调用将信号sig发送到进程号为pid的进程。如果pid等于0,那么信 号sig将发送给当前进程所属进程组里的所有进程。如果参数pid等于-1,信号sig将发送给除了进程1和自身以外的所有进程。如果参数pid小于- 1,信号sig将发送给属于进程组-pid的所有进程。如果参数sig为0,将不发送信号。该调用执行成功时,返回值为0;错误时,返回-1,并设置相应 的错误代码errno。下面是一些可能返回的错误代码:
EINVAL:指定的信号sig无效。
ESRCH:参数pid指定的进程或进程组不存在。注意,在进程表项中存在的进程,可能是一个还没有被wait收回,但已经终止执行的僵死进程。
EPERM: 进程没有权力将这个信号发送到指定接收信号的进程。因为,一个进程被允许将信号发送到进程pid时,必须拥有root权力,或者是发出调用的进程的UID 或EUID与指定接收的进程的UID或保存用户ID(savedset-user-ID)相同。如果参数pid小于-1,即该信号发送给一个组,则该错误 表示组中有成员进程不能接收该信号。

3、pause系统调用

系统调用pause的作用是等待一个信号。该调用的声明格式如下:
int pause(void);
在使用该调用的进程中加入以下头文件:
#include

该调用使得发出调用的进程进入睡眠,直到接收到一个信号为止。该调用总是返回-1,并设置错误代码为EINTR(接收到一个信号)。下面是一个简单的范例:

#include 
\#include 
\#include 
void sigroutine(int unused) { 
printf("Catch a signal SIGINT "); 
} 

int main() { 
signal(SIGINT, sigroutine); 
pause(); 
printf("receive a signal "); 
} 
Copy after login

在这个例子中,程序开始执行,就象进入了死循环一样,这是因为进程正在等待信号,当我们按下Ctrl-C时,信号被捕捉,并且使得pause退出等待状态。

4、alarm和 setitimer系统调用

系统调用alarm的功能是设置一个定时器,当定时器计时到达时,将发出一个信号给进程。该调用的声明格式如下:
unsigned int alarm(unsigned int seconds);
在使用该调用的进程中加入以下头文件:
#include

系 统调用alarm安排内核为调用进程在指定的seconds秒后发出一个SIGALRM的信号。如果指定的参数seconds为0,则不再发送 SIGALRM信号。后一次设定将取消前一次的设定。该调用返回值为上次定时调用到发送之间剩余的时间,或者因为没有前一次定时调用而返回0。

注意,在使用时,alarm只设定为发送一次信号,如果要多次发送,就要多次使用alarm调用。

对于alarm,这里不再举例。现在的系统中很多程序不再使用alarm调用,而是使用setitimer调用来设置定时器,用getitimer来得到定时器的状态,这两个调用的声明格式如下:
int getitimer(int which, struct itimerval *value);
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);
在使用这两个调用的进程中加入以下头文件:
#include

该系统调用给进程提供了三个定时器,它们各自有其独有的计时域,当其中任何一个到达,就发送一个相应的信号给进程,并使得计时器重新开始。三个计时器由参数which指定,如下所示:
TIMER_REAL:按实际时间计时,计时到达将给进程发送SIGALRM信号。
ITIMER_VIRTUAL:仅当进程执行时才进行计时。计时到达将发送SIGVTALRM信号给进程。
ITIMER_PROF:当进程执行时和系统为该进程执行动作时都计时。与ITIMER_VIR-TUAL是一对,该定时器经常用来统计进程在用户态和内核态花费的时间。计时到达将发送SIGPROF信号给进程。

定时器中的参数value用来指明定时器的时间,其结构如下:

struct itimerval { 
struct timeval it_interval; /* 下一次的取值 */ 
struct timeval it_value; /* 本次的设定值 */ 
}; 

该结构中timeval结构定义如下: 
struct timeval { 
long tv_sec; /* 秒 */ 
long tv_usec; /* 微秒,1秒 = 1000000 微秒*/ 
}; 
Copy after login

在setitimer 调用中,参数ovalue如果不为空,则其中保留的是上次调用设定的值。定时器将it_value递减到0时,产生一个信号,并将it_value的值设 定为it_interval的值,然后重新开始计时,如此往复。当it_value设定为0时,计时器停止,或者当它计时到期,而it_interval 为0时停止。调用成功时,返回0;错误时,返回-1,并设置相应的错误代码errno:
EFAULT:参数value或ovalue是无效的指针。
EINVAL:参数which不是ITIMER_REAL、ITIMER_VIRT或ITIMER_PROF中的一个。

下面是关于setitimer调用的一个简单示范,在该例子中,每隔一秒发出一个SIGALRM,每隔0.5秒发出一个SIGVTALRM信号:

#include 
\#include 
\#include 
\#include 
int sec; 

void sigroutine(int signo) { 
switch (signo) { 
case SIGALRM: 
printf("Catch a signal -- SIGALRM "); 
break; 
case SIGVTALRM: 
printf("Catch a signal -- SIGVTALRM "); 
break; 
} 
return; 
} 

int main() { 
struct itimerval value,ovalue,value2; 
sec = 5; 

printf("process id is %d ",getpid()); 
signal(SIGALRM, sigroutine); 
signal(SIGVTALRM, sigroutine); 

value.it_value.tv_sec = 1; 
value.it_value.tv_usec = 0; 
value.it_interval.tv_sec = 1; 
value.it_interval.tv_usec = 0; 
setitimer(ITIMER_REAL, &value, &ovalue); 

value2.it_value.tv_sec = 0; 
value2.it_value.tv_usec = 500000; 
value2.it_interval.tv_sec = 0; 
value2.it_interval.tv_usec = 500000; 
setitimer(ITIMER_VIRTUAL, &value2, &ovalue); 

for (;;) ; 
} 
Copy after login

该例子的屏幕拷贝如下:

localhost:~$ ./timer_test 
process id is 579 
Catch a signal – SIGVTALRM 
Catch a signal – SIGALRM 
Catch a signal – SIGVTALRM 
Catch a signal – SIGVTALRM 
Catch a signal – SIGALRM 
Catch a signal –GVTALRM
Copy after login

通过本文,你应该对 Linux 下的信号机制有了一个深入的了解,知道了它的定义、原理、用法和优缺点。你也应该明白了信号机制的作用和影响,以及如何在 Linux 下正确地使用和处理信号。我们建议你在使用 Linux 系统时,使用信号机制来提高系统的响应性和灵活性。同时,我们也提醒你在使用信号机制时要注意一些潜在的问题和挑战,如信号丢失、信号屏蔽、信号安全等。希望本文能够帮助你更好地使用 Linux 系统,让你在 Linux 下掌握信号机制的使用和处理。

The above is the detailed content of Signal mechanism under Linux: How to use signals for inter-process communication and control. For more information, please follow other related articles on the PHP Chinese website!

source:lxlinux.net
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template