Why does the operating system have the concept of context? Take you in-depth understanding of context basics

Go to: https://www.sohu.com/a/201480740_777180

Before talking about process context, interrupt context, and atomic context, it is necessary to discuss the following two concepts:

a - context

Context is translated from English context and refers to an environment. Relative to the process, it is the environment when the process is executed;

Specifically, each variable and data, including all register variables, files opened by the process, memory information, etc.

b - atom

Atom (atom) originally means "the smallest particle that cannot be further divided", and atomic operation means "an operation or a series of operations that cannot be interrupted";

1. Why is there a concept of context

Kernel space and user space are two working modes of modern operating systems. The kernel module runs in the kernel space, and the user mode application runs in the user space. They represent different levels and have different access rights to system resources. The kernel module runs at the highest level (kernel mode), all operations at this level are trusted by the system, and applications run at a lower level (user mode). At this level, the processor controls direct access to hardware and unauthorized access to memory. The kernel mode and user mode have their own memory mapping, that is, their own address space.

The processor is always in one of the following states:

Kernel mode , running in the context of the process , the kernel represents the process running in the kernel space;

Kernel mode , running in the interrupt context , the kernel represents the hardware running in the kernel space;

User mode, running in user space.

The two different operating states of the system have the concept of context. If an application in the user space wants to request system services, such as operating a physical device and mapping the address of the device to the user space, it must be implemented through system calls. (System calls are interface functions provided by the operating system to the user space).

Through the system call, the user space application program will enter the kernel space, and the kernel will run in the kernel space on behalf of the process. This involves context switching. The user space and the kernel space have different address mappings, general or special register sets. , And the user space process has to pass many variables and parameters to the kernel, and the kernel should also save some registers and variables of the user process so that it can return to the user space to continue execution after the system call ends.

Two, process context

The so-called process context refers to the values ​​in all registers of the CPU, the state of the process, and the content on the stack when a process is executing. When the kernel needs to switch to another process, it needs to save all the states of the current process, that is Save the process context of the current process so that when the process is executed again, the state at the time of switching can be restored and the execution can continue.

The context of a process can be divided into three parts: user-level context, register context, and system-level context.

User-level context: body, data, user stack, and shared memory area;

Register context: general registers, program registers (IP), processor status registers (EFLAGS), stack pointer (ESP);

System level context: process control block task_struct, memory management information (mm_struct, vm_area_struct, pgd, pte), kernel stack.

When process scheduling occurs, the process switch is a context switch (context switch).

The operating system must switch all the information mentioned above before the newly scheduled process can run. The system call is a mode switch. Compared with process switching, mode switching is much easier and saves time, because the main task of mode switching is to switch the context of process registers.

The process context is mainly exception handlers and kernel threads. The reason why the kernel enters the process context is because some work of the process itself needs to be done in the kernel. For example, a system call serves the current process, and an exception is usually an error state caused by the processing process. So it makes sense to refer to current in the context of the process.

Three, interrupt context

The hardware sends an interrupt signal to the CPU through the trigger signal, causing the kernel to call the interrupt handler and enter the kernel space. In this process, some hardware variables and parameters are also passed to the kernel, and the kernel performs interrupt processing through these parameters.

Therefore, "interrupt context" can be understood as the parameters passed by the hardware and some environments that the kernel needs to save, mainly the environment of the interrupted process.

The kernel enters the interrupt context because of the interrupt processing or soft interrupt caused by the interrupt signal. The occurrence of interrupt signals is random, and interrupt handlers and soft interrupts cannot predict in advance which process is currently running when the interrupt occurs, so it is possible to reference current in the interrupt context, but it does not make sense.

In fact, the interrupt signal that the A process wants to wait for may occur during the execution of the B process. For example, process A starts a disk write operation, process B is running after process A sleeps, and process B is interrupted by a disk interrupt signal after the disk is written, and process A is awakened during interrupt processing.

Fourth, process context VS interrupt context

The kernel can be in two contexts: process context and interrupt context.

After the system call, the user application enters the kernel space, and then the kernel space runs in the process context for the representative of the corresponding process in the user space.

Asynchronous interrupts will cause the interrupt handler to be called, and the interrupt handler runs in the interrupt context.

Interrupt context and process context cannot occur at the same time.

The kernel code running in the process context is preemptible, but the interrupt context will run until the end and will not be preempted. Therefore, the kernel restricts the work of the interrupt context and does not allow it to perform the following operations:

a - Go to sleep or actively give up the CPU

Since the interrupt context does not belong to any process, it has nothing to do with current (although current points to the interrupted process at this time), so once the interrupt context sleeps or gives up the CPU, it cannot be awakened. Also called atoms context (context Atomic ) .

b - Occupy the mutex

In order to protect the resources of the critical section of the interrupt handler, mutexes cannot be used. If the semaphore cannot be obtained, the code will sleep, and the same situation as above will occur. If a lock must be used, spinlock is used.

c - Perform time-consuming tasks

Interrupt processing should be as fast as possible, because the kernel has to respond to a large number of services and requests, and the interrupt context occupies too much CPU time, which will seriously affect system functions. When performing time-consuming tasks in an interrupt handling routine, it should be handled by the bottom half of the interrupt handling routine .

d - Access user space virtual memory

Because the interrupt context is not related to a specific process, it is the kernel running in the kernel space on behalf of the hardware, so the virtual address in the user space cannot be accessed in the interrupt context

e - Interrupt handling routines should not be set to reentrant (routines that can be called in parallel or recursively)

Because when an interrupt occurs, preempt and irq are disabled until the interrupt returns. Therefore, the interrupt context is not the same as the process context, and different instances of interrupt processing routines are not allowed to run concurrently on SMP.

f - Interrupt handling routine can be interrupted by higher level IRQ

If you want to prohibit this kind of interrupt, you can define the interrupt processing routine as a fast processing routine, which is equivalent to telling the CPU that when the routine is running, all interrupt requests on the local CPU are prohibited. The direct result of this is that the system performance is degraded because other interrupts are delayed in response.

Five, atomic context

A basic principle of the kernel is: in an interrupt or atomic context, the kernel cannot access user space, and the kernel cannot sleep. That is to say, in this case, the kernel cannot call any function that may cause sleep. Generally speaking, the atomic context refers to the interrupt or soft interrupt, and while holding a spin lock. The kernel provides four macros to determine whether it is in these situations:

1. #define in_irq() (hardirq_count()) //In processing hard interrupt

2. #define in_softirq() (softirq_count()) //In processing soft interrupt

3. #define in_interrupt() (irq_count()) //In processing hard or soft interrupt

4. #define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != 0) //Include all the above

The count accessed by these four macros is thread_info->preempt_count. This variable is actually a bit mask. The lowest 8 bits represent the preemption count, which is usually modified by spin_lock/spin_unlock, or forcibly modified by the programmer, and indicates that the maximum preemption depth allowed by the kernel is 256.

Bit 8-15 is the soft interrupt count, which is usually modified by local_bh_disable/local_bh_enable, and indicates that the maximum soft interrupt depth allowed by the kernel is 256.

Bit 16-27 is the hard interrupt count, which is usually modified by enter_irq/exit_irq, and indicates that the maximum hard interrupt depth allowed by the kernel is 4096.

The 28th bit is the PREEMPT_ACTIVE flag. The code means:

PREEMPT_MASK: 0x000000ff

SOFTIRQ_MASK: 0x0000ff00

HARDIRQ_MASK: 0x0fff0000

All the places where the above 4 macros return 1 are atomic contexts. The kernel is not allowed to access user space, the kernel is not allowed to sleep, and any functions that may cause sleep are not allowed to be called. And it means thread_info->preempt_count is not 0, which tells the kernel that preemption is disabled.

However, for in_atomic(), when preemption is enabled, it works very well. It can tell the kernel whether it currently holds a spinlock, whether to disable preemption, etc. However, when preemption is not enabled, spin_lock does not modify preempt_count at all, so even if the kernel calls spin_lock and holds the spin lock, in_atomic() still returns 0, which incorrectly tells the kernel that it is currently in a non-atomic context. Therefore, any code that relies on in_atomic() to determine whether it is in the atomic context is problematic when preemption is prohibited.

Guess you like

Origin blog.csdn.net/qq_36171263/article/details/97370222