1、互斥锁是管理临界资源的一种有效手段。因为互斥锁是独占的,所以在一个时刻只允许一个线程占有互斥锁,利用这个性质来实现共享资源的互斥锁保护。任何时刻只允许一个线程获得互斥量对象,未能够获得互斥量对象的线程被挂起在该互斥量的等待线程队列上。1 互斥锁控制块cpp view plaincopyprint?1. /* 2. * Mutual exclusion (mutex) structure 3. */ 4. struct rt_mutex 5. 6. struct rt_ipc_object parent; /*parent.parent), RT_Object_Class_Mutex, name
2、);/初始化互斥锁的内核对象 17. 18. /* init ipc object */ 19. rt_ipc_object_init(/初始化互斥锁的 IPC 对象 20. 21. mutex-value = 1;/设置互斥锁值初始化为 1 22. mutex-owner = RT_NULL;/初始化互斥锁当前没有拥有者 23. mutex-original_priority = 0xFF;/初始化互斥锁原始优先级为 255,即拥有者线程的优先级 24. mutex-hold = 0;/初化为当前互斥锁的被 take 的次数为 0(同一线程) 25. 26. /* set flag */ 2
3、7. mutex-parent.parent.flag = flag;/设置互斥锁的内核对象标志 28. 29. return RT_EOK; 30. 注意互斥锁在初始化时其值 value 被初始化为 1,即默认情况下其是可用的.在其被 take 之后 value 值减 1 变为 0 时才不可用.2.2 创建互斥锁cpp view plaincopyprint?1. /* 2. * This function will create a mutex from system resource 3. * 4. * param name the name of mutex 5. * param fl
4、ag the flag of mutex 6. * 7. * return the created mutex, RT_NULL on error happen 8. * 9. * see rt_mutex_init 10. */ 11.rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag) 12. 13. struct rt_mutex *mutex; 14. 15. RT_DEBUG_NOT_IN_INTERRUPT;/确保此函数不是在 ISR 中使用 16. 17. /* allocate object */ 18. m
5、utex = (rt_mutex_t)rt_object_allocate(RT_Object_Class_Mutex, name);/动态分配一个互斥锁 19. if (mutex = RT_NULL) 20. return mutex; 21. 22. /* init ipc object */ 23. rt_ipc_object_init(/初始化此互斥锁的内核 IPC 对象 24. 25. mutex-value = 1;/初始化互斥锁的值为 1 26. mutex-owner = RT_NULL;/无拥有者 27. mutex-original_priority = 0xFF;/互斥
6、锁原始优先级为 255 28. mutex-hold = 0;/take 此互斥锁的线程个数(同一线程) 次数为 0 29. 30. /* set flag */ 31. mutex-parent.parent.flag = flag;/设置互斥锁内核对象标志 32. 33. return mutex; 34. 此函数与初始化类似,直接跳过 .3 脱离或删除互斥锁3.1 脱离互斥锁cpp view plaincopyprint?1. /* 2. * This function will detach a mutex from resource management 3. * 4. * para
7、m mutex the mutex object 5. * 6. * return the operation status, RT_EOK on successful 7. * 8. * see rt_mutex_delete 9. */ 10.rt_err_t rt_mutex_detach(rt_mutex_t mutex) 11. 12. RT_ASSERT(mutex != RT_NULL); 13. 14. /* wakeup all suspend threads */ 15. rt_ipc_list_resume_all(/唤醒所有挂起的线程 16. 17. /* detach
8、 semaphore object */ 18. rt_object_detach(/脱离互斥锁的内核对象 19. 20. return RT_EOK; 21. 脱离互斥锁只要是唤醒挂起的线程及脱离其内核对象,没有什么好说明的.3.2 删除互斥锁cpp view plaincopyprint?1. /* 2. * This function will delete a mutex object and release the memory 3. * 4. * param mutex the mutex object 5. * 6. * return the error code 7. * 8.
9、 * see rt_mutex_detach 9. */ 10.rt_err_t rt_mutex_delete(rt_mutex_t mutex) 11. 12. RT_DEBUG_NOT_IN_INTERRUPT;/确保此函数不是在 ISR 中使用 13. 14. RT_ASSERT(mutex != RT_NULL); 15. 16. /* wakeup all suspend threads */ 17. rt_ipc_list_resume_all(/唤醒所有挂起的线程 18. 19. /* delete semaphore object */ 20. rt_object_delet
10、e(/删除互斥锁的内核对象 21. 22. return RT_EOK; 23. 除了唤醒挂起的线程还需要删除其内核对象.3.3 获取互斥锁cpp view plaincopyprint?1. /* 2. * This function will take a mutex, if the mutex is unavailable, the 3. * thread shall wait for a specified time. 4. * 5. * param mutex the mutex object 6. * param time the waiting time 7. * 8. * re
11、turn the error code 9. */ 10.rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t time) 11. 12. register rt_base_t temp; 13. struct rt_thread *thread; 14. 15. /* this function must not be used in interrupt even if time = 0 */ 16. RT_DEBUG_NOT_IN_INTERRUPT;/确保此函数不是在 ISR 中使用 17. 18. RT_ASSERT(mutex !=
12、RT_NULL); 19. 20. /* disable interrupt */ 21. temp = rt_hw_interrupt_disable();/关中断 22. 23. /* get current thread */ 24. thread = rt_thread_self();/获取当前线程 25. 26. RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, ( 27. 28. RT_DEBUG_LOG(RT_DEBUG_IPC, 29. (“mutex_take: current thread %s, mutex value: %d, ho
13、ld: %dn“, 30. thread-name, mutex-value, mutex-hold); 31. 32. /* reset thread error */ 33. thread-error = RT_EOK;/设置当前线程的错误标志为 RT_EOK 34. 35. if (mutex-owner = thread)/如果当前互斥锁的拥有者就是当前线程, 即当前线程重复 take 同一互斥锁 ,则当前互斥锁的 take 次数加 1 36. 37. /* its the same thread */ 38. mutex-hold +; 39. 40. else/如果当前互斥锁的拥有
14、者不是本身线程 41. 42. /* The value of mutex is 1 in initial status. Therefore, if the 43. * value is great than 0, it indicates the mutex is avaible. 44. */ 45. if (mutex-value 0)/当前互斥锁的值大于 0,则说明此互斥锁可用 46. 47. /* mutex is available */ 48. mutex-value -;/将互斥锁的值减 1 49. 50. /* set mutex owner and original pr
15、iority */ 51. mutex-owner = thread;/设置互斥锁的拥有者为当前线程 52. mutex-original_priority = thread-current_priority;/设置互斥锁的原始优先级为当前线程的优先级 53. mutex-hold +;/互斥锁被成功 take,take 次数加 1 54. 55. else/如果当前互斥锁不可用 56. 57. /* no waiting, return with timeout */ 58. if (time = 0)/如果等待参数为 0,则应立即返回错误 59. 60. /* set error as t
16、imeout */ 61. thread-error = -RT_ETIMEOUT;/设置超时错误 62. 63. /* enable interrupt */ 64. rt_hw_interrupt_enable(temp);/开中断 65. 66. return -RT_ETIMEOUT;/返回超时错误 67. 68. else/如果时间参数不为 0 69. 70. /* mutex is unavailable, push to suspend list */ 71. RT_DEBUG_LOG(RT_DEBUG_IPC, (“mutex_take: suspend thread: %sn
17、“, 72. thread-name); 73. 74. /* change the owner thread priority of mutex */ 75. if (thread-current_priority owner-current_priority)/如果当前线程的优先级比互斥锁拥有者线程大的话(优先级值越小,优先级越高) 76. 77. /* change the owner thread priority */ 78. rt_thread_control(mutex-owner,/则修改当前互斥锁拥有者线程的优先级降为当前线程的优先级,如果不这么做的话,则如果拥有者线程永远不
18、释放,则当前线程永远不可能获取此互斥锁 ,这样有可以会造成问题 79. RT_THREAD_CTRL_CHANGE_PRIORITY,/这样拥有者线程和当前线程优先级相同,系统采用时间片轮转方式, 当前线程就有了获得互斥锁的可能了 80. 81. 82. 83. /* suspend current thread */ 84. rt_ipc_list_suspend( 87. 88. /* has waiting time, start thread timer */ 89. if (time 0)/如果时间参数大于 0 90. 91. RT_DEBUG_LOG(RT_DEBUG_IPC, 9
19、2. (“mutex_take: start the timer of thread:%sn“, 93. thread-name); 94. 95. /* reset the timeout of thread timer and start it */ 96. rt_timer_control( 99. rt_timer_start(/启动定时器 100. 101. 102. /* enable interrupt */ 103. rt_hw_interrupt_enable(temp);/开中断 104. 105. /* do schedule */ 106. rt_schedule();
20、/立即重新调度线程 107. 108. if (thread-error != RT_EOK)/等待时间已到,但还是未成功获得互斥锁时,thread-error 的值会被设为-RT_ETIMEOUT,见 thread.c 源文件中的 rt_thread_timeout 函数 109. 110. /* return error */ 111. return thread-error; 112. 113. else/如果在另一线程执行 rt_mutex_release 函数唤醒此线程,则此线程在在处被唤醒时即已经获得互斥锁,这一过程中 thread-error 的值都未被修改,还是保持为 RT_E
21、OK 114. 115. /* the mutex is taken successfully. */ 116. /* disable interrupt */ 117. temp = rt_hw_interrupt_disable();/关中断 118. 119. 120. 121. 122. 123. /* enable interrupt */ 124. rt_hw_interrupt_enable(temp);/开中断 125. 126. RT_OBJECT_HOOK_CALL(rt_object_take_hook, ( 127. 128. return RT_EOK; 129. 关
22、于上述的末尾部分的代码,其实在 thread.c 源文件中的 rt_thread_timeout 函数中有将超时线程的 thread-error = -RT_ETIMEOUT,即当当前线程获取互斥锁的时间已到还是未成功时,当前线程的 thread-error 将在定时器的超时回调函数中设置为-RT_ETIMEOUT,而如果另一线程在释放互斥锁 rt_mutex_release 函数中如果唤醒当前阻塞的线程,在这个过程中thread-error 的值将一直保持原样 RT_EOK.这就是为什么在这里可以通过判断 thread-error 的值来得知当前线程是否已经获得互斥锁了.注:使用互斥量会导致
23、的一个潜在问题就是线程优先级翻转。所谓优先级翻转问题即当一个高优先级线程通过互斥量机制访问共享资源时,该互斥量已被一低优先级线程占有,而这个低优先级线程在运行过程中可能又被其它一些中等优先级的线程抢先,因此造成高优先级线程被许多具有较低优先级的线程阻塞,实时性难以得到保证。例如:有优先级为A、B 和 C 的三个线程,优先级 A B C,线程 A,B 处于挂起状态,等待某一事件的发生,线程 C 正在运行,此时线程 C 开始使用某一共享资源 S。在使用过程中,线程 A 等待的事件到来,线程 A 转为就绪态,因为它比线程 C 优先级高,所以立即执行。但是当线程A 要使用共享资源 S 时,由于其正在被
24、线程 C 使用,因此线程 A 被挂起切换到线程 C 运行。如果此时线程 B 等待的事件到来,则线程 B 转为就绪态。由于线程 B 的优先级比线程C 高,因此线程 B 开始运行,直到其运行完毕,线程 C 才开始运行。只有当线程 C 释放共享资源 S 后,线程 A 才得以执行。在这种情况下,优先级发生了翻转,线程 B 先于线程A 运行。这样便不能保证高优先级线程的响应时间。在 RT-Thread 中实现的是优先级继承算法。优先级继承通过在线程 A 被阻塞期间提升线程C 的优先级到线程 A 的优先级别从而解决优先级翻转引起的问题。这防止了 C(间接地防止 A)被 B 抢占。通俗地说,优先级继承协议使
25、一个拥有资源的线程以等待该资源的线程中优先级最高的线程的优先级执行。当线程释放该资源时,它将返回到它正常的或标准的优先级。因此,继承优先级的线程避免了被任何中间优先级的线程抢占。(参见上面修改互斥锁拥有者线程优先级处的代码部分).5 释放互斥锁cpp view plaincopyprint?1. /* 2. * This function will release a mutex, if there are threads suspended on mutex, 3. * it will be waked up. 4. * 5. * param mutex the mutex object 6
26、. * 7. * return the error code 8. */ 9. rt_err_t rt_mutex_release(rt_mutex_t mutex) 10. 11. register rt_base_t temp; 12. struct rt_thread *thread; 13. rt_bool_t need_schedule; 14. 15. need_schedule = RT_FALSE;/需要重新调试标志初始化为不需要 16. 17. /* get current thread */ 18. thread = rt_thread_self();/获取当前线程 19.
27、 20. /* disable interrupt */ 21. temp = rt_hw_interrupt_disable();/关中断 22. 23. RT_DEBUG_LOG(RT_DEBUG_IPC, 24. (“mutex_release:current thread %s, mutex value: %d, hold: %dn“, 25. thread-name, mutex-value, mutex-hold); 26. 27. RT_OBJECT_HOOK_CALL(rt_object_put_hook, ( 28. 29. /* mutex only can be rele
28、ased by owner */ 30. if (thread != mutex-owner)/如果当前线程非拥有者,则说明有问题 31. 32. thread-error = -RT_ERROR;/设置当前线程有错误 33. 34. /* enable interrupt */ 35. rt_hw_interrupt_enable(temp);/开中断 36. 37. return -RT_ERROR;/返回错误 38. 39. 40. /* decrease hold */ 41. mutex-hold -;/互斥锁的阻塞线程个数减 1 42. /* if no hold */ 43. i
29、f (mutex-hold = 0)/如果互斥锁当前已将全部 take 次数释放 44. 45. /* change the owner thread to original priority */ 46. if (mutex-original_priority != mutex-owner-current_priority)/如果互斥锁的原始优先级不等于拥有者当前的优先级 47. 48. rt_thread_control(mutex-owner,/则需要将拥有者线程的优先级还原为原始优先级 49. RT_THREAD_CTRL_CHANGE_PRIORITY, 50. 51. 52. 53
30、. /* wakeup suspended thread */ 54. if (!rt_list_isempty( 60. 61. RT_DEBUG_LOG(RT_DEBUG_IPC, (“mutex_release: resume thread: %sn“, 62. thread-name); 63. 64. /* set new owner and priority */ 65. mutex-owner = thread;/设置互斥锁的拥有者为挂起的线程 66. mutex-original_priority = thread-current_priority;/设置互斥锁的原始优先级为此
31、挂起线程的优先级 67. mutex-hold +;/互斥锁的 take 次数加 1(由于被唤醒的线程即将获取此互斥锁) 68. 69. /* resume thread */ 70. rt_ipc_list_resume(/唤醒此线程 71. 72. need_schedule = RT_TRUE;/设置需要调度 73. 74. else/如果挂起线程链表为空 75. 76. /* increase value */ 77. mutex-value +;/互斥锁的值加 1 78. 79. /* clear owner */ 80. mutex-owner = RT_NULL;/互斥锁的拥有者
32、为空 81. mutex-original_priority = 0xff;/互斥锁的原始优先级设为 255 82. 83. 84. 85. /* enable interrupt */ 86. rt_hw_interrupt_enable(temp);/开中断 87. 88. /* perform a schedule */ 89. if (need_schedule = RT_TRUE)/如需要调度则调度线程 90. rt_schedule(); 91. 92. return RT_EOK; 93. 只有在确保互斥锁再没有线程 take 时,即 hold=0 时,才去唤醒阻塞在此互斥锁上的
33、第一个线程,并将此互斥锁的拥有者线程设置为此被唤醒的线程 ,并记录唤醒线程的优先级 ,因为唤醒线程即将成功拥有互斥锁,所以互斥锁的 hold 值加 1.6 互斥锁控制cpp view plaincopyprint?1. /* 2. * This function can get or set some extra attributions of a mutex object. 3. * 4. * param mutex the mutex object 5. * param cmd the execution command 6. * param arg the execution argument 7. * 8. * return the error code 9. */ 10.rt_err_t rt_mutex_control(rt_mutex_t mutex, rt_uint8_t cmd, void *arg) 11. 12. return -RT_ERROR;/互斥锁目前暂不能控制 13.