mutex(互斥锁)与spinlock的区别主要在于前者是sleep wait,后者是busy wait,因此mutex不可以用于中断上下文的操作,代码如下。
mutex在等待的时候不会关闭抢占,而是把CPU调度给别人,当前逻辑相当于等待事件就绪被唤醒。由于大部分竞态发生时都只有一个其他的逻辑在持有锁,并且很快会被释放,所以mutex中专门添加了对这种情况的自旋等待优化。当发现没有其他逻辑在等待mutex释放时,当前逻辑就会优化自旋等待锁的释放。这个优化功能是通过osq域实现的。
mutex的结构体中有三个主要的成员:owner、wait_lock和wait_list链表。owner就是mutex的真实锁,是否获得锁就是通过设置owner来完成的,mutex的阻塞等待被组织成一个链表wait_list,操作这个链表的时候要持有自旋锁wait_lock,而实际在阻塞等待时,将CPU交给调度器以让其他线程得以运行之前,需要释放这个锁,代码如下。
判断一个mutex是否上锁是通过lock->owner的值屏蔽掉值里面用来表示flag的几位之后得到的,0表示没有上锁。如果上锁,那么值是当前持有锁的线程task_struct结构体的指针。这里的flag占了三位,指针占了剩下的部分,也就可以看出来task_struct结构体至少是8字节(也就是64位)对齐的,实际上这个数据结构是cache line大小对齐的。mutex与信号量很相似,两者在实现的时候也反映了类似的编码风格。
mutex的主要函数实现如下。
mutex释放锁的逻辑比较简单,简单取出列表的第一个等待者,然后有针对性地唤醒即可。管理等待列表都是在mutex_lock中操作的,mutex_unlock并不操作链表。这种设计使得等待队列的单入口管理变得简单。