持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第15天,点击查看活动详情
在Linux内核中,除信号量以外,还有一个类似的实现叫作互斥锁(mutex)。信号量是在并行处理环境中对多个处理器访问某个公共资源进行保护的机制,互斥锁用于互斥操作。 信号量根据初始count的大小,可以分为计数信号量和互斥信号量。根据著名的洗手间理论,信号量相当于一个可以同时容纳N个人的洗手间,只要洗手间人不满,其他人就可以进去,如果人满了,其他人就要在外面等待。互斥锁类似于街边的移动洗手间,每次只能进去一个人,里面的人出来后才能让排队中的下一个人进去。既然互斥锁类似于count值等于1的信号量,为什么内核社区要重新开发互斥锁,而不是复用信号量的机制呢? 互斥锁最早是在Linux 2.6.16内核中由Red Hat Enterprise Linux的资深内核专家Ingo Molnar设计和实现的。信号量的count成员可以初始化为1,并且down()和up()函数也可以实现类似于互斥锁的功能,那为什么要单独实现互斥锁机制呢?Ingo Molnar认为,在设计之初,信号量在Linux内核中的实现没有任何问题,但是互斥锁相对于信号量要简单轻便一些。在锁争用激烈的测试场景下,互斥锁比信号量执行速度更快,可扩展性更好。另外,mutex数据结构的定义比信号量小。这些都是在互斥锁设计之初Ingo Molnar提到的优点。互斥锁上的一些优化方案(如自旋等待)已经移植到了读写信号量中。
mutex数据结构
下面来看mutex数据结构的定义。
<include/linux/mutex.h>
struct mutex {
atomic_long_t owner;
spinlock_t wait_lock;
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
struct optimistic_spin_queue osq;
#endif
struct list_head wait_list;
};
wait_lock:自旋锁,用于保护wait_list睡眠等待队列。 wait_list:用于管理所有在互斥锁上睡眠的进程,没有成功获取锁的进程会在此链表上睡眠。 owner:Linux 4.10内核把原来的count成员和owner成员合并成一个。原来的count是一个原子值,1表示锁没有被持有,0表示锁被持有,负数表示锁被持有且有等待者在排队。现在新版本的owner中,0表示锁没有未被持有,非零值则表示锁持有者的task_struct指针的值。另外,最低3位有特殊的含义。