In the previous blog post real-time operating system kernel task Scheduling point summarizes the RTOs task scheduling time, when the operating system kernel decides to run another task, it will be the context of the current task, usually refers to the CPU register, save to the time of the task of the stack, and restore the context of the new task to keep it running, this process is called context switching. The above paragraph, almost in each of the embedded software teaching materials, materials will have, but can speak a little deeper but not much. Landlord in the embedded industry in the first n years has been stuck in the understanding of this sentence, but always to the mysterious process is very curious and no avail. Why do the classics not describe the process in detail? Landlord thought, the reasons are as follows: 1 context switching is a process that is closely related to the operating system kernel, and the RTOs manufacturers are already writing the code for debugging through the sale of their operating system kernel, so there are not many people who can really understand the process of writing and debugging. And because this part of the content is very stable, once successfully run, there is little need to modify. No need to change, who will go to see it. 2 context switching is a processor-related process, the so-called context is generally referred to as the CPU registers, and each processor register is different, the switching action is not the same, this article takes MIPS as an example to explain. 3 Embedded application developers mostly use C language for development, and do not pay attention to these specific switching processes, in their eyes, this is a very natural process. And the context switching process because of the direct operation of the CPU register, it is necessary to use the assembly or embed the assembly in the C language code to achieve, this will make many people "look at the assembly of Fear", do not know these registers move to move over what the past is doing. What's more, it's almost impossible to understand context switching by using ordinary debugging methods like printing, only through a hardware debugger (such as JTAG) to step through the CPU instructions and look at the changes in memory to be able to really understand.
For embedded architects, system software engineers, there should be no technical corner. Landlord in this article with a common running track (as shown in Figure 1), a situational analysis of the process of multiple task context switching, hoping to be interested in crossing a little help or inspiration.
As can be seen in Figure 1, the CPU runs as follows: High priority task--> Low priority task--> interrupt-to-high priority task, (the high-priority task is abbreviated to Hightask, and the low-priority task is abbreviated to lowtask). Assuming the system has only 2 different priority tasks, from a certain moment, Hightask is running, it has been running for a period of time, into the block, the CPU to Lowtask, this is the first context switch; Lowtask interrupted during execution, this is the second time , interrupts to run for a period of time, and will be sent by sending a message or a signal, such as trigger Hightask continue to run, this is the third; Hightask wakes up for a while, and then gives the CPU to Lowtask again, this is the fourth time. Let's look at it one by one. When each task is created, a stack is created for the system kernel (some systems apply their own stack space) to hold data such as function stack frames and temporary variables in the call. Before the first context switch occurs, assume that the Hightask function call procedure before yielding the CPU is: Funca-->funcb-->xxx_waitmsg-->xxx_contextswitch, Its stack distribution is shown in Figure 2:
Figure 2 This is a downward-growing stack, from the high address down to the Funca, FUNCB, xxx_waitmsg, Xxx_contextswitch function stack frame, Hightask is called xxx_waitmsg actively let the CPU, This function will routinely remove the current task from the ready state linked list, hanging to the list of blocked states, to find the next task to run (in fact, to find its task control block structure and the SP pointer inside, about the task scheduling algorithm refer to ucosii real-time operating system task scheduling), and then call XXX _contextswitch to switch contexts, the implementation of Xxx_contextswitch is generally as follows {Move the current task's stack pointer to make a memory save the current task's context find the next stack pointer to run the task to restore the context of the next task, The last step is to assign the saved PC value to the CPU register} There is a need to introduce two "original concepts": full context and partial context. The full context is the variable register content that is used in the CPU; Consider this case,hightask finally executes the Xxx_contextswitch function, very calmly let out the CPU, it knows to be switched out before the execution of an instruction where, And also know that the execution to this command here, which registers the value is meaningful, these are needed to be saved in the re-dispatch to restore the scene of the "partial context." In the landlord experienced the switch of a MIPS processor, only need to save s0,s1, and RA3 registers, in addition to add a flag field to indicate whether this save is a partial or complete context, after saving the stack structure as shown in Figure 3:
Figure 3 After this step is done, it is important to update the stack pointer of the current task to a global data structure, which holds the stack pointers for all the task. With this data structure, we can get a stack pointer to any task at any time to restore its running scene.
The context of the hightask is finished, and then the context of the lowtask is restored, because it is unclear whether the context of Lowtask is partial or complete, and this step is skipped. Lowtask begins execution, and after a period of time, the system comes up with an interrupt because the interruption occurs at any time and is unpredictable, so the context preservation is a complete context. Assuming that the Lowtask function call procedure is FUNCC-->FUNCD, the stack should be as shown in Figure 4 before being interrupted:
Figure 4 After the interrupt arrives, the MIPS CPU will move the PC to a fixed-address exception vector table to execute, the process is as follows: {Decrease the stack pointer of the current task to make a memory save the full context of the current task record the current task's stack pointer to the GP register Set the SP register of the CPU to the interrupt stack address (typically a global variable) to execute the interrupt response function, commonly known as an IRQ (this step may trigger a more hightask, need to change the GP register value to Hightask SP) from the GP register to remove the next task stack pointer to run The final step in recovering the task context is to assign the saved PC value to the CPU register} The full context information that the MIPS processor needs to save almost all of the CPU registers, such as: At,v0,v1,a0~a3,t0~t9,s0~s8,ra,epc ( Exception PC, which records the address of the instruction to be executed before the lowtask is interrupted, and the result of the multiplier (obtained by instruction Mflo and MFHI), plus the flag indicating that this is the full context. The stack memory structure after Lowtask is saved as shown in Figure 5:
As you can see in Figure 5, full context saving and recovery is significantly more time-consuming than a partial context. In the second half of the exception processing, the SP pointer taken from the GP register is the stack of hightask, first it needs to determine its flag, refer to Figure 3, is a partial context, then take s0,s1 out of the stack into the register, and then remove the value of RA, Execute the instructions below to go back to the scene before Hightask sleep (before executing the JR command, you need to move the SP pointer and point to the Xxx_contextswitch stack frame). JR RA Good, now Hightask continue to execute, after a period of time, but also repeat the previous story, Hightask actively let the CPU, do a partial context save, and find Lowtask SP, restore its context. Same, first find the context of the flag,lowtask is the full context of interrupted interrupts, and then one of these register values from the stack to be returned to the CPU register, the last step back to Lowtask continue to execute the instruction is the JR K0 (K0 Register saved EPC, reason reference 32 Universal registers for MIPS)
The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion;
products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the
content of the page makes you feel confusing, please write us an email, we will handle the problem
within 5 days after receiving your email.
If you find any instances of plagiarism from the community, please send an email to:
info-contact@alibabacloud.com
and provide relevant evidence. A staff member will contact you within 5 working days.