上下文切换内部 [英] Context switch internals

查看:98
本文介绍了上下文切换内部的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想借助这个问题来学习并弥补我的知识空白.

I want to learn and fill gaps in my knowledge with the help of this question.

因此,用户正在运行一个线程(内核级),并且它现在调用yield(我认为是系统调用). 调度程序现在必须将当前线程的上下文保存在TCB中(该线程存储在内核中的某个位置),并选择另一个线程来运行并加载其上下文并跳转至其CS:EIP. 为了缩小范围,我正在研究在x86体系结构上运行的Linux.现在,我想详细介绍一下:

So, a user is running a thread (kernel-level) and it now calls yield (a system call I presume). The scheduler must now save the context of the current thread in the TCB (which is stored in the kernel somewhere) and choose another thread to run and loads its context and jump to its CS:EIP. To narrow things down, I am working on Linux running on top of x86 architecture. Now, I want to get into the details:

因此,首先我们有一个系统调用:

So, first we have a system call:

1)yield的包装函数会将系统调用参数推入堆栈.推送返回地址,并通过将系统调用号推送到某个寄存器(例如EAX)来引发中断.

1) The wrapper function for yield will push the system call arguments onto the stack. Push the return address and raise an interrupt with the system call number pushed onto some register (say EAX).

2)中断将CPU模式从用户更改为内核,并跳转到中断向量表,并从那里跳转到内核中的实际系统调用.

2) The interrupt changes the CPU mode from user to kernel and jumps to the interrupt vector table and from there to the actual system call in the kernel.

3)我想现在调用调度程序,现在它必须将当前状态保存在TCB中.这是我的困境.由于调度程序将使用内核堆栈而不是用户堆栈来执行其操作(这意味着必须更改SSSP)它如何存储用户状态而无需在进程中修改任何寄存器.我在论坛上读过一些关于保存状态的特殊硬件说明,但是调度程序如何访问它们,谁来运行这些说明以及何时运行?

3) I guess the scheduler gets called now and now it must save the current state in the TCB. Here is my dilemma. Since, the scheduler will use the kernel stack and not the user stack for performing its operation (which means the SS and SP have to be changed) how does it store the state of the user without modifying any registers in the process. I have read on forums that there are special hardware instructions for saving state but then how does the scheduler get access to them and who runs these instructions and when?

4)现在,调度程序将状态存储到TCB中并加载另一个TCB.

4) The scheduler now stores the state into the TCB and loads another TCB.

5)当调度程序运行原始线程时,控件返回到包装器函数,该函数清除堆栈并恢复线程.

5) When the scheduler runs the original thread, the control gets back to the wrapper function which clears the stack and the thread resumes.

侧面问题:调度程序是否作为仅内核线程(即只能运行内核代码的线程)运行?每个内核线程或每个进程是否有单独的内核堆栈?

Side questions: Does the scheduler run as a kernel-only thread (i.e. a thread which can run only kernel code)? Is there a separate kernel stack for each kernel-thread or each process?

推荐答案

从总体上讲,有两种不同的理解机制.第一个是内核进入/退出机制:它将单个正在运行的线程从正在运行的用户模式代码切换到该线程上下文中的正在运行的内核代码,然后再次返回.第二个是上下文切换机制本身,它以内核模式从在一个线程的上下文中运行切换到另一个线程.

At a high level, there are two separate mechanisms to understand. The first is the kernel entry/exit mechanism: this switches a single running thread from running usermode code to running kernel code in the context of that thread, and back again. The second is the context switch mechanism itself, which switches in kernel mode from running in the context of one thread to another.

因此,当线程A调用sched_yield()并由线程B替换时,会发生以下情况:

So, when Thread A calls sched_yield() and is replaced by Thread B, what happens is:

  1. 线程A进入内核,从用户模式更改为内核模式;
  2. 内核上下文中的线程A-切换到内核中的线程B;
  3. 线程B退出内核,从内核模式变回用户模式.

每个用户线程都有一个用户模式堆栈和一个内核模式堆栈.当线程进入内核时,用户模式堆栈(SS:ESP)和指令指针(CS:EIP)的当前值将保存到线程的内核模式堆栈中,并且CPU切换到内核模式堆栈-通过int $80 syscall机制,这是由CPU本身完成的.其余的寄存器值和标志也将保存到内核堆栈中.

Each user thread has both a user-mode stack and a kernel-mode stack. When a thread enters the kernel, the current value of the user-mode stack (SS:ESP) and instruction pointer (CS:EIP) are saved to the thread's kernel-mode stack, and the CPU switches to the kernel-mode stack - with the int $80 syscall mechanism, this is done by the CPU itself. The remaining register values and flags are then also saved to the kernel stack.

当线程从内核返回到用户模式时,寄存器值和标志从内核模式堆栈中弹出,然后用户模式堆栈和指令指针值从内核模式中保存的值中恢复堆栈.

When a thread returns from the kernel to user-mode, the register values and flags are popped from the kernel-mode stack, then the user-mode stack and instruction pointer values are restored from the saved values on the kernel-mode stack.

当线程上下文切换时,它将调用调度程序(调度程序不会作为单独的线程运行-始终在当前线程的上下文中运行).调度程序代码选择一个下一步要运行的进程,然后调用switch_to()函数.此函数实质上只是切换内核堆栈-将堆栈指针的当前值保存到当前线程(在Linux中称为struct task_struct)的TCB中,并从TCB中为下一个线程加载先前保存的堆栈指针.此时,它还保存和恢复内核通常不使用的其他线程状态-诸如浮点数/SSE寄存器之类的东西.如果要切换的线程不共享相同的虚拟内存空间(即它们处于不同的进程中),那么页表也将被切换.

When a thread context-switches, it calls into the scheduler (the scheduler does not run as a separate thread - it always runs in the context of the current thread). The scheduler code selects a process to run next, and calls the switch_to() function. This function essentially just switches the kernel stacks - it saves the current value of the stack pointer into the TCB for the current thread (called struct task_struct in Linux), and loads a previously-saved stack pointer from the TCB for the next thread. At this point it also saves and restores some other thread state that isn't usually used by the kernel - things like floating point/SSE registers. If the threads being switched don't share the same virtual memory space (ie. they're in different processes), the page tables are also switched.

因此,您可以看到在上下文切换时线程的核心用户模式状态没有保存和恢复-在您进入和离开内核时,它已经保存并恢复到线程的内核堆栈中.上下文切换代码不必担心破坏用户模式寄存器的值-到那时,这些值已经安全地保存在内核堆栈中.

So you can see that the core user-mode state of a thread isn't saved and restored at context-switch time - it's saved and restored to the thread's kernel stack when you enter and leave the kernel. The context-switch code doesn't have to worry about clobbering the user-mode register values - those are already safely saved away in the kernel stack by that point.

这篇关于上下文切换内部的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆