内核同步机制

  • 原子变量
  • 自旋锁
  • Semaphore

 

用户态同步机制

 

 

自旋锁

自旋锁是专为防止多处理器并发而引入的一种锁,它在内核中大量应用于中断处理等部分(对于单处理器来说,防止中断处理中的并发可简单采用关闭中断的方式,即在标志寄存器中关闭/打开中断标志位,不需要自旋锁)。

何谓自旋锁?它是为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。

使用自旋锁时需要注意: 不能递归获取同一个自旋锁,否则会产生死锁。使用时间要比较短,否则容易占用大量CPU时间。

读写锁

读写锁实际是一种特殊的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。这种锁相对于自旋锁而言,能提高并发性,因为在多处理器系统中,它允许同时有多个读者来访问共享资源,最大可能的读者数为实际的逻辑CPU数。写者是排他性的,一个读写锁同时只能有一个写者或多个读者(与CPU数相关),但不能同时既有读者又有写者。   

在读写锁保持期间也是抢占失效的。

如果读写锁当前没有读者,也没有写者,那么写者可以立刻获得读写锁,否则它必须自旋在那里,直到没有任何写者或读者。如果读写锁没有写者,那么读者可以立即获得该读写锁,否则读者必须自旋在那里,直到写者释放该读写锁。

通常, 当读写锁处于读模式锁住状态时, 如果有另外线程试图以写模式加锁, 读写锁通常会阻塞随后的读模式锁请求, 这样可以避免读模式锁长期占用, 而等待的写模式锁请求长期阻塞。

Slim Reader/Writer (SRW) Locks中的Shared mode就相当于读锁, 而exclusive mode就相当于写锁。

 

Semaphore(信号量)

Semaphore 可以认为是一种互斥锁,如果有一个任务试图获得一个已被持有的信号量时,信号量会将其推入等待队列,然后让其睡眠。这时处理器获得自由去执行其它代码。当持有信号量的进程将信号量释放后,在等待队列中的一个任务将被唤醒,从而便可以获得这个信号量。

1) 由于争用信号量的进程在等待锁重新变得可用时会睡眠,所以信号量适合用于锁被长时间持有的情况

2) 因为睡眠、维护等待队列以及唤醒所花费的开销可能比锁占用的全部时间还有长

3) 由于执行线程在锁被争用时会睡眠,所以只能在进程上下文中才能获取信号量锁,在中断上下文中是不能进行调度的

4) 持有信号量的进程可以睡眠,因为其他进程试图获取同一信号量锁时不会因此而死锁,而持有信号量的进程最终会继续执行的

5) 持有信号量的进程不能占用自旋锁。因为等待信号量时会睡眠,而在持有自旋锁时是不允许睡眠的,往往在需要和用户空间同步时,你的代码会需要睡眠,此时使用信号量是唯一的选择。信号量不同于自旋锁,它不会禁止内核抢占,所以持有信号量的代码可以被抢占。这就意味着信号量不会对调度的等待时间带来负面影响。

信号量的一个重要特性,它可以同时允许任意数量的所持有者,而自旋锁在一个时刻最多允许一个任务持有它。信号量同时允许的持有者数量实在声明信号量时指定,这个值称为使用者数量。通常情况下,信号量和自旋锁一样,在一个时刻只允许一个持有者。这时计数等于1,这样的信号量称为二值信号量或互斥信号量。

在内核中实现信号量时,会利用spinlock进行保护,包括记数以及对waiter list的访问。

 

Condition Variable

 

 

Mutex

内核对象,需要内核和用户态的切换,代价比较高。同时,因为它是内核对象,所以可以跨进程使用。

 

Critical Section

非内核对象

 

Event

 

 

Reference:

http://msdn.microsoft.com/en-us/library/windows/desktop/ms681924(v=vs.85).aspx