在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);
  1. 在Thread 1中,首先获得互斥锁,然后判断条件(ready==0)是否成立,如果成立,则调用cv_wait(&condv, &mutex)进入睡眠;
  2. 在Thread 2中,首先获得互斥锁,然后将ready赋值为1,调用cv_signal(&condv)唤醒正在睡眠的Thread1,同时释放持有的互斥锁;
  3. 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)之协同工作原理的更多相关文章

  1. 详解linux互斥锁 pthread_mutex和条件变量pthread_cond

    [cpp] view plaincopy ============================================================= int pthread_creat ...

  2. 深入理解 iOS 开发中的锁

    来源:伯乐在线 - 夏天然后 链接:http://ios.jobbole.com/89474/ 点击 → 申请加入伯乐在线专栏作者 摘要 本文的目的不是介绍 iOS 中各种锁如何使用,一方面笔者没有大 ...

  3. Linux内核互斥锁--mutex

    一.定义: /linux/include/linux/mutex.h   二.作用及访问规则: 互斥锁主要用于实现内核中的互斥访问功能.内核互斥锁是在原子 API 之上实现的,但这对于内核用户是不可见 ...

  4. 线程锁(互斥锁Mutex)

    线程锁(互斥锁Mutex) 一个进程下可以启动多个线程,多个线程共享父进程的内存空间,也就意味着每个线程可以访问同一份数据,此时,如果2个线程同时要修改同一份数据,会出现什么状况? # -*- cod ...

  5. Golang 读写锁RWMutex 互斥锁Mutex 源码详解

    前言 Golang中有两种类型的锁,Mutex (互斥锁)和RWMutex(读写锁)对于这两种锁的使用这里就不多说了,本文主要侧重于从源码的角度分析这两种锁的具体实现. 引子问题 我一般喜欢带着问题去 ...

  6. 一文带你剖析LiteOS互斥锁Mutex源代码

    摘要:多任务环境下会存在多个任务访问同一公共资源的场景,而有些公共资源是非共享的临界资源,只能被独占使用.LiteOS使用互斥锁来避免这种冲突,互斥锁是一种特殊的二值性信号量,用于实现对临界资源的独占 ...

  7. 互斥锁Mutex与信号量Semaphore的区别

    转自互斥锁Mutex与信号量Semaphore的区别 多线程编程中,常常会遇到这两个概念:Mutex和Semaphore,两者之间区别如下: 有人做过如下类比: Mutex是一把钥匙,一个人拿了就可进 ...

  8. 线程锁(互斥锁Mutex)及递归锁

    一.线程锁(互斥锁) 在一个程序内,主进程可以启动很多个线程,这些线程都可以访问主进程的内存空间,在Python中虽然有了GIL,同一时间只有一个线程在运行,可是这些线程的调度都归系统,操作系统有自身 ...

  9. Linux组件封装(一)中互斥锁MutexLock的封装

    本文对Linux中的pthread_mutex_t做一个简易的封装. 互斥锁主要用于互斥,互斥是一种竞争关系,主要是某一个系统资源或一段代码,一次做多被一个线程访问. 条件变量主要用于同步,用于协调线 ...

随机推荐

  1. Memcached快递上手之C#

    Memcached快递上手之C# Memcached是开源高性能分布式缓存组件,目前已经广泛应用各类互联网领域. 具有多种语言的客户端开发包,包括:Perl/PHP/JAVA/C/Python/Rub ...

  2. JavaScript里的依赖注入

    JavaScript里的依赖注入 我喜欢引用这句话,“程序是对复杂性的管理”.计算机世界是一个巨大的抽象建筑群.我们简单的包装一些东西然后发布新工具,周而复始.现在思考下,你所使用的语言包括的一些内建 ...

  3. GoLang获取struct的tag

    GoLang获取struct的tag内容:beego的ORM中也通过tag来定义参数的. 获取tag的内容是利用反射包来实现的.示例代码能清楚的看懂! package main import ( &q ...

  4. Android Recovery模式学习体会

        最近在学习Android的Recovery模式,感觉它和Windows的安全模式很相似.两者的工作原理都是只加载少量的系统组件(内核是必须的),使系统运行在最小模式,这样就可以在不影响当前系统 ...

  5. 【CSS】圆角阴影边框CSS

    .someClassName { width:300px; display: inline-block; padding: 5px 10px 6px; text-decoration: none; b ...

  6. [转]ARM/Thumb2PortingHowto

    src: https://wiki.edubuntu.org/ARM/Thumb2PortingHowto#ARM_Assembler_Overview When you see some assem ...

  7. poj3083走玉米地问题

    走玉米地迷宫,一般有两种简单策略,遇到岔路总是优先沿着自己的左手方向,或者右手方向走.给一个迷宫,给出这两种策略的步数,再给出最短路径的长度. ######### #.#.#.#.# S....... ...

  8. Devexpress XtraReports 交叉报表

    [原创]Devexpress XtraReports 系列 5 创建交叉报表   昨天我们已经介绍了如何创建多栏报表,详见:[原创]Devexpress XtraReports 系列 4 创建多栏报表 ...

  9. mybatis3.4测试CRUD

    导入包 H:\jar\jdbc\mysql-connector-java-5.1.13-bin.jarH:\jar\mybatis\mybatis-3.4.1\mybatis-3.4.1.jarH:\ ...

  10. 配置 SQL Server Email 发送以及 Job 的 Notification通知功能

    配置 SQL Server Email 发送以及 Job 的 Notification通知功能 在与数据库相关的项目中, 比如像数据库维护, 性能警报, 程序出错警报或通知都会使用到在 SQL Ser ...