Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/loops/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Linux kernel 上下文开关内部_Linux Kernel_Kernel_Scheduler_Context Switch - Fatal编程技术网

Linux kernel 上下文开关内部

Linux kernel 上下文开关内部,linux-kernel,kernel,scheduler,context-switch,Linux Kernel,Kernel,Scheduler,Context Switch,我想通过这个问题来学习并填补我的知识空白 因此,用户正在运行一个线程(内核级),它现在调用yield(我想这是一个系统调用)。 调度器现在必须将当前线程的上下文保存在TCB中(存储在内核的某个地方),然后选择另一个线程运行并加载其上下文,然后跳转到其CS:EIP。 为了缩小范围,我正在研究运行在x86架构之上的Linux。现在,我想谈谈细节: 首先,我们有一个系统调用: 1) yield的包装函数将把系统调用参数推送到堆栈上。按下返回地址并引发一个中断,系统调用号被推到某个寄存器上(比如EAX)

我想通过这个问题来学习并填补我的知识空白

因此,用户正在运行一个线程(内核级),它现在调用
yield
(我想这是一个系统调用)。 调度器现在必须将当前线程的上下文保存在TCB中(存储在内核的某个地方),然后选择另一个线程运行并加载其上下文,然后跳转到其
CS:EIP
。 为了缩小范围,我正在研究运行在x86架构之上的Linux。现在,我想谈谈细节:

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

1)
yield
的包装函数将把系统调用参数推送到堆栈上。按下返回地址并引发一个中断,系统调用号被推到某个寄存器上(比如
EAX

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

3) 我猜调度程序现在被调用,现在它必须在TCB中保存当前状态。这是我的困境。因为,调度器将使用内核堆栈而不是用户堆栈来执行其操作(这意味着必须更改
SS
SP
),它如何存储用户的状态而不修改进程中的任何寄存器。我在论坛上读到过关于保存状态的特殊硬件指令,但调度程序如何访问这些指令,谁运行这些指令,何时运行

4) 调度器现在将状态存储到TCB并加载另一个TCB

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


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

在第2步中,您忽略了堆栈从线程的用户级堆栈(您在其中推送args)切换到线程的受保护级别堆栈。被系统调用中断的线程的当前上下文实际上保存在这个受保护的堆栈上。在ISR内部,就在进入内核之前,这个受保护的堆栈再次切换到您所说的内核堆栈。一旦进入内核,诸如调度器函数之类的内核函数最终将使用内核堆栈。稍后,调度程序选择一个线程,系统返回ISR,它从内核堆栈切换回新选择的(或者,如果没有更高优先级的线程处于活动状态,则切换回前者)线程的受保护级别堆栈,该堆栈最终包含新的线程上下文。因此,通过代码自动从该堆栈恢复上下文(取决于底层体系结构)。最后,一条特殊指令将恢复最新的易受影响的寄存器,如堆栈指针和指令指针。回到美国

总之,一个线程(通常)有两个堆栈,内核本身有一个。内核堆栈在每个内核输入结束时被擦除。有趣的是,从2.6开始,内核本身在某些处理中会被线程化,因此内核线程在普通内核堆栈之外有自己的受保护级别堆栈

一些资源:

  • 3.3.3执行进程切换,了解Linux内核,O'Reilly
  • 5.12.1英特尔手册3A(系统编程)中的异常或中断处理程序。章节号可能因版本不同而有所不同,因此查找“中断和异常处理例程的传输上的堆栈使用情况”应该可以找到好的章节号

希望这有帮助

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

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

  • 线程A进入内核,从用户模式切换到内核模式
  • 内核上下文中的线程A切换到内核中的线程B
  • 线程B退出内核,从内核模式切换回用户模式
  • 每个用户线程都有一个用户模式堆栈和一个内核模式堆栈。当线程进入内核时,用户模式堆栈(
    SS:ESP
    )和指令指针(
    CS:EIP
    )的当前值保存到线程的内核模式堆栈中,CPU切换到内核模式堆栈-通过
    int$80
    系统调用机制,这是由CPU自己完成的。剩余的寄存器值和标志随后也保存到内核堆栈中

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

    当线程上下文切换时,它会调用调度程序(调度程序不会作为单独的线程运行-它总是在当前线程的上下文中运行)。调度器代码选择下一个要运行的进程,并调用
    switch\u to()
    函数。此函数实际上只是切换内核堆栈-它将堆栈指针的当前值保存到当前线程的TCB中(在Linux中称为
    struct task_struct
    ),并从TCB为下一个线程加载以前保存的堆栈指针。此时,它还保存和恢复一些内核通常不使用的其他线程状态,如浮点/SSE寄存器。如果正在切换的线程不共享相同的虚拟m
    pushad; // save context of outgoing thread on the top of the kernel stack of outgoing thread
    ; here kernel uses kernel stack of outgoing thread
    mov [TCB_of_outgoing_thread], ESP;
    mov  ESP , [TCB_of_incoming_thread]    
    ; here kernel uses kernel stack of incoming thread
    popad; // save context of incoming thread from the top of the kernel stack of incoming thread