Linux 自旋锁与信号量
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/195853/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spinlock versus Semaphore
提问by iankits
What are the basic differences between a semaphore & spin-lock?
信号量和自旋锁之间的基本区别是什么?
When would we use a semaphore over a spin-lock?
我们什么时候在自旋锁上使用信号量?
采纳答案by Damon
Spinlock and semaphore differ mainly in four things:
自旋锁和信号量的区别主要有四点:
1. What they are
A spinlockis one possible implementation of a lock, namely one that is implemented by busy waiting ("spinning"). A semaphore is a generalization of a lock (or, the other way around, a lock is a special case of a semaphore). Usually, but not necessarily, spinlocks are only valid within one process whereas semaphores can be used to synchronize between different processes, too.
1.他们是什么
一个自旋锁是一个可能实现的锁,即一个由忙等待(“旋转”)来实现。信号量是锁的泛化(或者,反过来说,锁是信号量的特例)。通常,但不一定,自旋锁仅在一个进程内有效,而信号量也可用于在不同进程之间进行同步。
A lock works for mutual exclusion, that is onethread at a time can acquire the lock and proceed with a "critical section" of code. Usually, this means code that modifies some data shared by several threads.
A semaphorehas a counter and will allow itself being acquired by one or severalthreads, depending on what value you post to it, and (in some implementations) depending on what its maximum allowable value is.
锁用于互斥,即一次一个线程可以获取锁并继续执行代码的“临界区”。通常,这意味着修改由多个线程共享的某些数据的代码。
一个信号有一个计数器,并允许自己被收购一个或多个线程,这取决于你的价值张贴到它的东西,(在一些实现),这取决于它的最大允许值是什么。
Insofar, one can consider a lock a special case of a semaphore with a maximum value of 1.
就此而言,可以将锁视为最大值为 1 的信号量的特殊情况。
2. What they do
As stated above, a spinlock is a lock, and therefore a mutual exclusion (strictly 1 to 1) mechanism. It works by repeatedly querying and/or modifying a memory location, usually in an atomic manner. This means that acquiring a spinlock is a "busy" operation that possibly burns CPU cycles for a long time (maybe forever!) while it effectively achieves "nothing".
The main incentive for such an approach is the fact that a context switch has an overhead equivalent to spinning a few hundred (or maybe thousand) times, so if a lock can be acquired by burning a few cycles spinning, this may overall very well be more efficient. Also, for realtime applications it may not be acceptable to block and wait for the scheduler to come back to them at some far away time in the future.
2. 它们的作用
如上所述,自旋锁是一种锁,因此是一种互斥(严格来说是 1 对 1)机制。它通过重复查询和/或修改内存位置来工作,通常以原子方式。这意味着获取自旋锁是一个“忙碌”的操作,它可能会长时间(可能永远!)消耗 CPU 周期,而它实际上“什么也没有”。
这种方法的主要动机是上下文切换的开销相当于旋转数百(或数千)次,因此如果可以通过燃烧几个循环旋转来获取锁,这总体上很可能是更高效。此外,对于实时应用程序,阻塞并等待调度程序在将来某个很远的时间返回它们可能是不可接受的。
A semaphore, by contrast, either does not spin at all, or only spins for a very short time (as an optimization to avoid the syscall overhead). If a semaphore cannot be acquired, it blocks, giving up CPU time to a different thread that is ready to run. This may of course mean that a few milliseconds pass before your thread is scheduled again, but if this is no problem (usually it isn't) then it can be a very efficient, CPU-conservative approach.
相比之下,信号量要么根本不旋转,要么只旋转很短的时间(作为避免系统调用开销的优化)。如果无法获取信号量,它将阻塞,将 CPU 时间分配给准备运行的不同线程。这当然可能意味着在您的线程再次被调度之前经过几毫秒,但如果这没有问题(通常不是),那么它可能是一种非常有效的、CPU 保守的方法。
3. How they behave in presence of congestion
It is a common misconception that spinlocks or lock-free algorithms are "generally faster", or that they are only useful for "very short tasks" (ideally, no synchronization object should be held for longer than absolutely necessary, ever).
The one important difference is how the different approaches behave in presence of congestion.
3.它们在拥塞情况下的表现
一个常见的误解是自旋锁或无锁算法“通常更快”,或者它们只对“非常短的任务”有用(理想情况下,没有同步对象应该保持更长时间)比绝对必要的,永远)。
一个重要的区别是不同方法在拥塞情况下的表现。
A well-designed system normally has low or no congestion (this means not all threads try to acquire the lock at the exact same time). For example, one would normally notwrite code that acquires a lock, then loads half a megabyte of zip-compressed data from the network, decodes and parses the data, and finally modifies a shared reference (append data to a container, etc.) before releasing the lock. Instead, one would acquire the lock only for the purpose of accessing the shared resource.
Since this means that there is considerably more work outside the critical section than inside it, naturally the likelihood for a thread being inside the critical section is relatively low, and thus few threads are contending for the lock at the same time. Of course every now and then two threads will try to acquire the lock at the same time (if this couldn'thappen you wouldn't need a lock!), but this is rather the exception than the rule in a "healthy" system.
设计良好的系统通常拥塞很少或没有拥塞(这意味着并非所有线程都试图在完全相同的时间获取锁)。例如,通常不会编写获取锁的代码,然后从网络加载半兆字节的 zip 压缩数据,解码和解析数据,最后修改共享引用(将数据附加到容器等)。在释放锁之前。相反,人们只会为了访问共享资源而获取锁。
由于这意味着临界区外的工作比临界区内的工作要多得多,因此线程在临界区内的可能性自然相对较低,因此很少有线程同时争用锁。当然,两个线程会时不时地尝试同时获取锁(如果这不会发生,您就不需要锁!),但这与其说是“健康”系统中的规则,不如说是例外.
In such a case, a spinlock greatlyoutperforms a semaphore because if there is no lock congestion, the overhead of acquiring the spinlock is a mere dozen cycles as compared to hundreds/thousands of cycles for a context switch or 10-20 million cycles for losing the remainder of a time slice.
在这种情况下,自旋锁的性能大大优于信号量,因为如果没有锁拥塞,获取自旋锁的开销仅为几十个周期,而上下文切换需要数百/数千个周期,丢失需要 10-2000 万个周期时间片的剩余部分。
On the other hand, given high congestion, or if the lock is being held for lengthy periods (sometimes you just can't help it!), a spinlock will burn insane amounts of CPU cycles for achieving nothing.
A semaphore (or mutex) is a much better choice in this case, as it allows a different thread to run usefultasks during that time. Or, if no other thread has something useful to do, it allows the operating system to throttle down the CPU and reduce heat / conserve energy.
另一方面,考虑到高拥塞,或者如果锁被持有很长时间(有时你就是情不自禁!),自旋锁会燃烧疯狂数量的 CPU 周期而一事无成。
在这种情况下,信号量(或互斥量)是更好的选择,因为它允许不同的线程在此期间运行有用的任务。或者,如果没有其他线程可以做一些有用的事情,它允许操作系统降低 CPU 速度并减少热量/节约能源。
Also, on a single-core system, a spinlock will be quite inefficient in presence of lock congestion, as a spinning thread will waste its complete time waiting for a state change that cannot possibly happen (not until the releasing thread is scheduled, which isn't happeningwhile the waiting thread is running!). Therefore, given anyamount of contention, acquiring the lock takes around 1 1/2 time slices in the best case (assuming the releasing thread is the next one being scheduled), which is not very good behaviour.
此外,在单核系统上,自旋锁在锁拥塞的情况下效率非常低,因为自旋线程将浪费其全部时间等待不可能发生的状态更改(直到调度释放线程,这是等待线程运行时不会发生!)。因此,考虑到任何数量的争用,在最佳情况下(假设释放线程是下一个被调度的线程),获取锁需要大约 1 1/2 个时间片,这不是很好的行为。
4. How they're implemented
A semaphore will nowadays typically wrap sys_futex
under Linux (optionally with a spinlock that exits after a few attempts).
A spinlock is typically implemented using atomic operations, and without using anything provided by the operating system. In the past, this meant using either compiler intrinsics or non-portable assembler instructions. Meanwhile both C++11 and C11 have atomic operations as part of the language, so apart from the general difficulty of writing provably correct lock-free code, it is now possible to implement lock-free code in an entirely portable and (almost) painless way.
4. 它们是如何实现
的 现在信号量通常会sys_futex
在 Linux 下包装(可选地带有在几次尝试后退出的自旋锁)。
自旋锁通常使用原子操作实现,而不使用操作系统提供的任何东西。过去,这意味着使用编译器内部函数或不可移植的汇编器指令。同时,C++11 和 C11 都有原子操作作为语言的一部分,所以除了编写可证明正确的无锁代码的一般困难之外,现在可以在完全可移植和(几乎)无痛的方式。
回答by gbjbaanb
very simply, a semaphore is a "yielding" synchronisation object, a spinlock is a 'busywait' one. (there's a little more to semaphores in that they synchronise several threads, unlike a mutex or guard or monitor or critical section that protects a code region from a single thread)
很简单,信号量是一个“产生”的同步对象,自旋锁是一个“忙等待”对象。(信号量还有一点,因为它们同步多个线程,不像互斥锁或保护或监视器或临界区保护代码区域免受单个线程的影响)
You'd use a semaphore in more circumstances, but use a spinlock where you are going to lock for a very short time - there is a cost to locking especially if you lock a lot. In such cases it can be more efficient to spinlock for a little while waiting for the protected resource to become unlocked. Obviously there is a performance hit if you spin for too long.
在更多情况下您会使用信号量,但是在您要锁定很短时间的情况下使用自旋锁 - 锁定会产生成本,特别是如果您锁定很多。在这种情况下,在等待受保护资源解锁时,稍微自旋锁定会更有效。显然,如果旋转时间过长,性能会受到影响。
typically if you spin for longer than a thread quantum, then you should use a semaphore.
通常,如果您旋转的时间比线程量子长,那么您应该使用信号量。
回答by yoav.aviram
Spinlock refers to an implementation of inter-thread locking using machine dependent assembly instructions (such as test-and-set). It is called a spinlock because the thread simply waits in a loop ("spins") repeatedly checking until the lock becomes available (busy wait). Spinlocks are used as a substitute for mutexes, which are a facility supplied by operating systems (not the CPU), because spinlocks perform better, if locked for a short period of time.
自旋锁是指使用依赖于机器的汇编指令(例如 test-and-set)实现线程间锁定。之所以称为自旋锁,是因为线程只是在循环中等待(“自旋”),反复检查直到锁可用(忙等待)。自旋锁用作互斥锁的替代品,互斥锁是操作系统(而不是 CPU)提供的一种工具,因为自旋锁如果锁定时间较短,则性能更好。
A Semaphor is a facility supplied by operating systems for IPC, therefor it's main purpose is inter-process-communication. Being a facility supplied by the operating system it's performance will not be as good as that of a spinlock for inter-thead locking (although possible). Semaphores are better for locking for longer periods of time.
信号量是操作系统为 IPC 提供的工具,因此它的主要目的是进程间通信。作为操作系统提供的工具,它的性能不如用于线程间锁定的自旋锁(尽管可能)。信号量更适合长时间锁定。
That said - implementing splinlocks in assembly is tricky, and not portable.
也就是说 - 在组装中实现 splinlocks 很棘手,而且不可移植。
回答by Jonathan Leffler
Over and above what Yoav Aviram and gbjbaanb said, the other key point used to be that you would never use a spin-lock on a single-CPU machine, whereas a semaphore would make sense on such a machine. Nowadays, you are frequently hard-pressed to find a machine without multiple cores, or hyperthreading, or equivalent, but in the circumstances that you have just a single CPU, you should use semaphores. (I trust the reason is obvious. If the single CPU is busy waiting for something else to release the spin-lock, but it is running on the only CPU, the lock is unlikely to be released until the current process or thread is preempted by the O/S, which might take a while and nothing useful happens until the preemption occurs.)
除了 Yoav Aviram 和 gbjbaanb 所说的之外,另一个关键点曾经是你永远不会在单 CPU 机器上使用自旋锁,而信号量在这样的机器上是有意义的。如今,您经常很难找到没有多核、超线程或同等功能的机器,但在您只有一个 CPU 的情况下,您应该使用信号量。(我相信原因很明显。如果单个 CPU 忙于等待其他东西释放自旋锁,但它正在唯一的 CPU 上运行,则在当前进程或线程被抢占之前不太可能释放锁O/S,这可能需要一段时间,并且在发生抢占之前不会发生任何有用的事情。)
回答by Raman Chalotra
I am not a kernel expert but here are few points:
我不是内核专家,但这里有几点:
Even uniprocessor machine can use spin-locks if kernel preemption is enabled while compiling the kernel. If kernel preemption is disabled then spin-lock (perhaps) expands to a voidstatement.
如果在编译内核时启用了内核抢占,即使是单处理器机器也可以使用自旋锁。如果禁用内核抢占,则自旋锁(可能)扩展为void语句。
Also, when we are trying to compare Semaphore vs Spin-lock, I believe semaphore refers to the one used in kernel - NOT the one used for IPC (userland).
此外,当我们尝试比较信号量与自旋锁时,我相信信号量指的是内核中使用的信号量 - 而不是用于 IPC(用户空间)的信号量。
Basically, spin-lock shall be used if critical section is small (smaller than the overhead of sleep/wake-up) and critical section does not call anything that can sleep! A semaphore shall be used if critical section is bigger and it can sleep.
基本上,如果临界区很小(小于睡眠/唤醒的开销)并且临界区不调用任何可以睡眠的东西,则应使用自旋锁!如果临界区较大并且可以休眠,则应使用信号量。
Raman Chalotra.
拉曼查洛特拉。
回答by Johan Bezem
I would like to add my observations, more general and not very Linux-specific.
我想添加我的观察,更一般的而不是非常特定于 Linux 的。
Depending on the memory architecture and the processor capabilities, you might need a spin-lock in order to implement a semaphore on a multi-core or a multiprocessor system, because in such systems a race condition might occur when two or more threads/processes want to acquire a semaphore.
根据内存体系结构和处理器功能,您可能需要一个自旋锁来在多核或多处理器系统上实现信号量,因为在这样的系统中,当两个或多个线程/进程想要获取信号量。
Yes, if your memory architecture offers the locking of a memory section by one core/processor delaying all other accesses, and if your processors offers a test-and-set, you may implement a semaphore without a spin-lock (but very carefully!).
是的,如果您的内存架构通过一个内核/处理器延迟所有其他访问来提供内存部分的锁定,并且如果您的处理器提供测试和设置,则您可以实现没有自旋锁的信号量(但要非常小心! )。
However, as simple/cheap multi-core systems are designed (I'm working in embedded systems), not all memory architectures support such multi-core/multiprocessor features, only test-and-set or equivalent. Then an implementation could be as follows:
然而,由于设计了简单/便宜的多核系统(我在嵌入式系统中工作),并非所有内存架构都支持这种多核/多处理器功能,只有测试和设置或等效功能。那么一个实现可能如下:
- acquire the spin-lock (busy waiting)
- try to acquire the semaphore
- release the spin-lock
- if the semaphore was not successfully acquired, suspend the current thread until the semaphore is released; otherwise continue with the critical section
- 获取自旋锁(忙等待)
- 尝试获取信号量
- 释放自旋锁
- 如果没有成功获取到信号量,则暂停当前线程,直到信号量被释放;否则继续临界区
Releasing the semaphore would need to be implemented as follows:
释放信号量需要按如下方式实现:
- acquire the spin-lock
- release the semaphore
- release the spin-lock
- 获取自旋锁
- 释放信号量
- 释放自旋锁
Yes, and for simple binary semaphores on an OS-level it would be possible to use only a spin-lock as replacement. But only if the code-sections to be protected are really very small.
是的,对于操作系统级别的简单二进制信号量,可以仅使用自旋锁作为替代。但前提是要保护的代码段确实非常小。
As said before, if and when you implement your own OS, make sure to be careful. Debugging such errors is fun (my opinion, not shared by many), but mostly very tedious and difficult.
如前所述,如果您实现自己的操作系统,请务必小心。调试此类错误很有趣(我的观点,很多人都没有分享过),但大多是非常乏味和困难的。
回答by Zbyszek
From Linux Device Drivers by Rubinni
来自 Rubinni 的 Linux 设备驱动程序
Unlike semaphores, spinlocks may be used in code that cannot sleep, such as interrupt handlers
与信号量不同,自旋锁可用于无法休眠的代码中,例如中断处理程序
回答by Zbyszek
A "mutex" (or "mutual exclusion lock") is a signal that two or more asynchronous processes can use to reserve a shared resource for exclusive use. The first process that obtains ownership of the "mutex" also obtains ownership of the shared resource. Other processes must wait for for the first process to release it's ownership of the "mutex" before they may attempt to obtain it.
“互斥锁”(或“互斥锁”)是两个或多个异步进程可以用来保留共享资源以供独占使用的信号。第一个获得“互斥锁”所有权的进程也获得了共享资源的所有权。其他进程必须等待第一个进程释放它对“互斥锁”的所有权,然后才能尝试获取它。
The most common locking primitive in the kernel is the spinlock. The spinlock is a very simple single-holder lock. If a process attempts to acquire a spinlock and it is unavailable, the process will keep trying (spinning) until it can acquire the lock. This simplicity creates a small and fast lock.
内核中最常见的锁定原语是自旋锁。自旋锁是一种非常简单的单持有人锁。如果一个进程试图获取一个自旋锁并且它不可用,该进程将继续尝试(自旋)直到它可以获取该锁。这种简单性创建了一个小而快速的锁。
回答by CancerSoftware
Spinlock is used if and only if you are pretty certain that your expected result will happen very shortly, before your thread's execution slice time expires.
当且仅当您非常确定您的预期结果将在您的线程的执行切片时间到期之前很快发生时,才使用自旋锁。
Example: In device driver module, The driver writes "0" in hardware Register R0 and now it needs to wait for that R0 register to become 1. The H/W reads the R0 and does some work and writes "1" in R0. This is generally quick(in micro seconds). Now spinning is much better than going to sleep and interrupted by the H/W. Of course, while spinning, H/W failure condition needs to be taken care!
示例:在设备驱动模块中,驱动程序在硬件寄存器 R0 中写入“0”,现在需要等待该 R0 寄存器变为 1。H/W 读取 R0 并做一些工作并将“1”写入 R0。这通常很快(以微秒为单位)。现在旋转比睡觉和被硬件打断要好得多。当然,在旋转时,需要注意 H/W 故障情况!
There is absolutely no reason for a user application to spin. It doesn't make sense. You are going to spin for some event to happen and that event needs to be completed by another user level application which is never guaranteed to happen within quick time frame. So, I will not spin at all in user mode. I better to sleep() or mutexlock() or semaphore lock() in user mode.
用户应用程序绝对没有理由旋转。这没有意义。您将要为某个事件发生而旋转,并且该事件需要由另一个用户级应用程序完成,而该应用程序永远不会保证在快速时间范围内发生。所以,我不会在用户模式下旋转。我最好在用户模式下使用 sleep() 或 mutexlock() 或 semaphore lock() 。
回答by vinayak jadi
From what is the difference between spin locks and semaphores?by Maciej Piechotka:
从什么是自旋锁和信号量之间的区别?通过Maciej Piechotka:
Both manage a limited resource. I'll first describe difference between binary semaphore (mutex) and spin lock.
Spin locksperform a busy wait - i.e. it keeps running loop:
while (try_acquire_resource ()); ... release();It performs very lightweight locking/unlocking but if the locking thread will be preempted by other which will try to access the same resouce the second one will simply try to acquitre resource untill it run out of it CPU quanta.
On the other hand mutex behave more like:if (!try_lock()) { add_to_waiting_queue (); wait(); } ... process *p = get_next_process_from_waiting_queue (); p->wakeUp ();Hence if the thread will try to acquire blocked resource it will be suspended till it will be avaible for it. Locking/unlocking is much more heavy but the waiting is 'free' and 'fair'.
Semaphoreis a lock that is allowed to be used multiple (known from initialization) number of times - for example 3 threads are allowed to simultainusly hold the resource but no more. It is used for example in producer/consumer problem or in general in queues:
P(resources_sem) resource = resources.pop() ... resources.push(resources) V(resources_sem)
两者都管理有限的资源。我将首先描述二进制信号量(互斥量)和自旋锁之间的区别。
自旋锁执行忙等待 - 即它保持运行循环:
while (try_acquire_resource ()); ... release();它执行非常轻量级的锁定/解锁,但如果锁定线程将被其他试图访问相同资源的其他线程抢占,则第二个线程将简单地尝试获取资源,直到它用完它的 CPU 量。
另一方面,互斥锁的行为更像是:if (!try_lock()) { add_to_waiting_queue (); wait(); } ... process *p = get_next_process_from_waiting_queue (); p->wakeUp ();因此,如果线程试图获取被阻塞的资源,它将被挂起,直到它可用为止。锁定/解锁要重得多,但等待是“免费”和“公平”的。
信号量是一种锁,允许使用多次(从初始化可知)——例如,允许 3 个线程同时持有资源,但不能更多。例如,它用于生产者/消费者问题或通常用于队列:
P(resources_sem) resource = resources.pop() ... resources.push(resources) V(resources_sem)