我对系统调用和内核模块有几个问题。
假设我们有两个应用程序(A 和 B),并且每个应用程序都在不同的内核上运行。 (A-CPU 0,B-CPU 1)当两个应用程序并行调用同一个系统调用时,它们是否并行执行?如果是这样,系统调用执行的CPU核心是什么?系统调用是否在调用者的 CPU 内核上运行?
假设我们有一个内核模块,并且两个应用程序(A 和 B)通过 ioctl(并行)调用内核模块的同一函数。他们并行执行吗?如果是,那么服务内核模块功能的CPU核心是什么?如果不是,那么为多个应用程序提供并行内核模块功能的最有效方法是什么?
答案1
当 SMP 支持首次添加到 Linux 时,它使用了“巨锁”或者“BKL”(大内核锁),这仍然存在直到几年前。这有效地使内核成为单线程(除了我相信硬件中断服务),因此只能有一个系统调用处于活动状态,这当然限制了许多类型工作负载的性能。
随着时间的推移,BKL 被细粒度锁定所取代,一些系统调用可以完全并发运行,而另一些则不能完全并发。举一个简单的例子考虑PID分配和进程创建在Linux内核中。 PID 是通过位图,PID号的分配和释放可以通过无锁原子操作来完成。但是,进程表的维护并不是那么简单,这是通过“任务列表”锁来完成的,以确保相关数据结构的完整性(pid_hash
)。
接下来回答问题:
当两个应用程序并行调用同一个系统调用时,它们是并行执行的吗?
假设是当代内核,是的。但是,根据系统调用,它们可能会旋转、让出或以其他方式推迟某些操作。当使用互斥体或锁时,并发调用将无法更新相同的数据结构,或同时访问相同的硬件。
如果是这样,系统调用执行的CPU核心是什么?系统调用是否在调用者的 CPU 内核上运行?
它将在调用 CPU 上启动,它可能根据系统调用和系统其他地方发生的情况重新调度到另一个 CPU,除非您设置了严格的关联性。如果你稍微思考一下获取CPU()syscall 它应该更清楚(忽略它的事实可能不是 x86 上真正的系统调用)。
假设我们有一个内核模块,并且两个应用程序(A 和 B)通过 ioctl(并行)调用内核模块的同一函数。他们并行执行吗?
是的,该模块预计将根据需要使用细粒度锁定和其他同步原语。
如果是,那么服务内核模块功能的CPU核心是什么?
与上面相同,它将在调用 CPU 上启动。
如果不是,那么为多个应用程序提供并行内核模块功能的最有效方法是什么?
根据模块(和硬件),效率取决于对正确锁定的仔细和最少的使用(可能避免自旋锁、减少内存复制等)以及处理器亲和性的正确使用。如果您询问系统调用在哪里不能或不能同时处理,那么很难给出一个好的答案。通过将驱动程序的某些部分委托给多线程用户空间守护进程,这也许是可能的(我在一些加密加速器中观察到了这一点,其中加速器本身一次只能执行一项操作,并且一些小操作在中央处理器)。
免费的 PDF 书Linux 设备驱动程序(第三版)对于此类工作来说非常宝贵,尤其是第 5 章:并发和竞争条件。