深入理解Solaris内核中互斥锁(mutex)与条件变量(condvar)之协同工作原理
在Solaris上写内核模块总是会用到互斥锁(mutex)与条件变量(condvar), 光阴荏苒日月如梭弹指一挥间,Solaris的大船说沉就要沉了,此刻心情不是太好(Orz)。每次被年轻的有才华的同事们(比如Letty同学)问起mutex和cv怎么协同工作的,我总是不能给出一个非常清晰的解释。直到今天,看了cv_wait()的源代码之后,我终于可以给他们一个清楚明白的回答了。
Solaris的源码无法被公开粘贴出来,幸好还有OpenSolaris的继承者illumos。 先贴cv_wait()的源码,再讲互斥锁(mutex)与条件变量(condvar)的协同工作原理。
/*
186 * Block on the indicated condition variable and release the
187 * associated kmutex while blocked.
188 */
void
cv_wait(kcondvar_t *cvp, kmutex_t *mp)
{
if (panicstr)
return;
ASSERT(!quiesce_active); ASSERT(curthread->t_schedflag & TS_DONT_SWAP);
thread_lock(curthread); /* lock the thread */
cv_block((condvar_impl_t *)cvp);
thread_unlock_nopreempt(curthread); /* unlock the waiters field */
mutex_exit(mp);
swtch();
mutex_enter(mp);
}
注意: 198, 200-202行,等会儿再解释。
首先,一个典型的使用mutex和cv的例子是这样子滴,
static kmutex_t mutex;
static kcondvar_t condv;
static unsigned int ready = ; /* 1. init mutex and cv */
mutex_init(&mutex, NULL, MUTEX_DRIVER, NULL);
cv_init(&condv, NULL, CV_DRIVER, NULL); /* 2. use mutex and cv */ /* Thread 1 */ | /* Thread 2 */
mutex_enter(&mutex); | mutex_enter(&mutex);
while (ready == ) | ready = ;
cv_wait(&condv, &mutex); | cv_signal(&condv);
mutex_exit(&mutex); | mutex_exit(&mutex); /* 3. destroy mutex and cv */
cv_destroy(&condv);
mutex_destroy(&mutex);
- 在Thread 1中,首先获得互斥锁,然后判断条件(ready==0)是否成立,如果成立,则调用cv_wait(&condv, &mutex)进入睡眠;
- 在Thread 2中,首先获得互斥锁,然后将ready赋值为1,调用cv_signal(&condv)唤醒正在睡眠的Thread1,同时释放持有的互斥锁;
- Thread1一旦醒来,会重新判断条件(ready==0)是否成立,如果不成立,则释放互斥锁。 (当然,如果成立,则将再次进入睡眠,等待下次被唤醒)
然后, 问题(Letty同学曾经问过我的)来了: 既然Thread 1在L12行获得了互斥锁然后睡过去了,那么Thread 2怎么可能获得互斥锁?
This is a good question, a really good question! (ps. 老美每次被问住了的时候都这么说)
在今天之前我无法回答,或者只能估摸着回答说"只能看具体实现了"。 好了,我今天就是真看完了具体实现。在cv_wait()的源代码中,
198 cv_block((condvar_impl_t *)cvp);
...
200 mutex_exit(mp);
swtch();
mutex_enter(mp);
第198行将自己(currthread)加入睡眠队列,第200行将互斥锁释放,然后在第201行进入睡眠,等待被唤醒。一旦被唤醒,在第202行重新获得互斥锁。
也就是说,睡前释放互斥锁,醒来再获取互斥锁。这样别的线程就有机会获得互斥锁后干活,活干完后将睡眠的线程唤醒。
这也解释了为什么cv_wait()函数不仅仅只有一个参数kcondvar_t *cvp, 还包含参数kmutex_t *mp。
行文至此,我想用一句话作为总结,"The source code is the final world." 如果你想成为一个非常优秀的程序员,请记住RTFSC。

PS:
1. 如果想进一步弄懂为什么要将条件变量和互斥锁一起使用保证同步,请自行google或阅读OS相关的book。
2. 所有关于互斥锁和条件变量的协同工作原理应该是一致的,比如POSIX的pthread_mutex和pthread_cond,Linux内核mutex和completion variable等。
深入理解Solaris内核中互斥锁(mutex)与条件变量(condvar)之协同工作原理的更多相关文章
- 详解linux互斥锁 pthread_mutex和条件变量pthread_cond
[cpp] view plaincopy ============================================================= int pthread_creat ...
- 深入理解 iOS 开发中的锁
来源:伯乐在线 - 夏天然后 链接:http://ios.jobbole.com/89474/ 点击 → 申请加入伯乐在线专栏作者 摘要 本文的目的不是介绍 iOS 中各种锁如何使用,一方面笔者没有大 ...
- Linux内核互斥锁--mutex
一.定义: /linux/include/linux/mutex.h 二.作用及访问规则: 互斥锁主要用于实现内核中的互斥访问功能.内核互斥锁是在原子 API 之上实现的,但这对于内核用户是不可见 ...
- 线程锁(互斥锁Mutex)
线程锁(互斥锁Mutex) 一个进程下可以启动多个线程,多个线程共享父进程的内存空间,也就意味着每个线程可以访问同一份数据,此时,如果2个线程同时要修改同一份数据,会出现什么状况? # -*- cod ...
- Golang 读写锁RWMutex 互斥锁Mutex 源码详解
前言 Golang中有两种类型的锁,Mutex (互斥锁)和RWMutex(读写锁)对于这两种锁的使用这里就不多说了,本文主要侧重于从源码的角度分析这两种锁的具体实现. 引子问题 我一般喜欢带着问题去 ...
- 一文带你剖析LiteOS互斥锁Mutex源代码
摘要:多任务环境下会存在多个任务访问同一公共资源的场景,而有些公共资源是非共享的临界资源,只能被独占使用.LiteOS使用互斥锁来避免这种冲突,互斥锁是一种特殊的二值性信号量,用于实现对临界资源的独占 ...
- 互斥锁Mutex与信号量Semaphore的区别
转自互斥锁Mutex与信号量Semaphore的区别 多线程编程中,常常会遇到这两个概念:Mutex和Semaphore,两者之间区别如下: 有人做过如下类比: Mutex是一把钥匙,一个人拿了就可进 ...
- 线程锁(互斥锁Mutex)及递归锁
一.线程锁(互斥锁) 在一个程序内,主进程可以启动很多个线程,这些线程都可以访问主进程的内存空间,在Python中虽然有了GIL,同一时间只有一个线程在运行,可是这些线程的调度都归系统,操作系统有自身 ...
- Linux组件封装(一)中互斥锁MutexLock的封装
本文对Linux中的pthread_mutex_t做一个简易的封装. 互斥锁主要用于互斥,互斥是一种竞争关系,主要是某一个系统资源或一段代码,一次做多被一个线程访问. 条件变量主要用于同步,用于协调线 ...
随机推荐
- Love myself...
Sometimes we feel as if our lives rely on that one person. We think 'If I do this, he/she will like ...
- 最小生成树算法prim and kruskal
一.最小生成树定义: 从不同顶点出发或搜索次序不同,可得到不同的生成树 生成树的权:对连通网络来说,边附上权,生成树也带权,我们把生成树各边的权值总和称为生成树的权 最小代价生成树:在一个连通网 ...
- C#类的初始化
类的构造函数 类的构造函数,有实例构造函数和静态构造函数.如果我们没有构造函数,系统会为我们生成一个默认构造函数,如果我们已经定义了构造函数,系统就不会再为我们生成构造函数. class Simp ...
- [转]Avoiding GDB Signal Noise.
原文:http://peeterjoot.wordpress.com/2010/07/07/avoiding-gdb-signal-noise/ A quick note for future ref ...
- 判断UA这种事不能说的太明。
[微博] Mozilla/5.0 (Linux; U; Android 4.2.2; zh-cn; GT-I9502 Build/JDQ39) AppleWebKit/534.30 (KHTML, l ...
- AOP的成员介绍
AOP(Aspect Oriented Programming)面向切面编程,AOP的作用不过多介绍,本文是主要是介绍AOP的成员,是我在复习的时候记录的一些笔记,方便以后查阅方便一些. JointP ...
- Redis系统学习 四、超越数据结构
5种数据结构组成了Redis的基础,其他没有关联特定数据结构的命令也有很多.我们已经看过一些这样的命令:info,select,flushdb,multi,exec,discard,watch,和ke ...
- ngx-push-stream模块源码学习(五)——内存清理
1.定时器 采用nginx自身的定时器管理机制,具体细节待学习过nginx源码后加以补充 2.channel的生成周期 (0).初始(诞生) 发布.订阅均有可能产生ch ...
- MFC中菜单变灰的问题
MFC中菜单变灰 方法1:有UpdateCmdUI句柄时 定义一个BOOL变量m_b来标志菜单项是否有效, 该菜单项响应UPDATE_COMMAND_UI消息,在消息处理函数中pCmdUI->E ...
- API接口服务端
<?php /** * API接口服务端 * * */ require 'mysql_class.php'; header('Content-Type:text/html;charset=utf ...