上下文切换中的步骤

rap*_*yen 32 multithreading process context-switch

我被要求描述两个不同进程之间的上下文切换(1)所涉及的步骤,以及(2)同一进程中两个不同线程之间的步骤.

  1. 在上下文切换期间,内核将在其PCB中保存旧进程的上下文,然后加载计划运行的新进程的已保存上下文.
  2. 操作系统可以调度同一进程中两个不同线程之间的上下文切换,使它们看起来并行执行,因此通常比两个不同进程之间的上下文切换更快.

这太普遍了,或者你会加入什么来解释这个过程更清楚?

Mar*_*mes 57

由于进程切换总是涉及线程切换,因此以相反的顺序解释它们要容易得多.

单核CPU上的典型线程上下文切换如下所示:

  1. 所有上下文切换都由"中断"启动.这可能是运行驱动程序的实际硬件中断(例如,来自网卡,键盘,内存管理或计时器硬件),或者是执行类似硬件中断的调用序列的软件调用(系统调用)进入操作系统 在驱动程序中断的情况下,OS提供了一个驱动程序可以调用而不是执行"正常"直接中断返回的入口点,因此如果需要操作系统设置线程,则允许驱动程序通过OS调度程序退出准备就绪,(例如,它已标示信号量).

  2. 非平凡系统必须启动硬件保护级别更改才能进入内核状态,以便可以访问内核代码/数据等.

  3. 必须保存中断线程的核心状态.在一个简单的嵌入式系统中,这可能只是将所有寄存器推送到线程堆栈并将堆栈指针保存在其线程控制块(TCB)中.

  4. 许多系统在此阶段切换到OS专用堆栈,因此大量的OS内部堆栈要求不会在每个线程的堆栈上造成.

  5. 可能需要标记发生中断状态更改的线程堆栈位置以允许嵌套中断.

  6. 驱动程序/系统调用运行并且可以通过从内部队列添加/移除TCB来针对不同的线程优先级来改变准备好的线程集合,例如.网卡驱动程序可能已设置事件或发信号通知另一个线程正在等待的信号量,因此该线程将被添加到就绪集,或者正在运行的线程可能已调用sleep(),因此选择将其自身从就绪集中移除.

  7. 运行OS调度程序算法以决定接下来运行哪个线程,通常是位于该优先级队列前面的最高优先级就绪线程.如果下一个运行的线程属于与先前运行的线程不同的进程,那么这里需要一些额外的东西(参见后面的内容).

  8. 检索来自该线程的TCB的保存堆栈指针并将其加载到硬件堆栈指针中.

  9. 将恢复所选线程的核心状态.在我的简单系统上,寄存器将从所选线程的堆栈中弹出.更复杂的系统必须处理返回用户级保护.

  10. 执行中断返回,因此将执行转移到所选线程.

在多核CPU的情况下,事情更复杂.调度程序可以决定当前在另一个核心上运行的线程可能需要被停止并由刚刚准备就绪的线程替换.它可以通过使用其处理器间驱动程序硬件中断运行必须停止的线程的核心来实现此目的.除了所有其他东西之外,这个操作的复杂性是避免编写OS内核的一个很好的理由:)

典型的进程上下文切换发生如下:

  1. 进程上下文切换由线程上下文切换启动,因此上述所有内容1-9都需要发生.

  2. 在上面的步骤5,调度程序决定运行属于与拥有先前运行的线程的进程不同的进程的线程.

  3. 内存管理硬件必须加载新进程的地址空间,即任何选择器/段/标志/允许新进程的线程访问其内存的任何内容.

  4. 任何FPU硬件的上下文都需要从PCB保存/恢复.

  5. 可能存在需要保存/恢复的其他过程专用硬件.

在任何真实的系统中,这些机制都是依赖于体系结构的,上面是对上下文切换的含义的粗略和不完整的指导.进程交换机生成的其他开销并不是交换机的严格组成部分 - 在进程切换后可能会有额外的缓存刷新和页面错误,因为它的某些内存可能已经被分页以支持页面归属拥有之前运行的线程的进程.

  • 可以更详细地解释单核上下文切换的第4步吗?为什么嵌套中断需要"标记"?此外,寄存器在哪里保存完全?(假设Linux) (2认同)

Zar*_*trA 14

我希望我能提供更详细/清晰的图片.

首先,OS调度线程而不是进程,因为线程是系统中唯一的可执行单元.进程切换只是一个线程切换,其中线程属于不同的进程.由于这一般,开关程序很常见.

  1. 应该调用调度程序.有三个基本的风格:

    • 不自主开关.你的线程事件的一些外部事件已经发生,影响了schedulling.例如,定时器响铃已经唤醒了一个具有高优先级的线程,HDD控制器报告已经将所请求的部分文件读入内存,并且线程等待它可以继续执行,系统定时器告诉内核你的线程有耗尽了时间量等等.
    • 自主性.线程明确请求通过系统调用重新安排.例如,请求产量,或者请求睡眠,或者请求等待直到释放互斥锁.
    • 半自愿的.线程隐式触发重新安排执行一些不相关的系统调用.例如,它要求系统读取文件.操作系统已将此请求路由到磁盘控制器,并且不会浪费时间忙于等待决定切换到另一个线程的结果.
  2. 在所有情况下,为了能够执行线程切换,应该将控制传递给内核.在非自愿开关的情况下,这种控制传递通过中断来执行,在自愿(和半自愿)的情况下,通过系统调用传递控制.

  3. 在这两种情况下,进入内核都是CPU辅助的.处理器执行权限检查,记住线程被抢占的点(以便将来能够恢复它),从线程堆栈的用户部分切换到其内核对应部分,并将控件传递给预定义且众所周知的点.内核代码.

  4. 内核执行的第一个操作是保存CPU寄存器的内容,内核将为其自己的任务重用.通常内核只使用通用CPU寄存器并通过推入堆栈来保存它们.
  5. 然后内核处理主要请求 - 处理中断,或准备文件读取请求或执行计时器设置.
  6. 在请求处理的某个时刻,内核执行的操作要么影响当前线程的状态(判断此线程中没有任何待办事项,我们应该等待某事)或影响另一个线程的状态(新线程)由于接收到中断或由于互斥锁释放等原因而变得可运行.
  7. 内核调用一个scheduller.调度程序必须做出两个决定.第一个是关于如何处理当前线程.应该被阻止吗?如果是这样,应该放入什么等待队列?如果线程被非自愿地切换,则它被置于runqueue的末尾,如果线程被阻塞,因为它等待某些东西,它被放入其中一个等待队列中.第二个决定是关于下一个运行的线程.
  8. 一旦做出两个决定,scheduller就会执行上下文swicth传递给它的两个参数 - 当前和下一个线程的线程控制块.
  9. 上下文切换本身包括三个主要步骤.在第一个,内核确定CPU寄存器线程实际使用的是什么,并将其内容保存在堆栈或outgoint线程的TCB中.如果线程不使用FPU和SSE寄存器(适用于IA-32平台),则不会保存其内容.
  10. 第二步是上下文切换的核心.内核将当前指令指针推送到堆栈,堆栈指针的值保存在传出线程的TCB中.然后它从传入线程的TCB加载到CPU新的堆栈指​​针,并从其顶部弹出指令指针.那个!新的活动堆栈意味着新的活动线程.从这一点开始,系统的其余部分将认为它在传入线程的上下文中工作.
  11. 在第三步,内核确定传入线程实际使用的寄存器,并将其先前保存的内容(请参见步骤1)加载回CPU.
  12. 然后内核检查两个线程(传入和传出)是否属于同一进程.如果它们属于不同的进程(人们调用进程切换的情况),则内核将指向MMU的当前地址空间重置为新的虚拟到物理地址转换表集.作为此过程的一部分,CPU会刷新Translate Lookaside Buffer(TLB),它将虚拟缓存到物理地址转换规则.新的虚拟地址空间意味着以前缓存的规则现在不正确.请注意,这是关注进程的整组上下文切换操作中的唯一步骤!
  13. 内核为传入线程准备线程本地存储.例如,将各个存储器页面映射到指定地址或例如在IA-32上常见的方法是加载指向传入线程的TLS数据的新段.
  14. 最后内核加载到传入线程的内核部分的CPU地址.在此之后,每个新的内核调用都将使用传入线程堆栈的内核部分,并且不会破坏存储在传出线程堆栈上的数据.
  15. 可以由内核执行的另一个步骤是重新编程系统计时器.执行此内核会要求计时器响铃并在一段时间后传递控制权到内核.该时间段称为线程的时间量.
  16. 最后内核喜欢在上下文切换期间收集统计信息,包括soch信息,因为线程消耗的CPU时间,系统中实时上下文切换的发生方式,调用线程的次数,发布CPU自愿的次数并且不自觉地,他们失去了多少次量子.部分统计数据由scheduller使用,后者试图做出更优化的决策.统计数据的另一个目的是交付给系统管理员和用户,以向他们展示系统内部的情况.
  17. 此时可以认为线程切换已完成,并且内核继续先前中断的系统操作.例如,等待文件读取的线程检查内存中的读取结果并处理它.或者在一些大型系统活动中间在互斥锁上被阻塞的线程继续该活动.
  18. 最后,稍后或更早的线程完成其系统活动并希望返回到用户模式以继续其最初使用的主要任务.此时,内核从通用寄存器的内核堆栈内容中弹出,这些内容先前在进入内核期间保存并要求CPU执行返回用户模式.
  19. CPU捕获指令指针和堆栈指针的值,这些值先前在进入内核模式期间保存并恢复它们.这样做它还会将线程从其堆栈的内核部分切换回堆栈的用户部分.最后,CPU将要执行的代码的权限重置为更有限的集合(例如,禁止使用特殊系统指令,或禁止访问kerel代码和数据).最后,CPU将控制权传递回线程最初被抢占的点.在系统调用的情况下,线程将通过捕获和处理其结果来进行调用系统调用的点.在通过中断抢占的情况下,线程将完全在中断发生的同一点继续执行.在那种情况下,它甚至完全没有意识到它被打断了.

一些摘要说明:

  1. 内核调度并仅执行线程,而不是进程.由于这种情况,线程之间发生了swicth.
  2. 属于不同过程的踏板之间的上下文切换的过程基本上与属于同一过程的线程之间的过程相同.在第一种情况下只有一个额外步骤 - 加载新的虚拟地址空间(导致TLB刷新).
  3. 线程的上下文存储在线程堆栈的内核部分或线程TCB中(不在PCB中!).
  4. 线程切换引入了性能损失.线程切换有很大的直接成本.甚至由缓存污染和TLB刷新产生的更大的直接成本(如果在切换期间重新加载虚拟地址空间).


Abh*_*war 7

  1. 在交换机中,当前正在执行的进程的状态必须以某种方式保存,以便在重新调度时,可以恢复该状态。
  2. 进程状态包括进程可能使用的所有寄存器,尤其是程序计数器,以及可能需要的任何其他操作系统特定数据。这通常存储在称为进程控制块的数据结构中(PCB) 或开关帧。
  3. PCB 可能存储在内核内存中的每个进程堆栈上(与用户模式调用堆栈相反),或者可能有一些特定的操作系统定义的数据结构用于此信息。PCB 的句柄被添加到准备运行的进程队列中,通常称为就绪队列。
  4. 由于操作系统有效地暂停了一个进程的执行,因此它可以通过从就绪队列中选择一个进程并恢复其 PCB 来切换上下文。在这样做时,来自 PCB 的程序计数器被加载,因此可以在所选进程中继续执行。进程和线程优先级会影响从就绪队列中选择哪个进程(即,它可能是一个优先级队列)。

上下文切换

(来源:上下文切换