“内核线程仍然可能有点昂贵,因为需要系统调用才能在线程之间切换”到底是啥意思?
Posted
技术标签:
【中文标题】“内核线程仍然可能有点昂贵,因为需要系统调用才能在线程之间切换”到底是啥意思?【英文标题】:What does "kernel threads can still be somewhat expensive because system calls are required to switch between threads" really mean?“内核线程仍然可能有点昂贵,因为需要系统调用才能在线程之间切换”到底是什么意思? 【发布时间】:2021-05-06 06:20:33 【问题描述】:我阅读了一份描述操作系统线程的文档,它说如下:
上下文切换 属于同一进程的内核线程之间只需要寄存器、程序计数器和堆栈 被改变;不需要切换整个内存管理信息,因为这两个 线程共享相同的地址空间。因此,两个内核线程之间的上下文切换稍微快一些 而不是在两个进程之间切换。然而,内核线程仍然可能有点昂贵,因为 在线程之间切换需要系统调用。
我不明白最后一句话的意思。它提到了系统调用上发生的线程之间的什么样的切换?据我所知,应用程序中的每个系统调用都需要上下文切换,因为它需要切换到内核模式,所以没有什么特别的,但是,整个文档的上下文中的句子似乎有一些我不知道的东西关于那个。
这句话的真正含义是什么?
整个文档:http://lass.cs.umass.edu/~shenoy/courses/fall12/lectures/notes/Lec06_notes.pdf
【问题讨论】:
是的。比什么贵?那些从未开发过多线程应用程序并且无法理解为什么要使用多线程设计的人会抛弃这样的陈述作为填充物。一方面,我不会将硬件中断以及随后的驱动程序和调度程序运行描述为“系统调用”:( 您可以正确地看到各种“线程简介”文档和网站的“问题”。有些只是误导和缺乏范围,有些只是错误的:) 【参考方案1】:我认为作者使用系统调用作为一种抽象的方式来声明系统开销;虽然系统调用开销远非微不足道。
用户线程
在用户程序中,您可以通过分配堆栈(以某种方式)以及将用户寄存器保存和恢复到数据结构中来使 线程 出现。 基于用户的线程转移可以很简单:
void ThreadSwitch(Thread *from, Thread *to)
if (setjmp(from->regs) == 0)
longjmp(to->regs, 1);
其中 setjmp 只是将一些 cpu 寄存器存储到一个数组中,而 longjmp 从一个数组中加载相同的寄存器。这隐藏了大量的复杂性,比如我是如何拥有单独的堆栈并将它们集成到语言运行时的。关键是,这是一个非常快的操作。
硬件
当您调用系统调用(陷阱)时,处理器会在进入内核之前更改执行模式。很容易将陷阱视为 CPU 技术参考手册中概述的少数操作。但除了最基本的处理器之外,它还远远不止这些。当 CPU 运行时,它会为当前执行建立一个内部上下文,其中可能包括寄存器、内存和一种用于分支预测的稀疏关联数组的推测值。
当 CPU 切换模式时,如发生在陷阱中,其中一些必须丢弃[lost opportunity cost]
,而一些必须提交[synchronization cost]
。
当所有这些上下文都被构建时,一个 ARM A73 可以在每个周期维持大约 2 条指令。如果缺少上下文,它可能会下降 16 倍以上。
此外,陷阱指令是一种在内存排序方面的屏障形式;在陷阱的第一条指令执行之前,所有挂起的内存写入必须变得可见,这通常意味着转换到 L1 缓存。传统 CPU 上的存储缓冲区从几个条目到数百个不等;所以一个陷阱可以有 100 秒的 L1 写入延迟(可能有 Ln 驱逐)。
内核
ThreadSwitchSysCall
的内核条目不太可能像上面那样;尽管第一位在逻辑上类似于 setjmp()。每个 线程 最有可能位于一个列表中,指示其处置 就绪,等待,... 并具有相关的调度参数和亲缘关系。关联性是指线程更喜欢或限制在哪个 CPU 上运行。因此,可能需要将目标线程从列表中移除(确保该列表不受其他 CPU 影响)插入到另一个列表中(再次,一致地),可能会调用一种机制来提醒另一个 CPU 有工作,然后确定哪个 Thread 应该在其上运行。最后使用选择的 Thread,它将执行 longjmp() 的等效项以在其上下文中恢复操作。
这几乎是您在 RTOS 或微内核中所期望的最低要求。 Linux/Windows/MacOS 等重量级系统将在此类交换机上执行更多内务管理。
TL:DR
所以,当教科书说因为系统调用而昂贵时,它只是为您节省了上述所有细节。这也没有错。如果您查看 golang 等现代系统语言中的并发编程模型,它会将 内核线程 视为 virtual cpus,并将自己的线程 (goroutines)
视为 user线程复用到这些虚拟CPU上。这不是偶然的,他们没有读错教科书。
【讨论】:
以上是关于“内核线程仍然可能有点昂贵,因为需要系统调用才能在线程之间切换”到底是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章