I have a very basic doubt.
当进程正在等待信号量时,它进入睡眠状态。 因此,它无法轮询信号量值。
内核是否轮询信号量值,如果可用,则向所有等待它的进程发送信号?如果是这样,内核的开销不会太大。
或者signal()调用在内部通知所有等待信号量的进程。 请告诉我。
答案 0 :(得分:3)
当另一个进程告知操作系统已经使用信号量时,操作系统再次调度该进程。
信号量只是与OS调度程序交互的方式之一。
内核不会轮询信号量;它并不需要。每次进程调用sem_post()
(或等效的)时,都涉及与内核的交互。内核在sem_post()
期间执行的操作是查找以前在同一个信号量上调用sem_wait()
的进程。如果一个或多个进程调用sem_wait()
,它将选择具有最高优先级的进程并对其进行调度。这显示为sem_wait()
最终返回并且该过程继续执行。
如何在引擎盖下实施
从根本上说,内核需要实现一个名为"原子测试和设置"的东西。这是一种操作,其中可以测试某个变量的值,并且如果满足某个条件(例如值== 0),则改变变量值(例如,值= 1)。如果成功,内核将做一件事(比如安排一个进程),如果没有(因为条件值== 0为假),内核会做一些不同的事情(比如把一个进程放在do-not-上)时间表)。原子'部分是这个决定是在没有其他任何东西能够同时查看和更改同一个变量的情况下做出的。
有几种方法可以做到这一点。一种是暂停所有进程(或至少内核中的所有活动),以便没有其他任何东西同时测试变量的值。那不是很快。
例如,Linux内核曾经有一个叫做Big Kernel Lock的东西。我不知道这是否用于处理信号量交互,但这是操作系统过去用于原子测试的那种东西。集。
这些天CPU都有原子测试和设置操作码,速度要快得多。好的'摩托罗拉68000很久以前就有这样的一个;像PowerPC和x86这样的CPU需要很多年才能得到同样的指令。
如果你在Linux内部扎根,你会发现futexes
的提及。 futex是一种快速互斥体 - 它依赖于CPU的测试/设置指令来实现快速mutex
信号量。
在硬件中发布信号量
变体是邮箱信号量。这是信号量的一种特殊变体,在某些系统类型中非常有用,其中硬件需要在DMA传输结束时唤醒进程。邮箱是内存中的一个特殊位置,写入时会导致中断。这可以由内核转换为信号量,因为当引发该中断时,它会经历与称为sem_post()
的动作相同的动作。
这非常方便;设备可以DMA
向预先安排好的缓冲区提供大量数据,并通过向邮箱发送小DMA
转发。内核处理中断,如果进程先前已在邮箱信号量上调用sem_wait()
,则内核会对其进行调度。该过程也知道这个预先安排的缓冲区,然后可以处理数据。
在实时DSP系统上,这非常有用,因为它非常快且非常低的延迟;它允许进程以非常小的延迟从某个设备接收数据。另一种方法是,使用read()
/ write()
将设备中的数据从设备传输到进程的完整设备驱动程序堆栈相比之下速度非常慢。
<强>速度强>
信号量交互的速度完全取决于操作系统。
对于像Windows和Linux这样的操作系统,上下文切换时间相当慢(大约几微秒,如果不是几十微秒)。基本上这意味着当一个进程调用sem_post()
之类的东西时,内核在最终将控制权返回给进程之前有机会做很多不同的事情。在这段时间里做的事情几乎可以是任何事情!
如果一个程序使用了很多线程,而且他们之间都在使用信号量快速交互,那么sem_post()
和sem_wait()
就会浪费很多时间。在调用下一个sem_wait()
之前,如果某个流程从sem_post()
返回,则会强调执行大量工作。
然而,在像VxWorks这样的操作系统上,上下文切换时间非常快。那就是在调用sem_post()时内核中运行的代码非常少。结果是信号量交互效率更高。此外,像VxWorks这样的操作系统是以这样的方式编写的,以确保完成所有sem_post()
/ sem_wait()
工作的时间是常量。
这会影响这些系统上某个软件的架构。在VxWorks上,上下文切换很便宜,让大量线程都执行非常小的任务会受到很小的惩罚。在Windows / Linux上,更多的是强调相反的情况。
这就是为什么像VxWorks这样的操作系统非常适合硬实时应用程序,而Windows / Linux则不是。
Linux PREEMPT_RT补丁集的部分目的是在这样的操作中改善linux内核的延迟。例如,它将许多设备中断处理程序(设备驱动程序)推送到内核线程中;这些计划几乎就像任何其他线程一样。我们的想法是减少内核正在完成的工作量(并且内核线程完成了更多工作),这样它仍然必须自己完成工作(例如处理sem_post()
/ {{1} })花费的时间更少,并且更长久地确定这需要多长时间。它仍然不是延迟的硬保证,但它是一个相当不错的改进。这就是我们所说的软实时内核。但影响是机器的整体吞吐量可能会降低。
<强>信号强>
信号是令人讨厌的,可怕的事情,它们确实妨碍了使用sem_post()和sem_wait()之类的东西。我像瘟疫一样避开它们。
如果您使用的是Linux平台而且必须使用信号,请仔细查看sem_wait()
(man page)。这是处理信号的一种更好的方法,因为您可以选择在方便的时间(简称为signalfd
)接受它们,而不必在它们发生时立即处理它们。当然,如果您在某个程序中的任何地方使用read()
或epoll()
,那么select()
就可以了。