[Linux] Signal concept and signal generation


1. What is a signal?

Take daily life as an example, signal flares, school bells, animal courtship behavior, traffic lights, etc. These are all signals.


How do we recognize signals?
From a young age, our parents and teachers teach us to recognize these signals.


After the signal is generated, I may not process the signal immediately, but I remember that there is this signal. So the signal must be processed within a certaintime window in the future.

For example: when I ordered takeout in the dormitory, the delivery man called me. I was playing a game at the time. As soon as I hung up the phone, I went to the high ground. Then after finishing the game, I went down to get the takeout.
This is a typical signal that is not processed immediately after being received, but is processed within a certain time window in the future.


For processes:

  • 1. The process must have the ability to identify and process signals. Even if no signal is generated, it must be able to identify signals that may be generated in the future, which is a built-in function of the process.
  • 2. When the process really receives a signal, it may not process the signal immediately, but will process it at an appropriate time.
  • 3. A process must have the ability to generate a signal and save the signal within the time window until the signal is processed.

According to the second point above, there are three ways for a process to handle signals:

  • Default action
  • neglect
  • Custom action

Ask a question:
Why can ctrl + c kill the foreground process?

Foreground process:
In Linux, during a login, a terminal is equipped with a bash. For each login, only one process is allowed to be the foreground process.

In other words, our bash, the process that inputs commands, is the foreground process. After writing the code and running a program, the running process is the foreground process, and bash is turned into a background process.

When the keyboard is inputting, it is obtained by the foreground process.

Therefore, when a loop is executed, if you enter ctrl + c on the keyboard, it will be obtained and recognized by the foreground process as signal No. 2. Signal No. 2 is to interrupt the process.

So the essence of ctrl + c is that it is recognized by the process as signal No. 2.

1.signal system call

As mentioned earlier, there are three ways for a process to handle signals.
For signal No. 2, the default way for the process to handle the signal is to terminate itself.
The other two processing methods are solved by the signal function.
Insert image description here
The first parameter: signum, the signal number.
The second function: The type of this parameter is a function pointer type, which is actually a custom way of processing the signum signal.

In other words, when calling the signal system call, if parameter No. 2 is passed in, when signal No. 2 is encountered in the future, the processing method is to call the custom processing method of the handler function.

void myheader(int sig)
{
    
    
    cout << "process get a signal : %d "<< sig << endl;
}

int main()
{
    
    
    signal(SIGINT,myheader);
    int cnt = 50;
    while(cnt--)
    {
    
    
        cout << "I am a process" << endl;
        sleep(1);
    }
    return 0;
}

Insert image description here
When signal No. 2 is encountered, the custom processing method is to call the handler function written by yourself.

Note: The signal function only needs to be set once and it will always be valid. Whenever signal No. 2 is encountered in the future, a custom signal processing method will be used, that is, the handler function will be called.


2. How to parse keyboard data from hardware and input it to the kernel

First of all, as long as the keyboard is pressed, the operating system must know it first!

So, how does the operating system know there is data on the keyboard?

There are many hardwares in the computer. As long as we press the keys on the keyboard, a signal interrupt will be generated.
It is sent to the interrupt unit in the form of high and low current, and the interrupt unit then Send the signal to the pin of the CPU. After the CPU receives the signal, it notifies the operating system and sends the interrupt unit number to the operating system. After the operating system receives the signal, it executes the address of the method corresponding to the interrupt number in the interrupt vector table.

Then execute the method of reading the keyboard, that is, copy the contents pressed on the keyboard to the kernel buffer in the memory.
At this point, the operating system has completed the action of knowing that there is data on the keyboard and copying the data to the kernel buffer.

Insert image description here

Therefore, the operating system no longer needs to detect the signal of each hardware, and only needs to wait for the CPU to send the corresponding interrupt signal.


Insert image description here

For the above background process, I entered l and then s, but the problem was displayed on the screen out of order.

In fact, the principle is as follows:

When the keyboard is pressed, the keys are loaded into the buffer. When the l key is pressed for the first time, there is an l key in the keyboard buffer, and then the character is copied to the display buffer. , print to the monitor. At this time, the background process keeps running and prints the corresponding string to the monitor, causing the printing to be out of order.
But it does not affect the l character in the keyboard buffer at all.

Because the keyboard file and the display file are two different files, each with different buffers.
They do not interfere with each other, so when you enter s again, the keyboard buffer will form an ls command, and then copy the s to the monitor buffer and print it to the monitor file, and you will see the disorder. ls command, but after pressing the Enter key, the keyboard will send a hardware interrupt to the CPU. Just execute the ls command.

Insert image description here

3. Synchronous and asynchronous

The generation of signals and the execution of the code we write are asynchronous.

For example:

Zhang San and I went to the self-study room to study. Zhang San said that I would get a book when I got to the dormitory, and then I would wait for Zhang San downstairs. After waiting for Zhang San, we would go to self-study together. The process of us going together is synchronized.
If Zhang San and I go to the study room to study, and Zhang San says he is going to get books, I will leave first without waiting for him, and then I will not go to the study room at the same time as Zhang San. This process is asynchronous.

2. Generation of signals

There are five ways of generating signals.

1. Keyboard key combinations

Insert image description here

  • 1.ctrl + cGeneration 2 signal.
  • 2.ctrl + \Transportation No. 3 signal.

2. kill command

kill -signo + process pid

Send corresponding signals to specific processes.

3. System call interface

3.1kill

Insert image description here
Send the sig signal to the pid process.

//实现一个kill命令
void Usage(string name)
{
    
    
    cout << "Usage:\n\t" << name << " signo pid" << endl;
}

int main(int argc,char* argv[])
{
    
    
    if(argc != 3)
    {
    
    
        Usage(argv[0]);
        exit(1);
    }

    int signo = stoi(argv[1]); //获取信号码
    pid_t pid = stoi(argv[2]); //获取信号id
    int n = kill(pid,signo);
    if(n < 0) //kill 失败了
    {
    
    
        perror("kill");
        exit(1);
    }
    return 0;
}

3.2 raise

Sends the specified signal to the caller.
That is, send yourself a specified signal.

Insert image description here
The essence of this function is to callkill(getpid(),sig);

3.3abort

Send yourself signal number 6.
Insert image description here

The bottom layer of this function is to callKill(getpid(),6)

4. Abnormality

When an exception occurs in the code, a signal is sent to the process.

//模拟异常后发信号
void handler(int sig)
{
    
    
    cout << "receive " << sig << " signo!"<<endl;
    sleep(1);
}

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

    int a = 10;
    a/=0;
    return 0;
}

Insert image description here
Here is a question: Why does the signal keep getting triggered? How does the operating system know that the process has sent a signal?

Insert image description here
The register eip/pc in the CPU will record the step of the code in the process.
There is a register in the CPU called the status register. Although this status register is also a register, it is set as a register with bits, which means that one register can represent multiple states.

The data stored in these status registers, eip registers, etc. are called Process context.

When a process exits, it will take away all the context data about the process itself. When the next process is scheduled for execution by the CPU, it will load the context data of its own process into the CPU's registers.

Therefore, if the previous process modifies the contents of the status register, it will not affect the next process at all! ! !

So although we modified the contents of the status register, it only affects my process itself!

Therefore, when an exception occurs in a process, a certain bit in the status register will be set from 0 to 1. In order to ensure the health of the CPU, the operating system must frequently check whether the contents of the register have been modified, so as to obtain whether the process is abnormal! !

The CPU is also hardware, and the operating system is the manager of the hardware! ! !

Why doesn't the operating system directly help the process process these signals based on the signals sent by the process?

Because the purpose of the signal is to make the process understand clearly! !

Processes are assigned by the operating system to perform various tasks, and these tasks are given by the user. Once an exception occurs during the execution of a task and a signal is generated, the process needs to know why it generated the signal, so that it can give an explanation even if it dies!

Exceptions appear only to handle subsequent work in one place, not to solve them.

Exceptions cannot be resolved, and the user can only be informed of the cause and change the corresponding strategy.

5.Software conditions

Software conditions to implement alarm clock

The alarm function implements an alarm clock.
Insert image description here

Set a function to implement an alarm clock. It will sound after seconds and then send signal No. 14 to the process to terminate the process.
The return value is: the remaining time of the last set alarm.

If the first alarm is set for 10 seconds, and after 5 seconds, another alarm is set. When the second alarm is set at this time, the return value is the remaining time of the first alarm, which is 5.

void handler(int sig)
{
    
    
    cout << "receive a " << sig << " signal" << endl;
}

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

    int n = alarm(5);
    while(true)
    {
    
    
        cout << "i am a process ,pid : " << getpid() << endl;
        sleep(1);
    }
    return 0;
}

This code verifies the conclusion that the alarm clock sounds and signal 14 is sent to the process.


Re-discussing the core dump flag

Insert image description here
In the process waiting phase, one of the bits in the status parameter that the process is waiting for is calledcore dump标志位

The meaning of this bit is:
Once the process is abnormal, the OS will dump (store, dump) the running information of the process in the memory to the process. The current directory (disk) forms the core.pid file. This process is called core dump. and set the corresponding bit of the core dump to 1 to tell the user. Why is there a core dump function? This is to facilitate post-debugging, that is, debugging after the code goes wrong.

use

ulimit -a checks some configurations of the system. The first one is the core file size. The size of the core file is 0, which means the core function is not turned on.
Use < a i=2> ulimit -c 10240, set the core file size to a maximum of 10240 bytes, which is equivalent to turning on the core function.

Insert image description here

After writing code in the .cc file to write a divide-by-zero error, you will find the following result:
Insert image description here

That means that after an exception occurs in the process, a core.pid file is generated, which dumps the running information of the process in memory to the directory of the current process.

After opening the debugger to debug the current executable program,Insert image description here
the results are as follows. After importing the core.pid file, you can see where the specific errors occur. Such debugging is Post-debugging.

Among them, the default cloud server does not have the core dump function turned on.

Because in the company's server, if a certain service hangs up, no matter why it hangs up, restart it immediately, restart the service immediately, and then troubleshoot other problems based on the logs. However, if the core dump function is turned on, every time a service hangs up, a core.pid file will be formed. This file is quite large. If many services hang up during a certain period of time, or a service After it hangs, it restarts, and after restarting it hangs again. This will generate a large number of core.pid files over and over again, and over time the disk space will be filled up. At this time, it is no longer as simple as the service hanging up, but it may change. It has become a serious problem that the operating system hangs up!

Guess you like

Origin blog.csdn.net/w2915w/article/details/134637560