linux模块编程(四)——消息的使者list

来源:互联网 发布:mac capslock灯不亮了 编辑:程序博客网 时间:2024/05/16 07:54

http://blog.csdn.net/qb_2008/article/details/6839899


通过之前几节,我们已经了解了内核线程的创建方法kthread,内核同步的工具completion。现在我们就来学学内核线程传递消息的方法list。或许大家会说,list不是链表吗。不错,list是链表,但它可以变成承担消息传递的消息队列。消息的发送者把消息放到链表上,并通过同步工具(如completion)通知接收线程,接收线程再从链表上取回消息,就这么简单。linux内核或许没有给我们定制好的东西,但却给了我们可随意变换的、基础的工具,把这些工具稍加组合就能完成复杂的功能。list又是这些万能工具中最常用的。

      前面两篇文章的惯例是先对新增的功能做出介绍,并解释要用到的API。但我感觉这种既要解释原理,又要分析代码,又要写应用样例的十全文章,写起来实在吃力,而且漏洞百出。与其如此,我还不如把两部分分开,这里的模块编程就专心设计模块,编写内核API的组合使用样例;而原理介绍、API代码分析的部分,会转到linux内核部件分析的部分。这样我一方面能安心设计样例,一方面能把API介绍地更全面一些。

 

模块设计

       本模块目的是展示list作为消息队列使用时的情况。所以会创建一个全局链表work_list,定义一种消息的结构struct work_event,并创建两个内核线程work_thread和watchdog_thread。work_thread是消息的接收者,它循环检查work_list,如果其上有消息,就将其取出并执行,否则阻塞。watchdog_thread是消息的发送者,它周期性地发送消息到work_list,并唤醒work_thread。

 

模块实现

1、建立list子目录。

2、编写list.c,使其内容如下。

[cpp] view plaincopyprint?
  1. #include <linux/init.h>  
  2. #include <linux/module.h>  
  3. #include <linux/list.h>  
  4. #include <linux/completion.h>  
  5. #include <linux/kthread.h>  
  6.   
  7. MODULE_LICENSE("Dual BSD/GPL");  
  8.   
  9. static struct task_struct *work_tsk;  
  10.   
  11. static struct task_struct *watchdog_tsk;  
  12.   
  13. static DECLARE_SPINLOCK(work_list_lock);  
  14.   
  15. static LIST_HEAD(work_list);  
  16.   
  17. static DECLARE_COMPLETION(work_wait);  
  18.   
  19. enum WORK_EVENT_TYPE {  
  20.     EVENT_TIMER,  
  21.     EVENT_EXIT  
  22. };  
  23.   
  24. struct work_event {  
  25.     enum WORK_EVENT_TYPE type;  
  26.     int need_free;  
  27.     list_head list;  
  28. };  
  29.   
  30. static int work_thread(void *data)  
  31. {  
  32.     int count = 0;  
  33.   
  34.     while(1){  
  35.         if(list_empty(&work_list))  
  36.             wait_for_completion(&work_wait);  
  37.         spin_lock(&work_list_lock);  
  38.         while(!list_empty(&work_list)){  
  39.             struct work_event *event;  
  40.             event = list_entry(work_list.next, struct work_event, list);  
  41.             list_del(&event->list);  
  42.             spin_unlock(&work_list_lock);  
  43.   
  44.             if(event->type == EVENT_TIMER){  
  45.                 printk(KERN_INFO "event timer: count = %d\n", ++count);  
  46.             }  
  47.             else if (event->type == EVENT_EXIT){  
  48.                 if(event->need_free)  
  49.                     kfree(event);  
  50.                 goto exit;  
  51.             }  
  52.               
  53.             if(event->need_free)  
  54.                 kfree(event);  
  55.             spin_lock(&work_list_lock);  
  56.         }  
  57.     }  
  58. exit:  
  59.     return count;  
  60. }  
  61.   
  62. static int watchdog_thread(void *data)  
  63. {  
  64.     int count = 0;  
  65.     while(!kthread_should_stop()){  
  66.         msleep(1000);  
  67.         count++;  
  68.         if(count%5 == 0){  
  69.             struct work_event *event;  
  70.             event = kmalloc(sizeof(struct work_event), GFP_KERNEL);  
  71.             if(event == NULL){  
  72.                 printk(KERN_INFO "watchdog_thread: kmalloc failed!\n");  
  73.                 break;  
  74.             }  
  75.             event->type = EVENT_TIMER;  
  76.             event->need_free = 1;  
  77.             spin_lock(&work_list_lock);  
  78.             list_add_tail(&event->list, &work_list);  
  79.             spin_unlock(&work_list_lock);  
  80.             complete(&work_wait);  
  81.         }  
  82.     }  
  83.     return count;  
  84. }  
  85.   
  86. static int list_init()  
  87. {  
  88.     printk(KERN_INFO "list_init()\n");  
  89.   
  90.     watchdog_tsk = kthread_run(watchdog_thread, NULL, "watchdog_thread");  
  91.     if(IS_ERR(watchdog_tsk))  
  92.         goto err1;  
  93.   
  94.     work_tsk = kthread_run(work_thread, NULL, "work_thread");  
  95.     if(IS_ERR(work_tsk))  
  96.         goto err2;  
  97.       
  98.     return 0;  
  99.   
  100. err2:  
  101.     kthread_stop(watchdog_tsk);  
  102. err1:  
  103.     return 1;  
  104. }  
  105.   
  106. static void list_exit()  
  107. {  
  108.     printk(KERN_INFO "list_exit()\n");  
  109.   
  110.     if(!IS_ERR(watchdog_tsk)){  
  111.         int count = kthread_stop(watchdog_tsk);  
  112.         printk(KERN_INFO "watchdog_thread: running for %ss\n", count);  
  113.     }  
  114.     if(!IS_ERR(work_tsk)){  
  115.         get_task_struct(&work_tsk);  
  116.         struct work_event event;  
  117.         event.type = EVENT_EXIT;  
  118.         event.need_free = 0;  
  119.         spin_lock(&work_list_lock);  
  120.         list_add(&event.list, &work_list);  
  121.         spin_unlock(&work_list_lock);  
  122.         complete(&work_wait);  
  123.           
  124.         int count = kthread_stop(work_tsk);  
  125.         printk(KERN_INFO "work_thread: period 5s, running %d times\n", count);  
  126.     }  
  127. }  
  128.   
  129. module_init(list_init);  
  130. module_exit(list_exit);  


       整个模块较为简单,work_thread只是接收work_list中的消息并处理,所以在list_exit退出时也要给它发EVENT_EXIT类型的消息,使其退出。至于在list_exit发消息之前调用的get_task_struct,实在是无奈之举。因为我们发送EVENT_EXIT消息后work_thread会在kthread_stop调用前就马上结束,导致之后的kthread_stop错误。所以要先get_task_struct防止work_thread退出后释放任务结构中的某些内容,虽然有对应的put_task_struct,但我们并未使用,因为put_task_struct并未引出到符号表。当然,这种情况只是一时之举,等我们学习了更强大的线程同步机制,或者更灵活的线程管理方法,就能把代码改得更流畅。

      注意到代码中使用spin_lock/spin_unlock来保护work_list。如果之前不了解spin_lock,很容易认为它不足以保护。实际上spin_lock不止是使用自旋锁,在此之前还调用了preempt_disable来禁止本cpu任务调度。只要同步行为只发生在线程之间,spin_lock就足以保护,如果有中断或者软中断参与进来,就需要用spin_lock_irqsave了。

 

3、 编译运行模块。

 

       可能本节介绍的list显得过于质朴,但只有简单的东西才能长久保留。不久之后,我们或许不再用kthread,不再用completion,但list一定会一直用下去。

 

 

附录

       因为内核API介绍被移到了独立的文章中,所以这里的附录终于名副其实。可能一节中会用到不只一个部件分析的内容。我会把文中主要想使用的部件对应的文章放在首位,其余参考文章,如果有的话,再在下面一一列出。

      linux内核部件分析(一)连通世界的list