简体   繁体   English

谁在Linux内核中进行上下文切换后调用IRET?

[英]Who calls IRET after context switch in Linux Kernel?

I have been trying to understand how context switching works in Linux Kernel. 我一直试图理解上下文切换在Linux内核中是如何工作的。 It appears to me that there is a situation (explained later) which results in no invocation of IRET instruction after the interrupt (I am sure that there is something that I am missing!). 在我看来,有一种情况(后面会解释)导致在中断后没有调用IRET指令(我确信有些东西我错过了!)。 I am assuming that invocation of IRET after the interrupt is extremely necessary, since you can't get the same interrupt until you invoke IRET. 我假设在中断之后调用IRET是非常必要的,因为在调用IRET之前你不能得到相同的中断。 I am only worried about uni-processor kernel running on x86 arch. 我只担心在x86 arch上运行的单处理器内核。

The situation that I think might result in the described behavior is as follows: 我认为可能导致所述行为的情况如下:

  • Process A running in kernel mode calls schedule() voluntarily (for example while trying to acquire an already locked mutex). 进程在内核模式下运行会自动调用schedule() (例如,在尝试获取已锁定的互斥锁时)。

  • schedule() decides to perform a context switch to process B and hence calls context_switch() schedule()决定执行上下文切换到进程B,因此调用context_switch()

  • context_switch() switches virtual memory from A to B by calling switch_mm() context_switch()通过调用switch_mm()将虚拟内存从A切换到B.

  • context_switch() runs macro switch_to() to switch stacks and actually change the running process from A to B. Note that process A is now stuck inside switch_to() and the stack of process A looks like (stack growing downwards): context_switch()运行宏switch_to()来切换堆栈并实际将运行进程从A更改为B.注意,进程A现在卡在switch_to()内,进程A的堆栈看起来像(堆栈向下增长):


 ...
 [mutex_lock()]
 [schedule()]
 [context_switch()] (Stack Top)

  • Process B starts running. 进程B开始运行。 At some later time, it receives a timer interrupt and the timer interrupt handler decides that process B needs a reschedule. 稍后,它会收到定时器中断,定时器中断处理程序决定进程B需要重新安排。

  • On return from timer interrupt (but before invoking IRET) preempt_schedule_irq() is invoked. 从定时器中断返回(但在调用IRET之前),调用preempt_schedule_irq()

  • preempt_schedule_irq() calls schedule() . preempt_schedule_irq()调用schedule()

  • schedule() decides to context switch to process A and calls context_switch() . schedule()决定上下文切换到进程A并调用context_switch()

  • context_switch() calls switch_mm() to switch the virtual memory. context_switch()调用switch_mm()来切换虚拟内存。

  • context_switch() calls switch_to() to switch stacks. context_switch()调用switch_to()来切换堆栈。 At this point, stack of process B looks like following: 此时,进程B的堆栈如下所示:


...
[IRET return frame]
[ret_from_interrupt()]
[preempt_schedule_irq()]
[schedule()]
[context_switch()] (Stack top)

Now process A is running with its stack resumed. 现在进程A正在运行,其堆栈已恢复。 Since, context_switch() function in A was not invoked due to a timer interrupt, process A does not call IRET and it continues execution of mutex_lock(). 由于A中的context_switch()函数由于定时器中断而未被调用,因此进程A不会调用IRET并继续执行mutex_lock()。 This scenario may lead to blocking of timer interrupt forever. 这种情况可能会导致永久阻止定时器中断。

What am I missing here? 我在这里错过了什么?

Economical with the truth time, non-linux-specifc explanation/example: 真实时间经济,非linux特定解释/示例:

Thread A does not have to call IRET - the kernel code calls IRET to return execution to thread A, after all, that's one way it may have lost it in the first place - a hardware interrupt from some peripheral device. 线程A不必调用IRET - 内核代码调用IRET将执行返回给线程A,毕竟,这是它可能首先丢失它的一种方式 - 来自某些外围设备的硬件中断。

Typically, when thread A lost execution earlier on due to some other hardware interrupt or sycall, thread A's stack pointer is saved in the kernel TCB pointing to an IRET return frame on the stack of A before switching to the kernel stack for all the internal scheduler etc gubbins. 通常,当线程A由于某些其他硬件中断或sycall而在之前丢失执行时,线程A的堆栈指针被保存在指向A堆栈上的IRET返回帧的内核TCB中,然后切换到所有内部调度程序的内核堆栈古比斯。 If an exact IRET frame does not exist because of the particular syscall mechanism used, one is assembled. 如果由于使用了特定的系统调用机制而不存在精确的IRET帧,则会组装一个。 When the kernel needs to resume A, the kernel reloads the hardware SP with thread A's stored SP and IRET's to user space. 当内核需要恢复A时,内核将带有线程A的存储SP和IRET的硬件SP重新加载到用户空间。 Job done - A resumes running with interrupts etc, enabled. 已完成工作 - 已启用中断运行的恢复。

The kernel has then lost control. 然后内核失去了控制权。 When it's entered again by the next hardware interrupt/driver or syscall, it can set it's internal SP to the top of its own private stack since it keeps no state data on it between invocations. 当它被下一个硬件中断/驱动程序或系统调用再次输入时,它可以将其内部SP设置为其自己的私有堆栈的顶部,因为它在调用之间不保留任何状态数据。

That's just one way in which it can be made to work:) Obviously, the exact mechanism/s are ABI/architecture dependent. 这只是它可以工作的一种方式:)显然,确切的机制是依赖于ABI /架构。

I don't know about Linux, but in many operating systems, the context switch is usually performed by a dispatcher, not an interrupt handler. 我不了解Linux,但在许多操作系统中,上下文切换通常由调度程序执行,而不是中断处理程序。 If an interrupt doesn't result in a pending context switch, it just returns. 如果中断没有导致挂起的上下文切换,它就会返回。 If an interrupt triggered context switch is needed, the current state is saved and the interrupt exits via the dispatcher (the dispatcher does the IRET). 如果需要中断触发的上下文切换,则保存当前状态,并通过调度程序退出中断(调度程序执行IRET)。 This gets more complicated if nested interrupts are allowed, since the initial interrupt is the one that goes to the dispatcher, regardless of of which nested interrupt handler(s) triggered a context switch condition. 如果允许嵌套中断,则会变得更复杂,因为初始中断是进入调度程序的中断,无论哪个嵌套中断处理程序触发上下文切换条件。 An interrupt needs to check the saved state to see if it's a nested interrupt, and if not, it can disable interrupts to prevent nested interrupts occurring when it does the check for and optionally exits via the dispatcher to perform a context switch. 中断需要检查保存的状态以查看它是否是嵌套中断,如果不是,它可以禁用中断以防止在检查时发生嵌套中断,并可选择通过调度程序退出以执行上下文切换。 If the interrupt is a nested interrupt, it only has to set a context switch flag if needed, and rely on the initial interrupt to do the check and context switch. 如果中断是嵌套中断,则只需要在需要时设置上下文切换标志,并依靠初始中断进行检查和上下文切换。

Usually, there's no need for an interrupt to save a threads state in a kernel TCB unless a context switch is going to occur. 通常,除非要发生上下文切换,否则不需要中断来在内核TCB中保存线程状态。

The dispatcher also handles the cases where context switches are triggered by non-interrupt conditions, such as mutex, semaphore, ... . 调度程序还处理上下文切换由非中断条件触发的情况,例如互斥,信号量,....

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM