Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to realize the preemption of kernel state in Linux

2025-03-01 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >

Share

Shulou(Shulou.com)05/31 Report--

This article introduces the knowledge of "how to achieve Linux kernel state preemption". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!

1. The difference between non-preemptive and preemptive kernels

To simplify the problem, I use the embedded real-time system uC/OS as an example. The first thing to point out is that uC/OS has only kernel state, not user state, which is different from Linux.

In a multitasking system, the kernel is responsible for managing each task, or allocating CPU time for each task, and is responsible for communication between tasks. The basic service provided by the kernel is task switching. Scheduler, there is also a word called dispatcher in English, which also means scheduling. This is one of the main responsibilities of the kernel, which is to decide which task to run. Most real-time kernels are based on priority scheduling. Each task is given priority according to its degree of importance. Priority-based scheduling means that CPU always allows tasks that are in the priority of ready state to run first. However, there are two different situations when high-priority tasks are given access to CPU, depending on what type of kernel is used, whether it is an inexorable or strippable kernel.

Non-preemptive kernel

A non-preemptive kernel allows the task to actively relinquish the right to use CPU. Non-preemptive scheduling is also known as cooperative multitasking, in which each task cooperates with each other and shares a CPU. Asynchronous events are still handled by interrupting the service. Interrupting the service can change a high-priority task from a pending state to a ready state. However, after the interruption of the service, the control will return to the original interrupted task, and the high-priority task will not get the right to use CPU until the task actively relinquishes the right to use CPU. The non-preemptive kernel is shown in the following figure.

The advantages of a non-preemptive kernel are:

Fast interrupt response (compared with preemptive kernel)

Allow the use of non-reentrant functions

There is little need to use semaphores to protect shared data. Running tasks occupy the CPU, so you don't have to worry about being preempted by other tasks. This is not absolute, in the use of printers, mutual exclusion conditions still need to be met.

The disadvantages of a non-preemptive kernel are:

The task response time is slow. The high-priority task is ready but not ready to run until the currently running task releases CPU.

The task-level response time of the non-preemptive kernel is uncertain, and it is entirely up to the application to release the CPU when the priority task will gain control of the CPU.

Preemptive kernel

The system response time can be guaranteed by using a preemptive kernel. Once the priority task is ready, you can always get the right to use CPU. When a running task puts a higher priority task into a ready state, the current task is deprived of its CPU use, or suspended, and that high priority task immediately takes control of the CPU. If the interrupt service subroutine puts a high-priority task into a ready state, when the interrupt is completed, the interrupted task is suspended and the high-priority task starts to run. The preemptive kernel is shown in the following figure.

The advantages of a preemptive kernel are:

With a preemptive kernel, it is known when priority tasks can be executed and that you can get the right to use CPU. The use of preemptive kernels makes task-level response time possible.

The disadvantages of a preemptive kernel are:

You cannot use non-reentrant functions directly. When calling a non-reentrant function, the mutex condition should be met, which can be achieved by using mutually exclusive semaphores. If the non-reentrant function is called, the low-priority task CPU is deprived of the right to use the high-priority task, and the data in the non-reentrant function may be destroyed.

2. User state preemption and kernel state preemption under Linux

Linux has user mode in addition to kernel mode. The context of the user program belongs to the user state, and the context of the system call and interrupt handling routines belong to the kernel state. Prior to 2. 6 kernel, Linux kernel only supported user-mode preemption.

2.1user mode preemption (User Preemption)

When kernel returns user state (user-space) and the need_resched flag is 1, scheduler is called, which is user state preemption. When kernel returns to user mode, the system can safely perform the current task or switch to another task. When the kernel returns to user state after the interrupt handling routine or system call is completed, the value of the need_resched flag is checked. If it is 1, the scheduler selects a new task and executes it.

The interrupt and system call return path (return path) is implemented in entry.S (entry.S includes not only kernel entry code but also kernel exit code).

2.2 Kernel state preemption (Kernel Preemption)

Until 2. 6 kernel, kernel code (interrupts and system calls belonging to kernel code) runs until the code is completed or blocked (system calls can be blocked). In 2.6kernel, Linux kernel becomes preemptive. When returning from the interrupt handling routine to the kernel state (kernel-space), kernel checks to see if it can be preempted and needs to be rescheduled. Kernel can preempt a task at any point in time (because interruptions can occur at any point in time), as long as the state of the kernel is secure and reschedulable at that point in time.

3. Design of kernel state preemption

3.1 preemptive conditions

What conditions must be met before kernel can preempt the kernel state of a task?

There's no lock. Locks are used to protect critical areas and cannot be preempted.

Kernel code is reentrant (reentrant). Because kernel is SMP-safe, it satisfies reentrancy.

How to determine that the current context (interrupt handling routines, system calls, kernel threads, etc.) does not hold locks? Linux adds the preempt_count variable as a counter for preemption in the thread_info structure of each task. This variable is initially 0, and the counter increases by one when locked and minus one when unlocked.

3.2 trigger conditions for preemptive kernel states

The kernel provides a need_resched flag (which is in the task structure thread_info) to indicate whether rescheduling is required.

3.3 when to trigger rescheduling

Set_tsk_need_resched (): sets the need_resched flag in the specified process

Clear_tsk need_resched (): clears the need_resched flag in the specified process

Need_resched (): check the value of the need_resched flag; return true if it is set, false otherwise

When do you need to reschedule:

The clock interrupt processing routine checks the time slices of the current task. When the time slices of the task are exhausted, the scheduler_tick () function sets the need_resched flag.

Semaphores, queuing, completion and other mechanisms are all based on waitqueue when waking up, while the wake-up function of waitqueue is default_wake_function, which calls try_to_wake_up to change the awakened task to ready state and sets the need_resched flag.

Setting the nice value of a user process may bring high-priority tasks into a ready state

When you change the priority of a task, it may bring a high-priority task into a ready state

When you create a new task, it may bring high-priority tasks into a ready state

When load balancing CPU (SMP), the current task may need to be run on another CPU

3.4 preemption of the timing of occurrence (when to check preemption conditions)

When an interrupt handling routine exits and returns to the kernel state (kernel-space). This is an implicit call to the schedule () function, and instead of actively giving up the right to use CPU, the current task is deprived of the right to use CPU.

When the kernel code changes from a non-preemptable state to a preemptable state (preemptible again). That is, when preempt_count changes from a positive integer to 0. This is also an implicit call to the schedule () function.

A task explicitly calls the schedule () function in the kernel state. The task actively relinquishes the right to use CPU.

A task is blocked in the kernel state, resulting in a call to the schedule () function. The task actively relinquishes the right to use CPU.

3.5 actions that disable / enable preemptive conditions

The functions that operate on preempt_count are add_preempt_count (), sub_preempt_count (), inc_preempt_count (), and dec_preempt_count ().

The operation that enables the preemption condition is preempt_enable (), which calls the dec_preempt_count () function and then calls the preempt_check_resched () function to check if rescheduling is needed.

The action to disable the preemption condition is preempt_disable (), which calls the inc_preempt_count () function.

There are many functions in the kernel that call preempt_enable () and preempt_disable (). For example, the spin_lock () function calls the preempt_disable () function, and the spin_unlock () function calls the preempt_enable () function.

3.6 when preemption is not allowed

The preempt_count () function is used to get the value of preempt_count, and preemptible () is used to determine whether the kernel is preemptive or not.

There are several situations where the Linux kernel should not be preempted, except that the Linux kernel can be preempted at any point. These situations are as follows:

The kernel is doing interrupt processing. In the Linux kernel, processes cannot preempt interrupts (interrupts can only be aborted and preempted by other interrupts, processes cannot be aborted, preemptive interrupts), and process scheduling is not allowed in interrupt routines. The process scheduling function schedule () determines this and prints an error message if it is called during an interrupt.

The kernel is doing Bottom Half (the lower half of the interrupt) processing of the interrupt context. The soft interrupt is executed before the hardware interrupt returns, and it is still in the interrupt context.

The code snippet of the kernel is holding locks such as spinlock spin locks, writelock/readlock read-write locks, and so on, drying the protected state of these locks. The purpose of these locks in the kernel is to ensure the correctness of concurrent execution of processes running on different CPU in a short period of time in SMP systems. When holding these locks, the kernel should not be preempted, otherwise preemption will cause other CPU to be unable to acquire locks for a long time and die.

The kernel is executing the scheduler Scheduler. The reason for preemption is for new scheduling, there is no reason to preempt the scheduler and then run the scheduler.

The kernel is "proprietary" data structure manipulation (Per-CPU date structures) for each CPU. In SMP, per-CPU data structures are not protected by spinlocks because they are implicitly protected (different CPU have different per-CPU data, and processes running on other CPU do not use another CPU's per-CPU data). However, if preemption is allowed, but a process is preempted and rescheduled, it is possible to schedule to other CPU, then there will be a problem with the defined Per-CPU variable, and preemption should be prohibited.

4. Implementation of kernel state preemption in Linux

4.1 data structure

[cpp] view plain copy

Struct thread_info {struct task_struct * task; / * main task structure * / struct exec_domain * exec_domain; / * execution domain * / / * if there is a TIF_NEED_RESCHED flag, the scheduler must be called. * / unsigned long flags; / * low level flags * / / * Thread flag: * TS_USEDFPU: indicates whether the process has used FPU, MMX and XMM registers during the current execution. * / unsigned long status; / * thread-synchronous flags * / / * the CPU logical number of the running queue in which the process can be run. * / _ U32 cpu; / * current CPU * / _ S32 preempt_count; / * 0 = > preemptable, BUG * / mm_segment_t addr_limit / * thread address space: 0-0xBFFFFFFF for user-thead 0-0xFFFFFFFF for kernel-thread * / struct restart_block restart_block; unsigned long previous_esp / * ESP of the previous stack in case of nested (IRQ) stacks * / _ U8 supervisor_stack [0];}

4.2 Code flow

Functions that disable / enable preemptive conditions

[cpp] view plain copy

# ifdef CONFIG_DEBUG_PREEMPT extern void fastcall add_preempt_count (int val); extern void fastcall sub_preempt_count (int val); # else # define add_preempt_count (val) do {preempt_count () + = (val);} while (0) # define sub_preempt_count (val) do {preempt_count ()-= (val) } while (0) # endif # define inc_preempt_count () add_preempt_count (1) # define dec_preempt_count () sub_preempt_count (1) / * Select the preempt_count field * / # define preempt_count () (current_thread_info ()-> preempt_count) # ifdef CONFIG_PREEMPT asmlinkage void preempt_schedule (void) in the thread_info descriptor / * increase preemption count by 1 * / # define preempt_disable ()\ do {\ inc_preempt_count ();\ barrier ();\} while (0) / * subtract preemption count by 1 * / # define preempt_enable_no_resched ()\ do {\ barrier () \ dec_preempt_count ();\} while (0) # define preempt_check_resched ()\ do {\ if (unlikely (test_thread_flag (TIF_NEED_RESCHED)\ preempt_schedule () \} while (0) / * subtracts the preemption count by 1 and calls preempt_schedule () * / # define preempt_enable ()\ do {\ preempt_enable_no_resched ();\ preempt_check_resched () when the TIF_NEED_RESCHED flag of the thread_info descriptor is set to 1. While (0) # else # define preempt_disable () do {} while (0) # define preempt_enable_no_resched () do {} while (0) # define preempt_enable () do {} while (0) # define preempt_check_resched () do {} while (0) # endif

A function that sets the need_resched flag

[cpp] view plain copy

Static inline void set_tsk_need_resched (struct task_struct * tsk) {set_tsk_thread_flag (tsk,TIF_NEED_RESCHED);} static inline void clear_tsk_need_resched (struct task_struct * tsk) {clear_tsk_thread_flag (tsk,TIF_NEED_RESCHED);} "how to achieve kernel state preemption in Linux" ends here. Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Servers

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report