http://www.360doc.com/content/12/0404/10/9400799_200672862.shtml
看过Linux内核的同学都知道,Linux内核中除了有semaphore之外,还有一个mutex lock。前者我们的操作系统教科书称之为信号量,后者不知道教科书有没有具体的名称,但是在Linux内核中,它的称谓是"互斥锁"或者“互斥体”(总之,称谓不是问题)。为了提升一下本帖的理论密度,特从Wiki中摘录一段关于semaphore的描述:
“In computer science, a semaphore is a variable or abstract data type that provides a simple but useful abstraction for controlling access by multiple processes to a common resource in a parallel programming environment.A useful way to think of a semaphore is as a record of how many units of a particular resource are available, coupled with operations to safely (i.e., without race conditions) adjust that record as units are required or become free, and if necessary wait until a unit of the resource becomes available. Semaphores are a useful tool in the prevention of race conditions and deadlocks; however, their use is by no means a guarantee that a program is free from these problems. Semaphores which allow an arbitrary resource count are called counting semaphores, while semaphores which are restricted to the values 0 and 1 (or locked/unlocked, unavailable/available) are called binary semaphores (same functionality that mutexes have).”。
其中关键信息主要是“a semaphore is a data type for controlling access by multiple processes to a common resource in a parallel programming environment... Semaphores which allow an arbitrary resource count are called counting semaphores, while semaphores which are restricted to the values 0 and 1 (or locked/unlocked, unavailable/available) are called binary semaphores (same functionality that mutexes have)”,也即信号量在并行处理环境下对多个processes访问某个公共资源进行保护,后面提到的binary semaphore,本质上应该就是mutex了,从same functionality that mutexes have这句话来看,mutex和binary semaphore功能应该相同。从以上的文字中显然可以看到,相对mutex而言信号量的适用范围更广(mutex只是信号量的用途之一),这个我们接下来在后续的Linux源码中也可以看到这其中某些细微之处的区分。
*注:昨天写这个帖子时手头没有操作系统方面的书籍拿来参考,今天我翻了一下《现代操作系统》(陈向群等译,机械工业出版社 1999年11月第1版), 关于这个话题,书里明确提到的只有"2.2.5 信号量",至于mutex,书中并没有作为一个独立的概念提出来,只是在讲信号量时提到了上面所说的binary semaphore,并且说“信号量mutex(应该是指binary semaphore)用于互斥...互斥是避免混乱所必需的操作...信号量的另一种用途是用于实现同步(synchronization)。信号量full和empty用来保证一定的事件顺序发生或不发生。在本例中,它们保证缓冲区满的时候生产者停止运行,或者当缓冲区空的时候消费者停止运行。这种用法与互斥是不同的” --- P30-31 *
OK,理论上的概念有了,那么就来看看实际当中Linux下的semaphone到底长的啥样。以下是semaphore在Linux源码中的定义,源码来自3.2.9:
<include/linux/semaphore.h>
- /* Please don't access any members of this structure directly */
- struct semaphore {
- raw_spinlock_t lock;
- unsigned int count;
- struct list_head wait_list;
- };
- #define DECLARE_MUTEX(name) \
- struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)
那么既然count=1的信号量可以用来完成mutex lock的功能,那么内核何必再多此一举弄出个mutex lock出来呢?
关于Linux内核中的mutex机制,一篇很重要的文档来自内核源码中的Documentation/mutex-design.txt,由Ingo molnar同学起头,标题是"Generic Mutex Subsystem",这篇文档开宗名义,直接将1楼中最后一个问题给端了出来(因此我估计这个问题此前已经有很多人骚扰过Ingo等同学了):
"Why on earth do we need a new mutex subsystem, and what's wrong with semaphores?" 前面已经讲过,当struct semaphore中的成员变量为1,就可以用来实现mutex这种东西,而且内核也明确定义了DEFINE_SEMAPHORE宏将count初始化为1,信号量上的DOWN与UP操作就更不用说了,在内核中也都有很好的实现,难道这种binary semaphore机制还不能满足我们的要求吗,干嘛还非得弄一个新的mutex机制出来呢?
下面是Ingo同学对此的解释,他说“firstly, there's nothing wrong with semaphores. But if the simpler mutex semantics are sufficient for your code, then there are a couple of advantages of mutexes”,就是说,信号量在Linux中的实现是没任何问题的(上来先安抚一下大家躁动的心情),但是mutex的语义相对来说要较信号量要来得简单,所以如果你的代码若只是想对某一共享资源进行互斥访问的话,那么使用这种简化了的mutex机制可以带来如下的一坨好处。对这句话我的理解是,mutex将binary semaphore的实现简化了(the simper mutex),因此如果单纯从互斥的角度,用mutex会有很多好处。
接下来Ingo列出的一大堆使用mutex的好处,在这个帖子中我们将一条一条地来看,再结合内核源码,看看事实是否的确象他说的那样:
- 'struct mutex' is smaller on most architectures: E.g. on x86, 'struct semaphore' is 20 bytes, 'struct mutex' is 16 bytes. A smaller structure size means less RAM footprint, and better CPU-cache utilization.
这条最好验证,尤其还是x86平台,找个简单的内核模块,打印一下sizeof就可以了。在我的x86-64 32位Linux系统(内核版本2.6.37)上, struct semaphore的大小是16字节,而struct mutex的大小则是20字节,另两台x86-64 64位Linux系统(内核版本3.x)上的结果则是,struct semaphore的大小是24字节,而struct mutex的大小则是32字节。这里不妨看一下struct mutex在内核中的定义:
<include/linux/mutex.h>
- struct mutex {
- /* 1: unlocked, 0: locked, negative: locked, possible waiters */
- atomic_t count;
- spinlock_t wait_lock;
- struct list_head wait_list;
- #if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP)
- struct task_struct *owner;
- #endif
- #ifdef CONFIG_DEBUG_MUTEXES
- const char *name;
- void *magic;
- #endif
- #ifdef CONFIG_DEBUG_LOCK_ALLOC
- struct lockdep_map dep_map;
- #endif
- };
- struct mutex {
- /* 1: unlocked, 0: locked, negative: locked, possible waiters */
- atomic_t count;
- spinlock_t wait_lock;
- struct list_head wait_list;
- struct task_struct *owner;
- };
所以接下来Ingo提到的tighter code的优势,估计对mutex而言也不复存在了... (他本人对mutex相对于semaphore在RAM footprint方面的优势不复存在的最新回复是:"Mutex got larger due to the adaptive spin-mutex performance optimization",因此我很自然地将这句话理解成,由于要实现所谓的“adaptive spin-mutex performance optimization",那么就不惜牺牲了"less RAM footprint, and better CPU-cache utilization",所以我们有理由期待接下来的spin-mutex performance optimization会给mutex带来性能上的飞跃...)