Real time operating system and non real time operating system
In the field of industrial control, we often hear about real-time systems and non real-time systems. Linux and Windows are typical non real-time systems. Typical real-time operating systems include VxWorks, RT thread, uCOS, QNX, WinCE, etc.
Relationship between "real-time and non real-time system" and "preemptive and non preemptive scheduling"
Preemptive scheduling: processes with high priority can preempt the CPU for execution. In this scheduling algorithm, the system always selects the algorithm with the highest priority for scheduling, and once the high priority task is ready, it will be scheduled immediately without waiting for the low priority task to give up the CPU.
Non preemptive scheduling: generally refers to polling scheduling. When a process is already in the kernel state, that is, an internal system call has been generated. Unless the CPU is voluntarily abandoned, the process will run until it completes or exits the kernel.
In RTOS, the main scheduling algorithm is preemptive scheduling based on task priority. If preemptive scheduling is not used, the work can not be completed within the specified time, because it is impossible to confirm when the process currently occupying the CPU will exit the CPU.
Therefore, real-time system and preemptive scheduling are interdependent. Without preemptive scheduling, there will be no real-time system. (I'll draw such a conclusion temporarily, but other methods of realizing real-time system have not been verified yet)
preempt preemption mechanism of Linux
The current Linux kernel adds the kernel preempt mechanism. Kernel preemption means that the user program can be preempted during the execution of system calls, and the process is temporarily suspended to enable the newly awakened high priority process to run. This preemption can not be safely carried out anywhere in the kernel. For example, the code in the critical area cannot be preempted. Critical area refers to the instruction sequence in which no more than one process can execute at the same time. In the Linux kernel, these parts need to be protected with spin locks.
How to avoid process preemption (spin lock)
How to use spin lock
We have learned that preemption is not allowed in some places in Linux. We should limit some preemption in the critical area. We should use spin lock for protection.
There are many tutorials on the use of spin lock, which will not be repeated here.
reference: https://blog.csdn.net/silent123go/article/details/52760424
After we lock the resource, the program will execute preempt_disable(), the code is as follows.
/** * When the kernel is not preemptive, spin_lock implementation process. */ #define _spin_lock(lock) \ do { \ /** * Call preempt_disable disables preemption. */ preempt_disable(); \ /** * _raw_spin_lock Perform atomic testing and setting operations on the slock field of the spin lock. */ _raw_spin_lock(lock); \ __acquire(lock); \ } while(0)
Conditions for judging whether preemption is possible (incomplete, to be supplemented)
void preempt_count_add(int val) is used to increase the reference count of the current process, which can prevent the current process from being preempted
The corresponding is void preempt_count_sub(int val)
It is used to the reference count of the current process, so that when the reference count is 0, the current process can be preempted
These two functions are a pair and are generally used together
The routines used are as follows:
#define __irq_enter() \ do { \ account_irq_enter_time(current); \ preempt_count_add(HARDIRQ_OFFSET); \ trace_hardirq_enter(); \ } while (0) /* * Exit irq context without processing softirqs: */ #define __irq_exit() \ do { \ trace_hardirq_exit(); \ account_irq_exit_time(current); \ preempt_count_sub(HARDIRQ_OFFSET); \ } while (0)
You can see that preempt is called when entering irq_ count_ Add to increase the reference count to avoid preemption. Leave irq by calling preempt_count_sub to reduce the reference count and enable preemption
The source code analysis is as follows:
void preempt_count_add(int val) { #ifdef CONFIG_DEBUG_PREEMPT /* * Underflow? */ if (DEBUG_LOCKS_WARN_ON((preempt_count() < 0))) return; #endif __preempt_count_add(val); #ifdef CONFIG_DEBUG_PREEMPT /* * Spinlock count overflowing soon? */ DEBUG_LOCKS_WARN_ON((preempt_count() & PREEMPT_MASK) >= PREEMPT_MASK - 10); #endif preempt_latency_start(val); }
Assume that config is not turned on_ DEBUG_ If preempt, then preempt_ count_ First called in add__ preempt_count_add to increase the reference count, and then call preempt_. latency_ Start to start
Start timing the latency. If no config is defined for this has_ DEBUG_ Preempt and CONFIG_PREEMPT_TRACER is also equivalent to an empty function
void preempt_count_sub(int val) { #ifdef CONFIG_DEBUG_PREEMPT /* * Underflow? */ if (DEBUG_LOCKS_WARN_ON(val > preempt_count())) return; /* * Is the spinlock portion underflowing? */ if (DEBUG_LOCKS_WARN_ON((val < PREEMPT_MASK) && !(preempt_count() & PREEMPT_MASK))) return; #endif preempt_latency_stop(val); __preempt_count_sub(val); }
Assume that config is not turned on_ DEBUG_ If preempt, ppreempt_ count_ Preempt is called first in sub_ latency_ Stop to Stop timing the latency to increase the reference count, and then call preempt_latency_start to start
And then call it. preempt_count_sub to reduce the reference count of the current process. If the reference count is 0, the process preemption is enabled
reference: http://blog.sina.com.cn/s/blog_502c8cc401012pxj.html