zoukankan      html  css  js  c++  java
  • 深入理解Solaris内核中互斥锁(mutex)与条件变量(condvar)之协同工作原理

    在Solaris上写内核模块总是会用到互斥锁(mutex)与条件变量(condvar), 光阴荏苒日月如梭弹指一挥间,Solaris的大船说沉就要沉了,此刻心情不是太好(Orz)。每次被年轻的有才华的同事们(比如Letty同学)问起mutex和cv怎么协同工作的,我总是不能给出一个非常清晰的解释。直到今天,看了cv_wait()的源代码之后,我终于可以给他们一个清楚明白的回答了。

    Solaris的源码无法被公开粘贴出来,幸好还有OpenSolaris的继承者illumos。 先贴cv_wait()的源码,再讲互斥锁(mutex)与条件变量(condvar)的协同工作原理。

    185 /*
    186  * Block on the indicated condition variable and release the
    187  * associated kmutex while blocked.
    188  */
    189 void
    190 cv_wait(kcondvar_t *cvp, kmutex_t *mp)
    191 {
    192     if (panicstr)
    193             return;
    194     ASSERT(!quiesce_active);
    195
    196     ASSERT(curthread->t_schedflag & TS_DONT_SWAP);
    197     thread_lock(curthread);                 /* lock the thread */
    198     cv_block((condvar_impl_t *)cvp);
    199     thread_unlock_nopreempt(curthread);     /* unlock the waiters field */
    200     mutex_exit(mp);
    201     swtch();
    202     mutex_enter(mp);
    203 }

    注意: 198, 200-202行,等会儿再解释。

    首先,一个典型的使用mutex和cv的例子是这样子滴,

     1 static kmutex_t         mutex;
     2 static kcondvar_t       condv;
     3 static unsigned int     ready = 0;
     4 
     5 /* 1. init mutex and cv */
     6 mutex_init(&mutex, NULL, MUTEX_DRIVER, NULL);
     7 cv_init(&condv, NULL, CV_DRIVER, NULL);
     8 
     9 /* 2. use mutex and cv */
    10 
    11   /* Thread 1 */               | /* Thread 2 */
    12   mutex_enter(&mutex);         | mutex_enter(&mutex);
    13   while (ready == 0)           | ready = 1;
    14       cv_wait(&condv, &mutex); | cv_signal(&condv);
    15   mutex_exit(&mutex);          | mutex_exit(&mutex);
    16 
    17 /* 3. destroy mutex and cv */
    18 cv_destroy(&condv);
    19 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); 201 swtch(); 202 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等。

  • 相关阅读:
    17. Letter Combinations of a Phone Number java solutions
    43. Multiply Strings java solutions
    3. Longest Substring Without Repeating Characters java solutions
    51. N-Queens java solutions
    74. Search a 2D Matrix java solutions
    48. Rotate Image java solutions
    64. Minimum Path Sum java solutions
    leetcode : Container With Most Water
    leetcode : reverse integer
    leetcode : ZigZag Conversion
  • 原文地址:https://www.cnblogs.com/idorax/p/6285337.html
Copyright © 2011-2022 走看看