[Linux] Linux signal overview signal generation

Table of contents

0. Problem introduction:

0.1 Set the process as a background process

0.2 View the background process and bring the background process to the foreground

0.3 Set the foreground process as a background process

1. The concept of signal

2. Check the signal list

3. Common ways of signal processing

4. Signal generation

4.1 The way the user layer generates signals

4.1.1 Generate signals through terminal buttons

4.1.2 Call the system function to signal the process (kill, raise, abort)

4.1.3 Signals generated by software conditions

4.1.4 Signals generated by hardware exceptions


0. Problem introduction:

In our experience of learning Linux, we also used signals many times. For example: when we use xshell, press Ctrl+c on the command line, this keyboard input generates a hardware interrupt, which is acquired by the operating system, interpreted as a signal, and sent to the target foreground process. Because the foreground process received the signal, it caused the process to exit.

Notice:

  1. The signal generated by Ctrl+C can only be sent to the foreground process. Adding & after a command can be run in the background, so that the Shell can accept new commands and start a new process without waiting for the process to end.
  2. Shell can run a foreground process and any number of background processes at the same time. Only the foreground process can receive signals generated by control keys like Ctrl+C.
  3. When the foreground process is running, the user may press Ctrl+C at any time to generate a signal, which means that the user space code of the process may receive a SIGINT signal and terminate anywhere, so the signal is relative to the control flow of the process It is asynchronous (Asynchronous).

0.1 Set the process as a background process

./process name&

0.2 View the background process and bring the background process to the foreground

We found that once the process is set as a background process, it cannot be killed. At this time, it is printing hello myproc in an infinite loop. So how do we kill this process? One way is to set the background process as a foreground process, and then use ctrl + c to close it. This method is also easier to understand. So here we will use this method to close the background process.

First of all, we need to check what tasks are being executed at this time, we can enter jobs to view the current task list

Secondly, we bring this process to the foreground, use fg 1, where 1 is the task number, that is, the number in [ ]+, note that there needs to be a space between fg and 1.

0.3 Set the foreground process as a background process

We just learned that the fg task number is used to bring the background process to the foreground, and f means front. So if we want to set the foreground process as a background process, we can use bg task number, we can test it

1. The concept of signal

Signal is a way of asynchronous notification of events between processes, which belongs to soft interrupt.

2. Check the signal list

Use the kill -l command to view a list of system-defined signals

  • Each signal has a number and a macro definition name, these macro definitions can be found in signal.h, for example define #define SIGINT 2
  • Numbers above 34 are real-time signals. Here we only discuss signals below number 34, not real-time signals. The conditions under which these signals generate the default actions are detailed in signal(7): man 7 signal

3. Common ways of signal processing

Since the signal is generated asynchronously, when the signal is generated, the corresponding process may be doing more important things, so this process can temporarily not process the signal! The process is doing more important things, indicating that the process may not need to understand and handle this signal! But it does not mean that this signal will not be processed. The process must remember that this signal has come (is there a signal? What signal?). Therefore, there are three common ways of signal processing:

  1. ignore this signal
  2. Execute the default processing action for this signal
  3. Provide a signal processing function that requires the kernel to switch to the user mode to execute the processing function when processing the signal. This method is called catching the signal (Catch).

4. Signal generation

We mentioned earlier that we are now only talking about signals 1-31. Then the process must deal with a signal, it must be described first, and then organized. So how does the process remember this signal? Of course it is saved in the PCB of the process. Since we only consider processes 1-31, there is a uint32_t sig in the process's tast_struct to represent the signal. The idea of ​​bitmap is used here. What signal to generate we use is the bit position. So how to judge whether there is a bit generated? We pass the content of the bit, 1 means generated, 0 means not generated. So a uint_32 is enough to represent 1-31 signals.

And tast_struct is the data structure of the kernel, so only the operating system has the right to obtain all the attributes of the process. Therefore, the entire life cycle of the process, no matter how the signal is generated, can only be set by the operating system for us.

4.1 The way the user layer generates signals

4.1.1 Generate signals through terminal buttons

The first way to generate signals is through terminal key generation, that is, the keyboard. The system also provides us with a signal function that can capture the signals we generate. Next, we will use the signal function to verify that the following terminal keys can generate signals.

The default processing action of SIGINT is to terminate the process, and the default processing action of SIGQUIT is to terminate the process and Core Dump. Now we can verify the following.

We can generate signals using functions. We can send a signal to a process using the signal function.

Among them, handler is a function pointer (callback method), which is an interface that allows us to customize the signal processing (the third way to process signals)

Let's see how to use the signal function in the C++ code. In the following code, we set that if the process receives the SIGINT signal, it will call the handler function to output the specified content. We are all SIGINT is the No. 2 signal. Pressing Ctrl + C on the keyboard is essentially sending the No. 2 signal to the foreground process. Therefore, when the process starts running, when we press Ctrl + C, the program will call the hanlder method to print the specified content. We Let's see the result.

#include <iostream>
#include <signal.h>
#include <unistd.h>

using namespace std;

void handler(int signo)
{
    cout<<"我是一个进程,刚刚获取了一个信号,信号编号是: "<<signo <<endl;
}

int main()
{
    signal(SIGINT,handler);

    sleep(3);
    cout<<"进程已经设置完了"<<endl;
    sleep(3);
    while(true)
    {
        cout<<"我是一个运行中的进程,我的pid: "<<getpid()<<endl;
        sleep(2);
    }
    
    return 0;
}

Note: The hanlder method will only be called when SIGINT is generated here.

When the No. 2 signal is set by us, the process will not execute the default action according to the No. 2 signal. Therefore, we cannot use ctrl + c to terminate the process at this time. We can press ctrl + \, ctrl + \ is Generate signal No. 3 to the process, and we can also customize the signal No. 3. According to this principle, we can customize all 31 signals, so can this process not be killed? Actually not! Process No. 9 cannot be customized. Because the operating system needs to protect itself, it is certainly not possible to customize all signals.

#include <iostream>
#include <signal.h>
#include <unistd.h>

using namespace std;

void handler(int signo)
{
    cout<<"我是一个进程,刚刚获取了一个信号,信号编号是: "<<signo <<endl;
}

int main()
{
    for(int sig=1;sig<=31;sig++)
    {
        signal(sig,handler);
    }

    //signal(SIGINT,handler);

    sleep(3);
    cout<<"进程已经设置完了"<<endl;
    sleep(3);
    while(true)
    {
        cout<<"我是一个运行中的进程,我的pid: "<<getpid()<<endl;
        sleep(2);
    }

    return 0;
}

So we know that the keyboard can generate signals, so who sent the signal to the process? The answer, of course, is the OS operating system

4.1.2 Call the system function to signal the process (kill, raise, abort)

Send a signal using the kill function

We know that the kill command can send a signal to a process. For example, we often used kill -9 pid to kill the process whose process number is pid. At the same time, kill is also a system function. We can also call the kill function through software to send a signal to the specified process.

Let's use a piece of C++ code to demonstrate how the kill function is used: our plan is to simulate the implementation of the kill command by calling the kill function to send a signal to the specified process. Therefore, when the program finishes running, if you enter ./mykill 9 pid, you can kill the specified pid process.

#include <iostream>
#include <cstring>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>

#include <unistd.h>

using namespace std;

void handler(int signo)
{
    cout<<"我是一个进程,刚刚获取了一个信号,信号编号是: "<<signo <<endl;
}


static void Usage(const std::string &proc)
{
    cerr<<"Usage:\n\t" << proc << "signo pid"<<endl;
}

//我想写一个kill命令
// ./mykill 9 pid
int main(int argc,char* argv[])
{
    if(argc != 3) 
    {
        Usage(argv[0]);
        exit(1);
    }
 
    if(kill(static_cast<pid_t>(atoi(argv[2])),atoi(argv[1])) == -1)
    {
        cerr<<"kill :" <<strerror(errno) << endl;
    }
    return 0;
}

Use the raise function

raise is a self-fetching function, which is a process that sends a signal to itself. So let's use it in the code and add that we want to send signal 2 to ourselves.

#include <iostream>
#include <cstring>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>

#include <unistd.h>

using namespace std;

void handler(int signo)
{
    cout<<"我是一个进程,刚刚获取了一个信号,信号编号是: "<<signo <<endl;
}


static void Usage(const std::string &proc)
{
    cerr<<"Usage:\n\t" << proc << "signo pid"<<endl;
}

//我想写一个kill命令
// ./mykill 9 pid
int main(int argc,char* argv[])
{
    signal(2,handler);//没有调用对应的handler方法,仅仅是注册


    while(true)
    {
        sleep(1);
        raise(2);//自己给自己发送2号信号
    }
    return 0;
}

use the abort function

abort wants to send the SIGABRT signal by itself -- we also call the abort function in the code for the No. 6 signal, and capture the following signal.

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>

#include <unistd.h>

using namespace std;

void handler(int signo)
{
    cout<<"我是一个进程,刚刚获取了一个信号,信号编号是: "<<signo <<endl;
}


static void Usage(const std::string &proc)
{
    cerr<<"Usage:\n\t" << proc << "signo pid"<<endl;
}

//我想写一个kill命令
// ./mykill 9 pid
int main(int argc,char* argv[])
{
    signal(6,handler);//没有调用对应的handler方法,仅仅是注册


    while(true)
    {
        sleep(1);
        abort();
        //raise(2);//自己给自己发送2号信号
    }
    return 0;
}

Through the results, we found that we did capture signal No. 6 just now, but the process still exited. Therefore, except for signal No. 9, the process can exit. Signal number 6 is also available.

4.1.3 Signals generated by software conditions

alarm

Calling the alarm function can set an alarm clock, that is, tell the kernel to send a SIGALRM (No. 14) signal to the current process after seconds seconds, and the default processing action of changing the signal is to terminate the process.

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>

#include <unistd.h>

using namespace std;
int main(int argc,char* argv[])
{
    alarm(3);//3秒后alarm
    int cnt = 0;
    while(true)
    {
        cout<< "我是一个进程 cnt : "<<cnt++<<endl;
        sleep(1);
    }
    return 0;
}

We call the alarm function, and the alarm clock will sound after 3 seconds. We have not changed the action of the alarm, so when the alarm clock sounds, the alarm function will perform the default operation of terminating the process, so after 3 seconds, the process will automatically exit.

There is a picture here but I can't get it out, I write the result here in the form of code

[Lxy@VM-20-12-centos 11-28]$ ./mykill 
我是一个进程 cnt : 0
我是一个进程 cnt : 1
我是一个进程 cnt : 2
Alarm clock

4.1.4 Signals generated by hardware exceptions

A hardware exception is somehow detected by the hardware and notified to the kernel, which then sends the appropriate signal to the current process. For example, if the current process executes an instruction other than 0, the computing unit of the CPU will generate an exception, and the kernel will interpret this exception as a SIGFPE signal and send it to the process. Another example is that if the current process accesses an illegal memory address, the MMU will generate an exception, and the kernel will interpret this exception as a SIGSEGV signal and send it to the process.

When we write code every day, if we write a /0 error, what will happen to the process?

    int a = 10;
    cout<< a/0<<endl;

When we run, it will directly tell us that the floating point number is abnormal! Therefore, the C/C++ code we wrote before, we said that the program crashed. Now we stand on the perspective of the system and we can know that the so-called program crash is not very accurate. It is actually a process crash. The essence of a process crash is the process. Abnormal signal received! We write two examples of process crashes.

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>

#include <unistd.h>

using namespace std;
int cnt = 0;
void handler(int signo)
{
    cout<<"我是一个进程,刚刚获取了一个信号,信号编号是: "<<signo << endl;
}


static void Usage(const std::string &proc)
{
    cerr<<"Usage:\n\t" << proc << "signo pid"<<endl;
}

//我想写一个kill命令
// ./mykill 9 pid
int main(int argc,char* argv[])
{
    for(int sig = 1;sig<=31;sig++) signal(sig,handler);
    int a = 10;
    cout<< a/0<<endl;
    return 0;
}

Let's write another array out-of-bounds access to see the effect

    int a[100];
    a[10000000] = 100;

(End of this article)

Guess you like

Origin blog.csdn.net/qq_58325487/article/details/128129994