Linux内核实际项目中的死锁

描述

实际项目中的死锁

下面的例子要复杂一些,这是从实际项目中抽取出来的死锁,更具有代表性。

#include < linux/init.h >
#include < linux/module.h >
#include < linux/kernel.h >
#include < linux/kthread.h >
#include < linux/freezer.h >
#include < linux/delay.h >


static DEFINE_MUTEX(mutex_a);
static struct delayed_work delay_task;
static void lockdep_timefunc(unsigned long);
static DEFINE_TIMER(lockdep_timer, lockdep_timefunc, 0, 0);

static void lockdep_timefunc(unsigned long dummy)
{
    schedule_delayed_work(&delay_task, 10);
    mod_timer(&lockdep_timer, jiffies + msecs_to_jiffies(100));
}

static void lockdep_test_work(struct work_struct *work)
{
    mutex_lock(&mutex_a);
    mdelay(300);//处理一些事情,这里用mdelay替代
    mutex_unlock(&mutex_a);
}

static int lockdep_thread(void *nothing)
{
    set_freezable();//清除当前线程标志flags中的PF_NOFREEZE位,表示当前线程能进入挂起或休眠状态。
    set_user_nice(current, 0);
    while(!kthread_should_stop()){
        mdelay(500);//处理一些事情,这里用mdelay替代

        //遇到某些特殊情况,需要取消delay_task
        mutex_lock(&mutex_a);
        cancel_delayed_work_sync(&delay_task);
        mutex_unlock(&mutex_a);
    }

    return 0;
}


static int __init lockdep_test_init(void)
{
    printk("figo:my lockdep module initn");
    
   struct task_struct *lock_thread;

   /*创建一个线程来处理某些事情*/
   lock_thread = kthread_run(lockdep_thread, NULL, "lockdep_test");

   /*创建一个延迟的工作队列*/
   INIT_DELAYED_WORK(&delay_task, lockdep_test_work);

   /*创建一个定时器来模拟某些异步事件,如中断等*/
   lockdep_timer.expires = jiffies + msecs_to_jiffies(500);
   add_timer(&lockdep_timer);
 
    return 0;
}


static void __exit lockdep_test_exit(void)
{
  printk("goodbyen");
}

MODULE_LICENSE("GPL");
module_init(lockdep_test_init);
module_exit(lockdep_test_exit);

首先创建一个lockdep_thread内核线程,用于周期性地处理某些事情,然后创建一个名为lockdep_test_worker的工作队列来处理一些类似于中断下半部的延迟操作,最后使用一个定时器来模拟某些异步事件(如中断)。

在lockdep_thread内核线程中,某些特殊情况下常常需要取消工作队列。代码中首先申请了一个mutex_a互斥锁,然后调用cancel_delayed_work_sync()函数取消工作队列。另外,定时器定时地调度工作队列,并在回调函数lockdep_test_worker()函数中申请mutex_a互斥锁。

接下来的函数调用栈显示上述尝试获取mutex_a锁的调用路径。两个路径如下:

(1)内核线程lockdep_thread首先成功获取了mutex_a互斥锁,然后调用cancel_delayed_work_sync()函数取消kworker。注意,cancel_delayed_work_sync()函数会调用flush操作并等待所有的kworker回调函数执行完,然后才会调用mutex_unlock(&mutex_a)释放该锁。

内核

(2)kworker回调函数lockdep_test_worker()首先会尝试获取mutex_a互斥锁。 注意,刚才内核线程lockdep_thread已经获取了mutex_a互斥锁,并且一直在等待当前kworker回调函数执行完,所以死锁发生了

内核

下面是该死锁场景的CPU调用关系:

CPU0                      CPU1
----------------------------------------------------------------
内核线程lockdep_thread
lock(mutex_a)
   cancel_delayed_work_sync()
等待worker执行完成     
                                  delay worker回调函数
                                  lock(mutex_a);尝试获取锁
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分