Experiment: Understand the process of process scheduling and process switching during time tracking analysis process
First, the experimental requirements
? (1) Understanding the timing of process scheduling in Linux, you can search the kernel code for the schedule () function, see where the schedule () is called, to determine whether the summary of our course content is accurate.
? (2) Use GDB Trace to analyze a schedule () function to verify your understanding of the process scheduling and process switching processes of the Linux system.
? (3) Pay special attention to and carefully analyze the assembly code in Switch_to, understand the switching mechanism of the process context, and the relationship with the interrupt context switch.
Ii. Experimental Process 1, understanding the switching mechanism of the process context, and the relationship to the interrupt context switch
? (1) User state process It is not possible to call schedule () directly when it is in the user, because schedule is a kernel function, and it is not a system call, it cannot be called directly, it can only be called indirectly, The time to call schedule () indirectly is to interrupt the process. For the user state process, it is to switch out from the current running process, then it must go into the interrupt, this interrupt is a general interrupt, there will be an interruption of the process can occur after the timing, so the general user-state process can only be passively dispatched. Eg running user-state process x switches to the process of running user-state process y:
Running user-state process X?① Interrupt--save cs:eip/esp/eflags (current) to kernel Stack,then load Cs:eip (entry of a specific ISR) and Ss:esp (PO int to kernel stack).? ②save_all//Save the scene? Schedule () is called during ③ interrupt processing or before an interrupt is returned, where Switch_to makes a critical process context switch? ④ designator 1 starts running user-state process Y ( Here Y has been switched out through the above steps so you can continue from the label 1? ⑤restore_all//Recovery site? ⑥iret-pop cs:eip/ss:esp/eflags from kernel stack continue to run user-state process y
? (2) [kernel thread] can directly call schedule () for process switching, can also be scheduled during interrupt processing, that is, the kernel thread as a class of special processes can be active scheduling, can also be passive scheduling, the user state process cannot realize the active scheduling, It can only be dispatched by a point in time after it is trapped in the kernel state, that is, scheduling during interrupt processing.
2, the timing of process scheduling
- Interrupt processing (including clock interrupts, I/O interrupts, system calls, and exceptions), call schedule () directly, or call schedule () based on the need_resched tag when returning to the user state;
- Kernel threads can directly call schedule () for process switching, or in the process of interrupt processing, which means that kernel threads as a special kind of process can be active scheduling, but also can be passively dispatched;
- The user-state process cannot implement the active scheduling, but can only be dispatched by a point in time after the kernel state, that is, scheduling during interrupt processing.
3, the process of switching
- To control the execution of the process, the kernel must have the ability to suspend a process that is executing on the CPU and resume execution of a previously suspended process called process switching, task switching, context switching;
- Suspending a process that is executing on the CPU is different from saving the scene at the time of the outage, before and after the interrupt is in the same process context, but only by the user-state to the kernel state execution;
The process context contains all the information required by the process execution
User address space: Includes program code, data, user stack, etc.
Control information: Process descriptor, kernel stack, etc.
Hardware context (note that interrupts are also saved by the hardware context only if the method is saved differently)
The schedule () function selects a new process to run and invokes Context_switch for context switching, a macro called SWITCH_TO for critical context switching
Switch_to takes advantage of the prev and next two parameters: Prev points to the current process, and next points to the scheduled process
4. Key code Analysis (1) Schedule
asmlinkage __visible void __sched schedule(void) { struct task_struct *tsk = current;//来获取当前进程 sched_submit_work(tsk);//避免死锁 __schedule();//处理切换过程 }
static void __sched __schedule(void){...next = pick_next_task(rq, prev);if (likely(prev != next)) { ... context_switch(rq, prev, next); /* unlocks the rq */ ...} else { ... raw_spin_unlock_irq(&rq->lock); ...}...post_schedule(rq);...}
? We take out the next process of the Prev process in the run queue RQ according to the scheduling policy, if the next process and prev are not the same process, then switch the process and release the spin lock, or release the spin lock directly. (2) switch_to
ASM volatile ("pushfl\n\t"//Save the current process's flag bit "PUSHL%%ebp\n\t"////current process's base address stack save Ebp "Movl%%esp, %[prev_sp]\n\t "//save ESP to save the current kernel stack stack at the top" MOVL%[next_sp],%%esp\n\t "//Recover ESP The overall two steps are to complete the switchover of the kernel stack "MOVL $1f,%[prev_ip]\n\t"//Save Eip "PUSHL%[next_ip]\n\t"//restore EIP __ Switch_canary "JMP __switch_to\n"//Jump L "1:\t" "popl%%ebp\n\t"//Restore EB P "popfl\n"//restore flag bit/* OUTPUT parameters */: [prev_sp] "=m" (prev->thread.sp ), [prev_ip] "=m" (Prev->thread.ip), "=a" (last),/* Clobbered output registers: */ "=b" (EBX), "=c" (ecx), "=d" (edx), "=s" (ESI), "=d" (EDI) __switch_canary_oparam/ * Input Parameters: */: [next_sp] "M" (NEXT->THREAD.SP), [next_ip] "M" (Next->thread.ip), /* Regparm ParameteRS for __switch_to (): */[prev] "a" (prev), [next] "D" (next) __switch_canary_ip Aram:/* Reloaded Segment Registers */"memory");
First look at the current process (prev): First save the current process of Flags,push EBP, and then set the EIP to label 1, wait until the current process (prev) to start the next execution (by __ switch_to cut out), the kernel stack is restored, it happens to be from the pop EBP begins execution (as opposed to the previous push EBP), which restores the original stack state. Look at the next process (next): This process is going on the CPU, is the process that is switched out by JMP __ Switch_ to, because it uses the JMP instruction instead of the call command, The EIP was previously pressed manually, so __ Switch_ to go back to the Next_ IP where it began to execute, thus completing the process of switching. If there are only two processes, then the next prev will become Next,next into prev.
Textbook notes
One, process space distribution? For a process, the spatial distribution is as follows:????? Program Segment (TEXT): program code in memory mapping, storing the binary code of the function body. Initialized data: Data that was initialized at the beginning of the program run. Uninitialized data (BSS): Data that was not initialized at the beginning of the program's Run. Stack ( Stack): Stores a local, temporary variable, function call, and a return pointer to a stored function that controls the invocation and return of a function. Automatically allocates memory at the beginning of the block and automatically frees memory at the end, as if it were a stack in a data structure. Heap: Stores dynamic memory allocations, which need to be manually allocated by the programmer and released manually. Note that it is different from the heap in the data structure, and is distributed in a similar way to a linked list. Second, kernel space and user space? Linux has a virtual address space range of 0~4g,linux cores that divides this 4G space into two parts, with the highest 1G bytes (from virtual addresses 0xc0000000 to 0xFFFFFFFF) for the kernel to use, called "kernel space." Instead, the lower 3G bytes (from the virtual address 0x00000000 to 0xBFFFFFFF) are used by each process, called "User space." Because each process can enter the kernel through system calls, the Linux kernel is shared by all processes within the system. Thus, from a specific process perspective, each process can have a virtual space of 4G bytes. Linux uses a level two protection mechanism: level 0 for the kernel, level 3 for the user program, each process has its own private user space (0~3G), which is invisible to other processes in the system, and the highest 1GB bytes virtual kernel space is shared by all processes and cores. Kernel space is the kernel code and data, while the process of user space is stored in the user program code and data. Both the kernel space and the user space are in virtual space. Although the kernel space occupies up to 1GB bytes in each virtual space, but the mapping to physical memory is always from the lowest address (0x00000000), in addition, the use of virtual address can be good to protect the kernel space is destroyed by the user space, virtual address to the physical address conversion process has the operating system and the CPU together ( The operating system sets the page table for the CPU, and the CPU addresses the address translation via the MMU unit. Note: Each process in a multitasking operating system runs in its own memory sandbox, which is the virtual address space, which is always a 4GB memory address block in 32-bit mode. These virtual addresses are mapped to physical memory through a page table, and the page table is maintained by the operating system and referenced by the processor. Each process has a set of its own page tables. Third, process memory layout? The Linux process standardThe memory segment layout, as shown, corresponds to different memory segments in the address space, such as heap, stack, and so on.
Stack? The topmost segment of the process address space is the stack, which is used by most programming languages to store function parameters and local variables. Calling a method or function pushes a new stack frame into the stack, and the stack frame is cleared when the function returns. Because the data in the stack strictly adheres to the FIFO order, this simple design means that you do not have to use complex data structures to track the contents of the stack, only a simple pointer to the top of the stack, so the stack (pushing) and the stack (popping) process is very fast and accurate. Each thread in the process has its own stack.? by constantly pressing data into the stack, exceeding its capacity will drain the memory area of the stack, triggering a page failure (page fault), which is processed by the Linux Expand_ stack (), which calls Acct_ stack _ Growth () to check if there is a suitable place for the stack to grow. If the size of the stack is lower than rlimit_stack (usually 8MB), then the stack will normally be extended, the program continues to execute, and you won't feel anything happening. This is a general mechanism for extending the stack to the desired size. However, if the maximum stack space is reached, the stack overflows (stack overflow) and the program receives a segment error (segmentation fault). Memory mapped segment? Below the stack is the memory-mapped segment, which maps the contents of the file directly to memory. Any application can request this mapping through the Linux mmap () system call or Windows CreateFileMapping ()/mapviewoffile (). Memory mapping is a convenient and efficient way to file I/O, so it is used to load dynamic libraries. It is also possible to create an anonymous memory map that does not correspond to any file, and this method is used to hold the program's data. In Linux, if you request a chunk of memory through malloc (), the C runtime will create such an anonymous mapping instead of using heap memory. "Chunk" means larger than Mmap_threshold, default 128KB, can be adjusted by MALLOCP (). BBS and Data section? In the C language, BSS and data segments hold the contents of static (global) variables. The difference is that BSS holds uninitialized static variable content, and their values are not set directly in the program's source code. The BSS memory area is anonymous and it does not map to any files. If you write static intcntactiveusers, the contents of the cntactiveusers will be saved in the BSS. The data segment holds the contents of a static variable that has been initialized in the source code. The data segment is not anonymous, it maps a part of the program binary image, which is the static variable that specifies the initial value in the source code.Amount So, if you write static int cntactiveusers=10, the contents of Cntactiveusers are saved in the data segment, and the initial value is 10. Although a data segment maps a file, it is a private memory map, which means that changing the memory here does not affect the mapped file. Comparison chart:
2017-2018-1 20179215 "Linux kernel Fundamentals and Analysis" Nineth Week assignment