Linux C system programming (08) process management signals and signal processing

Signals and signal processing

The signal is a typical asynchronous communication method, and is also one of the commonly used process communication methods under Linux, also known as soft interrupt. Use the kill -l command to view the list of signals supported by the system. Generally there are:


1 Signal basics

1.1 Signal generation

There are 5 ways to generate signals under linux.

  1. Operate the terminal, such as ctrl + C, ctrl + D.
  2. The hardware is abnormal.
  3. Execute the command kill to send a signal to a process.
  4. Call the kill function to send a command to another process.
  5. When the kernel detects a certain software condition, it can signal the process.

Note: No matter which of the above five methods sends a signal, the process of receiving the signal will suspend the execution of the program and turn to process the received signal. If the process is in the ready state / blocking state, then once the CPU time slice is obtained, the signal will be processed first. If the process is in the suspended state, the received signal will wake up the suspended process, and the process will process the signal first. In short, the principle of giving priority to processing signals, even if the program is an endless loop.

1.2 Signal processing

There are 3 processing methods for processes under Linux:

  1. Ignore this signal: ignore it.
  2. Register a signal processing function: require the kernel to switch to user mode to call the processing function after receiving the signal. This method is called capturing a signal. User programs often need to do some custom processing on certain signals to solve the problem.
  3. Perform system default actions: different signals have different actions. There are two default actions known to the system: terminate process / ignore signals.

1.3 How to use common signals

Shortcut keys set by the system to send signals to processes.

ctrl+C:结束当前的进程。
ctrl+/:程序运行结束,并且产生了core文件。core文件用于程序的调试,可以用gdb工具对其进行调试。

Use the kill command to send a specified signal to a specified process.


2 Influence of signal

No matter where the current process is executed, it will jump to the signal processing function for execution, so the signal will also have side effects, which is inevitable. For example, in a process, the main function, other threads, and signal processing functions are independent execution processes, which are parallel. If a process has multiple execution processes and these processes access the same resources, there may be conflicts.

2.1 Reentry

basic concept:

  • Reentrance: For a function, there are different execution flow calls, it is possible to enter the function again before the first call has not returned.
  • Reentrant function: If a function only accesses local variables / parameters, it is called a reentrant function / pure code / thread-safe function.
  • Non-reentrant functions: functions access global variables / parameters, and functions that may cause errors due to reentrancy.

A function is not reentrant if it meets one of the following conditions:

  • Global data is used, including global variables and static variables.
  • Call the dynamic method to get the memory, because the method of dynamically allocating memory is also to manage the memory allocation with a linked list, this kind of book is also global scope.
  • Standard I / O libraries are used, and many implementations in standard I / O libraries use global data structures in a non-reentrant manner.

In short, the use of data / functions with global scope are not reentrant, and such functions / codes are called impure codes.

2.2 Atomic manipulation

  • For some operations of a process, sometimes in order to prevent the signal from affecting it, the instruction needs to be executed once, which is the origin of the atomic operation. The so-called atomic operation is an instruction relative to assembly.
  • For assignment statements, to get atomic operations, you need to use the type: sig_atomic_t.
  • For programs with multiple processes, the two keywords sig_atomic_t and volatile are always used at the same time.
  • The role of the keyword volatile is to prevent optimization.

2.3 Interrupt system call

  • The signal handler loaded with the signal function will always restart the interrupted system call after the signal processing is completed.
  • Use the sigaction function to set whether to restart the interrupted system call when the signal handler returns.

3 Signal processing functions

3.1 Set the signal processing function

Under Linux, users are allowed to provide their own signal processing functions, use signal functions to load the processing functions, and notify the system:

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

See the linux function reference manual for details . In fact, this is in the signal.h file:

#define SIG_IGN ((void *) (*)()) 1
#define SIG_DFL ((void *) (*)()) 0
#define SIG_ERR ((void *) (*)()) -1

The overall framework of the signal processing program in the system is this:

void signal_handler(int signo)
{
   switch(signo){
   case SIG1:          /*处理信号1*/
        ...
   case SIG2:          /*处理信号2*/
        ...
   case SIGn:          /*处理信号n*/
        ...
   }
}

The Linux system does not allow users to create new signals, but provides two signals SIGUSR1 and SIGUSR2 specifically for signal communication between applications. These two signals have no special meaning, and the system default is to ignore them.

3.2 Sending signals and sending signals to the process itself

The @ 1 signal can be well applied to inter-process communication. The kill function is used to send signals to processes / process groups under Linux. The prototype of the function is as follows:

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);

See the linux function reference manual for details . One process must pay attention to the following points when sending signals to another process:

  1. The process has the authority to send signals to the specified process. If the signal can be sent casually, it will give malicious programs an opportunity.
  2. System processes cannot receive signals. Malicious programs can cause the system to crash.
  3. The root user can send a signal to any process in the system, so the root user can kill any malicious process, but at the same time, a malicious program with root user authority can also kill other processes in the system.
  4. Processes can also use kill (getpid (), signo) to send signals to themselves.

@ 2 linux provides a function to send a signal to the process itself, which can replace the above form. The function prototype is as follows:

#include <signal.h>
int raise(int sig);

See the linux function reference manual for details . Using the raise function can realize the function of the exit function, the difference is that it does not do any aftercare (such as flushing the stream, closing the file, etc.) . Therefore, if the signal processing method is to terminate the process, you should write a function that handles all aftercare matters as the signal processing program for this signal, and use the signal function to set it and use it.

3.3 Set linux timer

In some occasions, a timer needs to be set, and the process of setting the timer will be notified after a certain period of time. Use the alarm function to set a timer under Linux. The function prototype is as follows:

#include <unistd.h>
unsigned int alarm(unsigned int seconds);

See the linux function reference manual for details . Alarm is also called the alarm clock function. It can set a timer in the process. When the time specified by the timer expires, it sends a SIGALRM signal to the process. Of course, some points still need to be noted:

  1. A process can only have one alarm time. If the alarm time has been set before calling alarm, any previous alarm time will be replaced by the new value.
  2. After the alarm signal passes the specified number of seconds, the signal is generated by the kernel. Due to the delay of the process scheduling, it takes some time for the process to be controlled to be able to process the signal. Therefore, the alarm function does not set a very accurate timer.
  3. The alarm function can also be used to achieve timing blocking, that is, when the device that needs to be read and written is not ready, it only waits for a limited time. Of course, it is possible to do this for a system that is not very heavily loaded, but once the load is very serious, then when the alarm function sets the timer, the CPU authority is lost. Due to too many processes in the system in the era, the alarm The function is easy to time out, then some operations are no longer restricted by the alarm function.

3.4 Suspend the process

When the running conditions of a process are already met, the process still needs to be blocked (for example, the process is expected to be delayed, and the sleep function often plays this role). This situation where the process resource enters the blocked state is called process suspension. Use the pause function under Linux to suspend a process. The function prototype is as follows:

#include <unistd.h>
int pause(void);
/*pause函数执行成功返回-1,同时将errno设置为EINTR。
pause函数使调用该函数的进程进入挂起状态,直到有一个终止进程的信号/一个信号处理程序从其返回后,pause函数才返回。*/

See the linux function reference manual for details .

3.5 Process sleep

The pause function is to suspend the process without time limit. If you want to resume the process within a certain time, use the sleep function. The function prototype is as follows:

#include <unistd.h>
unsigned int sleep(unsigned int seconds);
int usleep(useconds_t usec);

See the linux function reference manual for details .


4 Signal set and shielded signal

4.1 Signal set and signal set processing function

Masking signals is an important function under Linux system, allowing user processes to selectively receive and process signals. Signals that need to be shielded are represented by signal sets. This set is a bit vector, where each bit corresponds to a signal. This data type is sigset_t. The operation of the signal can not be directly shifted, and the system function is needed to realize the relevant function. Under linux, a set of signal set processing functions are provided:

#include <signal.h>
int sigemptyset(sigset_t *set);     //将信号集清空
int sigfillset(sigset_t *set);          //将所有信号添加到信号集
int sigaddset(sigset_t *set, int signum);     //将信号signum添加到信号集set中
int sigdelset(sigset_t *set, int signum);     //将信号signum从信号集set中删除
int sigismember(const sigset_t *set, int signum);     //在信号集set中查看signum信号是否被设置,被设置函数返回1,没被设置函数返回0,查看失败返回-1。

See the linux function reference manual for details .

Note: The signal number starts from 1, and the bit number in the signal processing function starts from 0, so there are:

signum==信号编码-1

4.2 Shielded signal

The signal needs to be processed in the program. Naturally, there are some cases where the signal does not need to be processed. So you need to shield the signal function. Each process has a signal mask word that marks the signal being masked. The essence of the signal mask word is the same as the signal set, which is a bit vector. The corresponding bit of the signal encoding is 1 to mask the signal, and the corresponding bit is 0 to process the signal. Under linux, use the sigpromask function to detect or change the signal mask word of the process. The prototype of the sigpromask function is as follows:

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

See the linux function reference manual for details . (Note: The two signals SIG_KILL and SIG_STOP cannot be masked. If these two signals are masked, the malicious process does not need to worry about being killed by the root user ’s kill command and it can be destroyed at will

4.3 Handling pending signals

The signal "pending" is a state, which refers to the period from the generation of the signal to the time before the signal is processed; it can be viewed by int sigpending (sigset_t * set) and int sigismember (const sigset_t * set, int signum) A certain signal is pending. The sigpending function is used to check pending signals under Linux. The prototype of the function is as follows:

#include <signal.h>
int sigpending(sigset_t *set);

See the linux function reference manual for details .

Published 289 original articles · praised 47 · 30,000+ views

Guess you like

Origin blog.csdn.net/vviccc/article/details/105159225