具有等待队列挂起系统的Linux驱动程序代码

我编写了一个示例linux设备驱动程序代码,该代码将创建两个内核线程,并且每个线程都会增加一个全局变量。我已经使用等待队列来执行增加变量的任务,并且每个线程将在等待队列上等待,直到计时器到期并且每个线程被随机唤醒。

但是问题是,当我插入此模块时,整个系统刚刚冻结,必须重新启动计算机。每次插入模块时都会发生这种情况。我尝试调试kthread代码,以查看是否错误进入了死锁情况,但是我无法找出该代码有什么问题。

谁能告诉我我在代码中做错了什么以使挂断情况发生?

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/errno.h>
    #include <linux/semaphore.h>
    #include <linux/wait.h>
    #include <linux/timer.h>
    #include <linux/sched.h>
    #include <linux/kthread.h>

    spinlock_t my_si_lock;
    pid_t kthread_pid1;
    pid_t kthread_pid2 ;
    static DECLARE_WAIT_QUEUE_HEAD(wqueue);
    static struct timer_list my_timer;
    int kthread_num;

    /* the timer callback */   
    void my_timer_callback( unsigned long data ){   
    printk(KERN_INFO "my_timer_callback called (%ld).\n", jiffies );
        if (waitqueue_active(&wqueue)) {
                wake_up_interruptible(&wqueue);
        }
    }

    /*Routine for the first thread */
    static int kthread_routine_1(void *kthread_num)
    {
        //int num=(int)(*(int*)kthread_num);
        int *num=(int *)kthread_num;
        char kthread_name[15];
        unsigned long flags;
        DECLARE_WAITQUEUE(wait, current);

        printk(KERN_INFO "Inside daemon_routine() %ld\n",current->pid);

        allow_signal(SIGKILL);
        allow_signal(SIGTERM);

        do{
                set_current_state(TASK_INTERRUPTIBLE);
                add_wait_queue(&wqueue, &wait);

                spin_lock_irqsave(&my_si_lock, flags);
                printk(KERN_INFO "kernel_daemon [%d] incrementing the shared data=%d\n",current->pid,(*num)++);
                spin_unlock_irqrestore(&my_si_lock, flags);

                remove_wait_queue(&wqueue, &wait);

                if (kthread_should_stop()) {
                        break;
                }

        }while(!signal_pending(current));

        set_current_state(TASK_RUNNING);
        return 0;
    }

    /*Routine for the second thread */
    static int kthread_routine_2(void *kthread_num)
    {
        //int num=(int)(*(int*)kthread_num);
        int *num=(int *)kthread_num;
        char kthread_name[15];
        unsigned long flags;
        DECLARE_WAITQUEUE(wait, current);

        printk(KERN_INFO "Inside daemon_routine() %ld\n",current->pid);

        allow_signal(SIGKILL);
        allow_signal(SIGTERM);

        do{
                set_current_state(TASK_INTERRUPTIBLE);
                add_wait_queue(&wqueue, &wait);

                spin_lock_irqsave(&my_si_lock, flags);
                printk(KERN_INFO "kernel_daemon [%d] incrementing the shared data=%d\n",current->pid,(*num)++);
                spin_unlock_irqrestore(&my_si_lock, flags);

                remove_wait_queue(&wqueue, &wait);

                if (kthread_should_stop()) {
                        break;
                }

        }while(!signal_pending(current));

        set_current_state(TASK_RUNNING);
        return 0;
    }

    static int __init signalexample_module_init(void)
    {
        int ret;

        spin_lock_init(&my_si_lock);
        init_waitqueue_head(&wqueue);
        kthread_num=1;

        printk(KERN_INFO "starting the first kernel thread with id ");
        kthread_pid1 = kthread_run(kthread_routine_1,&kthread_num,"first_kthread");
        printk(KERN_INFO "%ld \n",(long)kthread_pid1);
        if(kthread_pid1< 0 ){
                printk(KERN_ALERT "Kernel thread [1] creation failed\n");
                return -1;
        }

        printk(KERN_INFO "starting the second kernel thread with id");
        kthread_pid2 = kthread_run(kthread_routine_2,&kthread_num,"second_kthread");
        printk(KERN_INFO "%ld \n",(long)kthread_pid2);
        if(kthread_pid2 < 0 ){
                printk(KERN_ALERT "Kernel thread [2] creation failed\n");
                return -1;
        }

        setup_timer( &my_timer, my_timer_callback, 0 );

        ret = mod_timer( &my_timer, jiffies + msecs_to_jiffies(2000) );
        if (ret) {
                printk("Error in mod_timer\n");
                return -EINVAL;
        }
        return 0;
    }

    static void __exit signalexample_module_exit(void)
    {
        del_timer(&my_timer);
    }

module_init(signalexample_module_init);
module_exit(signalexample_module_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Demonstrates use of kthread");
Skrrgwasme

您需要schedule()在两个线程函数中都调用

/* In kernel thread function... */

set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&wqueue, &wait);

schedule(); /* Add this call here */

spin_lock_irqsave(&my_si_lock, flags);
/* etc... */

调用set_current_state(TASK_INTERRUPTIBLE)将设置当前进程的任务结构中的状态,这使调度程序可以在睡眠后将其移出运行队列。但是然后您必须告诉调度程序,“好吧,我设置了一个新状态。现在重新调度我。” 您错过了第二步,因此更改的标志要等到调度程序下一次决定挂起线程时才生效,并且无法知道将在多长时间后发生,或者何时执行代码的哪一行它会发生(锁定代码除外-不应被中断)。

我不太确定为什么会导致整个系统锁定,因为您的系统状态非常不可预测。由于内核线程在获取锁和循环之前没有等待计时器到期,因此我不知道何时可以期望调度程序实际上对新的任务结构状态采取措施,并且可能在与此同时。您的线程反复调用add_wait_queue(&wqueue, &wait);remove_wait_queue(&wqueue, &wait);,所以谁知道您的计时器回调触发时等待队列处于什么状态。实际上,由于内核线程正在旋转,因此此代码具有竞争条件:

if (waitqueue_active(&wqueue)) {
    wake_up_interruptible(&wqueue);
}

如果执行if语句,则可能在等待队列上有活动任务,只是在调用时将其清空wake_up_interruptible(&wqueue);

顺便说一句,我假设您当前增加全局变量的目标只是学习等待队列和睡眠状态的一种练习。如果您想真正实现共享计数器,请查看原子操作,然后便可以转储自旋锁。

如果决定保留自旋锁,则应改为使用DEFINE_SPINLOCK()宏。

另外,正如我在评论中提到的那样,您应该将两个kthread_pid变量更改task_struct *type。您还需要kthread_stop(kthread_pid);在__exit例程中对启动的每个线程进行调用kthread_should_stop()如果您从未告诉他们停止,它将永远不会返回true。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

是否可以从Linux内核中的驱动程序代码调用设备层代码

来自分类Dev

Linux设备驱动程序代码中的MODULE_ALIAS是什么?

来自分类Dev

此Linux设备驱动程序代码中是否需要自旋锁?

来自分类Dev

如何为现有的Windows驱动程序代码编写SOURCES文件?

来自分类Dev

谁在驱动程序代码中调用“探针”功能?

来自分类Dev

谁在驱动程序代码中调用“探针”功能?

来自分类Dev

编译简单的设备驱动程序代码

来自分类Dev

为什么内核使用默认的块驱动程序而不是我的驱动程序代码?

来自分类Dev

将x86驱动程序代码放入环1和2而不是0是否有好处?

来自分类Dev

是否有任何工具可以为我们提供网站上的网络驱动程序代码

来自分类Dev

Linux是否具有USB端口的设备驱动程序?

来自分类Dev

找不到适合我的Java应用程序代码的驱动程序

来自分类Dev

内核模块中的驱动程序代码无法执行?

来自分类Dev

如何检查驱动程序代码签名策略是否已启用并实际起作用?

来自分类Dev

在驱动程序代码内打印指针时,%llx和%p之间的差异

来自分类Dev

我的RGB条纹驱动程序代码中出现意外行为

来自分类Dev

如何从Windows驱动程序代码中区分devmgmt的“禁用”和“卸载”?

来自分类Dev

在NetBeans中具有程序代码的PHPUnit

来自分类Dev

具有异步C ++驱动程序的SQL / NoSQL数据库管理系统

来自分类Dev

Apache Spark使用本机依赖关系-独立模式下的驱动程序/执行程序代码流

来自分类Dev

如何制作多视频驱动程序Linux系统?

来自分类Dev

具有多个设备属性的Linux驱动程序链接到同一功能

来自分类Dev

具有多个设备属性的Linux驱动程序链接到同一功能

来自分类Dev

节点驱动程序挂起并超时

来自分类Dev

在多体系结构的64位Debian系统上具有nVidia OpenGL 32位驱动程序

来自分类Dev

Linux驱动程序和Android驱动程序有什么区别

来自分类Dev

具有Bluebird 2.9。*的Mongodb节点驱动程序2.0。*

来自分类Dev

具有依赖项的Inno Setup驱动程序

来自分类Dev

具有Mapbox Optimization API的多个驱动程序?

Related 相关文章

  1. 1

    是否可以从Linux内核中的驱动程序代码调用设备层代码

  2. 2

    Linux设备驱动程序代码中的MODULE_ALIAS是什么?

  3. 3

    此Linux设备驱动程序代码中是否需要自旋锁?

  4. 4

    如何为现有的Windows驱动程序代码编写SOURCES文件?

  5. 5

    谁在驱动程序代码中调用“探针”功能?

  6. 6

    谁在驱动程序代码中调用“探针”功能?

  7. 7

    编译简单的设备驱动程序代码

  8. 8

    为什么内核使用默认的块驱动程序而不是我的驱动程序代码?

  9. 9

    将x86驱动程序代码放入环1和2而不是0是否有好处?

  10. 10

    是否有任何工具可以为我们提供网站上的网络驱动程序代码

  11. 11

    Linux是否具有USB端口的设备驱动程序?

  12. 12

    找不到适合我的Java应用程序代码的驱动程序

  13. 13

    内核模块中的驱动程序代码无法执行?

  14. 14

    如何检查驱动程序代码签名策略是否已启用并实际起作用?

  15. 15

    在驱动程序代码内打印指针时,%llx和%p之间的差异

  16. 16

    我的RGB条纹驱动程序代码中出现意外行为

  17. 17

    如何从Windows驱动程序代码中区分devmgmt的“禁用”和“卸载”?

  18. 18

    在NetBeans中具有程序代码的PHPUnit

  19. 19

    具有异步C ++驱动程序的SQL / NoSQL数据库管理系统

  20. 20

    Apache Spark使用本机依赖关系-独立模式下的驱动程序/执行程序代码流

  21. 21

    如何制作多视频驱动程序Linux系统?

  22. 22

    具有多个设备属性的Linux驱动程序链接到同一功能

  23. 23

    具有多个设备属性的Linux驱动程序链接到同一功能

  24. 24

    节点驱动程序挂起并超时

  25. 25

    在多体系结构的64位Debian系统上具有nVidia OpenGL 32位驱动程序

  26. 26

    Linux驱动程序和Android驱动程序有什么区别

  27. 27

    具有Bluebird 2.9。*的Mongodb节点驱动程序2.0。*

  28. 28

    具有依赖项的Inno Setup驱动程序

  29. 29

    具有Mapbox Optimization API的多个驱动程序?

热门标签

归档