The working principle of preemption without MMU preemptive operating system~

The operating system is a very mysterious thing for beginners, many of which are not clear, leading to give up before getting started.

This article will start from the bottom and describe in detail the principle of preemption of preemptible operating systems (most RTOS).

contain:

  • Thread basic principles

  • Preemption between threads

  • Interrupt preemption thread

  • Interrupt preemption interrupt

strongerHuang

1

Run multiple threads

1. Single core "single thread"

Strictly speaking, a single-core processor can only execute one instruction at a time, which means it can only be "single thread." (Of course, multi-core processors are not the same)

In order to run multiple threads on a single-core processor, we actually need to quickly switch between threads on a regular basis so that users feel that multiple threads are running in parallel.

For example, the processor executes two threads, and the processor actually switches back and forth between the two threads, as shown in the following figure:

2. The processor switches between threads, how does it do it?

The single-core processor we are talking about is "single thread", it has a set of registers, we call this set of registers belong to a "thread".

For example, when calculating the sum of two numbers:

//假设我们有两个整数:a和b
int c = a + b ;

The actual situation is as follows (of course, it depends on the type of MCU, but the general idea is the same):

# MIPS反汇编:


LW V0, -32744(GP)   # "a" 的值从RAM加载到寄存器V0
LW V1, -32740(GP)   # 值"b" 从RAM加载到寄存器V1
ADDU V0, V1, V0     # a、b值相加,结果保存到寄存器V0中
SW V0, -32496(GP)   # 寄存器V0的值存储在RAM中(变量c所在的位置)

You will find that 4 actions are performed above, but the preemptive operating system can preempt another thread at any time, including between these 4 actions.

If other threads preempt in this process, other threads also preempt the current threads V0 and V1. If V0 and V1 are not saved, the current thread will be executed next time, and the result will be wrong.

Therefore, for the current problem, we need to save the values ​​of V0 and V1 before switching threads. When switching to the current thread next time, restore the values ​​of V0 and V1. The general process is as follows:

The approximate meaning is: when we need to switch from one thread to another, the kernel gains control, performs the necessary housekeeping (at least save and restores the register value), and then transfers control to the next thread to run.

strongerHuang

2

Thread stack

Where is the preemption position mentioned above, and which register value is stored in each thread? This is the content of the thread's stack.

In an operating system with an MMU, the (user's) thread stack can dynamically grow as needed: the more stack space a thread requires, the more thread stack (if the kernel allows) .

However, our general MCU does not have the "high-end" thing of MMU, and all RAM is statically mapped to the address space. Therefore, each thread will have RAM space for the stack. If the RAM used by the thread exceeds the amount of the stack, it will cause memory overflow or subtle errors. (Actually, the stack space of each thread is just a contiguous array space).

Therefore, when we decide how much stack to allocate for each thread, we only estimate how much stack may be needed, but the exact amount may not be very clear.

For example, if this is a GUI thread with multiple nested calls, it may require several kilobytes, but if it is a small thread with a pipe light, tens of bytes may be sufficient.

Assuming we have three threads, their stack consumption is as follows:

As mentioned above, the register value of each thread is stored in the thread's stack. The set of register values ​​of a thread is called the "context" of the thread. As shown in the figure below (thread A is the "active thread" being executed):

Please note that the context of thread A is not saved in the stack, the stack pointer points to the top of thread A's user data, and the current processor's registers are dedicated to thread A.

When the kernel decides to switch control to thread B, it will do the following:

  • Save all register values ​​to the stack (save to the top of the thread A stack);

  • Switch the stack pointer to the top of the stack of thread B;

  • Restore all register values ​​from the stack (from the top of the stack of thread B);

At this point, you will see:

strongerHuang

3

Interrupt (ISR) preemption

During the execution of the above, or when performing context switching, a very important content may be involved: interruption .

MCU usually has peripherals: TIM, UART, SPI, CAN, etc., which can trigger interrupts by important events at any time.

The interrupt condition is that when the currently executing thread is suspended, the processor performs other operations (Handles Interrupt) for a period of time, and then returns. The interruption may be triggered at any time, and we should be prepared to deal with it.

The interrupt handler is called ISR (interrupt service routine):

Interrupts may have different priorities. For example, if some low-priority interrupts are triggered, the currently executing thread will be suspended and the ISR will gain control. Then, if a high-priority interrupt is triggered, the currently executing ISR will be suspended again, and a new ISR will be run for the high-priority interrupt.

In this way, after completion, control will return to the first ISR, and upon completion, the interrupted thread will also be restored.

Important key code:

In the process of thread activity, if there are important things "critical code", if interruption occurs in the process, it is easy to cause unexpected results.

We need to protect this part of the critical code. Usually our approach is to disable the global interrupt before the "critical code", and start the global interrupt after the execution is complete.

Something to note:

Turn off the global interrupt, there will be no corresponding interrupt at this time, so the "critical code" cannot be too long.

strongerHuang

3

Interrupt stack

As mentioned above, if high-priority interrupts preempt low-priority interrupts, there will be a problem: low-priority code needs to be the same as threads to save the data stack.

There are generally two methods:

  • Use the interrupted thread stack;

  • Use a separate stack space for interrupts;

1. Use the interrupted thread stack

If you use the interrupted thread stack, it looks like the following figure:

This situation has a serious problem for you. Do you know what it is?

Frequent interrupts, or more interrupts, the thread's own stack space will soon be used up.

The stack of each thread should contain the following:

  • Thread's own data;

  • The context of the thread;

  • The data used to perform the worst-case ISR.

Therefore, we need to change a method to provide a separate stack space for all ISRs.

2. Use a separate stack space for interrupts

The use of a separate stack space for interrupts is roughly as shown in the figure above.

Well, this article describes the above several kinds of preemption and related content, you have learned a few points, and some do not understand, welcome to leave a message to discuss.

1. Analyze the impact of "interpretability" on artificial intelligence from an embedded perspective!

2. [MCU] Register, standard library, HAL library, LL library, so many libraries! How do you ask me to choose?

3. How many steps are there to develop embedded projects with Linux?

4. How does the program itself know its size? This is a question of whether a chicken lays an egg or an egg lays a chicken!

5. Domestic integrated development environment helps domestic RISC-V break the monopoly of foreign giants in chip technology

6. When doing embedded development, how do you realize LCD display?

Disclaimer: This article is reproduced online, and the copyright belongs to the original author. If you are involved in copyright issues, please contact us, we will confirm the copyright based on the copyright certification materials you provide and pay the author's remuneration or delete the content.

Guess you like

Origin blog.csdn.net/DP29syM41zyGndVF/article/details/109792042