rtmutex 的死锁检测

 以为linux里死锁检测就只有rtmutex里用到一点。先个出rtmutex被阻塞的一个场景,这里直接给出简化后的代码:

[cpp]  view plain copy
  1. int rtmutex_lock(struct rt_mutex *lock)  
  2. {  
  3.     if (rt_mutex_cmpxchg(lock, NULL, current)) {  
  4.         return 0;  
  5.     } else  
  6.         return rt_mutex_slowlock(lock, state, NULL, detect_deadlock);  
  7. }  
  8.   
  9. # define rt_mutex_cmpxchg(l,c,n)    (cmpxchg(&l->owner, c, n) == c)  
  10. unsigned long rt_mutex_cmpxchg(struct rt_mutex *lock,unsigned long old, unsigned long new)  
  11. {  
  12.     unsigned long retval;  
  13.     retval = lock->owner;  
  14.     if (retval == old){  
  15.         *lock->owner = new;  
  16.             return 1;  
  17.         }else  
  18.         return 0;  
  19. }  
  20.   
  21.   
  22. static int __sched  
  23. __rt_mutex_slowlock(struct rt_mutex *lock, int state,  
  24.             struct hrtimer_sleeper *timeout,  
  25.             struct rt_mutex_waiter *waiter,  
  26.             int detect_deadlock, unsigned long flags)  
  27. {  
  28.    for (;;) {  
  29.        if (try_to_take_rt_mutex(lock))  
  30.             break;  
  31.        if (!waiter->task) {  
  32.         ret = task_blocks_on_rt_mutex(lock, waiter, current,  
  33.                               detect_deadlock, flags);  
  34.    }  
  35.   
  36.    }  
  37.    
  38. }  
真正的阻塞函数是task_blocks_on_rt_mutex,这个函数就是把当前进程阻塞到资源lock上,

如果有必要还要调整lock持有者的优先级。调整了lock持有者的优先级,还要看lock持有者是否

 被另外一把锁lock2阻塞,如果是的话,还要调整lock2持有者的优先级,依次类推,原则就是

让锁持有者尽快完成任务,就可以释放资源了。

[cpp]  view plain copy
  1. static int task_blocks_on_rt_mutex(struct rt_mutex *lock,  
  2.                    struct rt_mutex_waiter *waiter,  
  3.                    struct task_struct *task,  
  4.                    int detect_deadlock, unsigned long flags)  
  5. {  
  6.         struct task_struct* owner = rtmutex_owner(lock);  
  7.     struct rt_mutex_waiter waiter;  
  8.     struct rt_mutex_waiter *top_waiter   
  9.         = rt_mutex_top_waiter(lock);  
  10.         waiter.task = current;  
  11.     waiter.lock = lock;  
  12.     plist_add(&waiter.list_entry,&lock->wait_list);  
  13.         if(waiter == rt_mutex_top_waiter(lock)){  
  14.         plist_del(&top_waiter->pi_list_entry, &owner->pi_waiters);  
  15.         plist_add(&waiter->pi_list_entry, &owner->pi_waiters);  
  16.         owner->prio = min(task_top_pi_waiter(owner)->pi_list_entry.prio,  
  17.            task->normal_prio);  
  18.           
  19.         }  
  20.     rt_mutex_adjust_prio_chain(owner);  
  21. }  
连环提升优先级:

[cpp]  view plain copy
  1. static int rt_mutex_adjust_prio_chain(struct task_struct *task)  
  2. {  
  3.     if(!task->state) // running  
  4.         return 0;  
  5.   
  6.     struct rt_mutex *new_lock = task->pi_blocked_on.lock;  
  7.     struct rt_mutex_waiter *waiter = task->waiter;  
  8.       
  9.     struct rt_mutex_waiter *old_lock_waiter = rt_mutex_top_waiter(new_lock);  
  10.     struct rt_mutex_waiter *old_task_resource;  
  11.   
  12.     //因为task的优先级变化了,我们需要将他在lock的wait_list(排序)的位置移动一下  
  13.     plist_del(&waiter.list_entry,&new_lock->wait_list);  
  14.     plist_add(&waiter.list_entry,&new_lock->wait_list);  
  15.       
  16.     //1) 如果这个task由于提升优先级,成为了new_lock的最高优先级等待者,或者,  
  17.     //2) 如果这个task曾经是new_lock最高优先级等待者,由于优先级降低,他不再是new_lock的最高等待者  
  18.     //3) 那么都需要调整一下new_lock持有者的资源链表的最高优先级等待者集合的顺序  
  19.   
  20.     struct task_struct* new_owner = rtmutex_owner(new_lock);  
  21.     old_task_resource = task_top_pi_waiter(new_owner);  
  22.   
  23.     if(waiter == rt_mutex_top_waiter(lock)){ //情况1  
  24.           
  25.         //对new_lock持有者的资源链表重新排序  
  26.       
  27.         plist_del(&waiter->pi_list_entry, &new_owner->pi_waiters);  
  28.         plist_add(&waiter->pi_list_entry, &new_owner->pi_waiters);  
  29.       
  30.         //如果排序后,这个task等待的锁成为了new_lock持有者new_owner的最高优先级资源  
  31.         //(不管以前它是不是new_owner的最高资源),  
  32.         //则需要修改new_lock持有者的优先级。由于修改了优先级,需要继续往下传递。  
  33.         if(waiter == task_top_pi_waiter(new_owner)) {  
  34.             new_owner->prio = waiter->prio;  
  35.             return rt_mutex_adjust_prio_chain(new_owner);  
  36.         }  
  37.         }else if(waiter == old_lock_waiter){ //情况2,曾经是lock的最高等待者,现在退位了  
  38.         //对new_lock持有者的资源链表重新排序,  
  39.         //删除以前的lock最高等待者,把目前lock的最高等待者加入链表  
  40.   
  41.         plist_del(&waiter->pi_list_entry, &new_owner->pi_waiters);  
  42.         plist_add(&rt_mutex_top_waiter(lock)->pi_list_entry, &new_owner->pi_waiters);  
  43.           
  44.         //如果以前task等待的那把锁,是new_owner的最高资源,  
  45.         //则这次无论如何new_owner的优先级都要降低了  
  46.         //将其优先级降低到目前owner持有的资源里最高者,  
  47.         //然后往下传递  
  48.         if(waiter == old_task_resource) {  
  49.             new_owner->prio = task_top_pi_waiter(new_owner)->pi_list_entry.prio;  
  50.             return rt_mutex_adjust_prio_chain(new_owner);  
  51.         }  
  52.         }  
  53.     return 0;  
  54.   
  55. }  
从上面的流程可以大体看出,死锁环成立的条件,

情形一,死锁阻塞链的第二个元素,和最后一个元素,都被同一个锁阻塞 (lock == orig_lock) 
情形二,首尾相连的环,即最后一把锁的持有者是第一个被阻塞的进程(rt_mutex_owner(lockn) == top_task)
是否还少了一种检测,是链表中间第n个到第n+x个形成死锁环
其实,如果是中间形成死锁,那么在之前他死锁时就应该被检测出来。

学过操作系统的应该知道,有多种死锁检测机制,比如银行家算法等,这点linux可以进一步优化。

下面分析。

未完待续

文章来自于:

http://blog.csdn.net/chenyu105/article/details/9748321


linux内核的优先级继承协议(pip)和μC/OSII 2.52内核的优先级置顶协议(pcp)

http://totoxian.iteye.com/blog/1220216

你可能感兴趣的:(rtmutex 的死锁检测)