LDD3源码分析之简单休眠

本博客转载于:http://blog.csdn.net/liuhaoyutz/article/details/7388163  [作者:刘昊昱 ]


本文分析LDD3章中关于简单休眠的示例代码sleepy.c

首先列出sleepy.c的完整代码:

  1.  1/*  
  2.  2 * sleepy.c -- the writers awake the readers  
  3.  3 *  
  4.  4 * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet  
  5.  5 * Copyright (C) 2001 O'Reilly & Associates  
  6.  6 *  
  7.  7 * The source code in this file can be freely used, adapted,  
  8.  8 * and redistributed in source or binary form, so long as an  
  9.  9 * acknowledgment appears in derived source files.  The citation  
  10. 10 * should list that the code comes from the book "Linux Device  
  11. 11 * Drivers" by Alessandro Rubini and Jonathan Corbet, published  
  12. 12 * by O'Reilly & Associates.   No warranty is attached;  
  13. 13 * we cannot take responsibility for errors or fitness for use.  
  14. 14 *  
  15. 15 * $Id: sleepy.c,v 1.7 2004/09/26 07:02:43 gregkh Exp $  
  16. 16 */  
  17. 17  
  18. 18#include <linux/module.h>  
  19. 19#include <linux/init.h>  
  20. 20  
  21. 21#include <linux/sched.h>  /* current and everything */  
  22. 22#include <linux/kernel.h> /* printk() */  
  23. 23#include <linux/fs.h>     /* everything... */  
  24. 24#include <linux/types.h>  /* size_t */  
  25. 25#include <linux/wait.h>  
  26. 26  
  27. 27MODULE_LICENSE("Dual BSD/GPL");  
  28. 28  
  29. 29static int sleepy_major = 0;  
  30. 30  
  31. 31static DECLARE_WAIT_QUEUE_HEAD(wq);  
  32. 32static int flag = 0;  
  33. 33  
  34. 34ssize_t sleepy_read (struct file *filp, char __user *buf, size_t count, loff_t *pos)  
  35. 35{  
  36. 36    printk(KERN_DEBUG "process %i (%s) going to sleep\n",  
  37. 37            current->pid, current->comm);  
  38. 38    wait_event_interruptible(wq, flag != 0);  
  39. 39    flag = 0;  
  40. 40    printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);  
  41. 41    return 0; /* EOF */  
  42. 42}  
  43. 43  
  44. 44ssize_t sleepy_write (struct file *filp, const char __user *buf, size_t count,  
  45. 45        loff_t *pos)  
  46. 46{  
  47. 47    printk(KERN_DEBUG "process %i (%s) awakening the readers...\n",  
  48. 48            current->pid, current->comm);  
  49. 49    flag = 1;  
  50. 50    wake_up_interruptible(&wq);  
  51. 51    return count; /* succeed, to avoid retrial */  
  52. 52}  
  53. 53  
  54. 54  
  55. 55struct file_operations sleepy_fops = {  
  56. 56    .owner = THIS_MODULE,  
  57. 57    .read =  sleepy_read,  
  58. 58    .write = sleepy_write,  
  59. 59};  
  60. 60  
  61. 61  
  62. 62int sleepy_init(void)  
  63. 63{  
  64. 64    int result;  
  65. 65  
  66. 66    /*  
  67. 67     * Register your major, and accept a dynamic number  
  68. 68     */  
  69. 69    result = register_chrdev(sleepy_major, "sleepy", &sleepy_fops);  
  70. 70    if (result < 0)  
  71. 71        return result;  
  72. 72    if (sleepy_major == 0)  
  73. 73        sleepy_major = result; /* dynamic */  
  74. 74    return 0;  
  75. 75}  
  76. 76  
  77. 77void sleepy_cleanup(void)  
  78. 78{  
  79. 79    unregister_chrdev(sleepy_major, "sleepy");  
  80. 80}  
  81. 81  
  82. 82module_init(sleepy_init);  
  83. 83module_exit(sleepy_cleanup);  

在模块初始化函数中,注册字符设备”sleepy”时,指定了该设备的读写函数分别是sleepy_readsleepy_write。当某个进程对sleepy执行读操作时,会进入休眠。当某个进程对sleepy执行写操作时,会唤醒相应等待队列中的所有休眠进程。

为了管理休眠进程,需要建立等待队列,等待队列就是一个进程链表,其中包含等待某个特定事件的所有进程。等待队列通过“等待队列头”来管理,等待队列头是一个类型为wait_queue_head_t的结构体。可以静态初始化一个等待队列头:

DECLARE_WAIT_QUEUE_HEAD(name);

也可以动态初始化一个等待队列头:

wait_queue_head_t my_queue;

init_waitqueue_head(&my_queue);

一个进程要进入休眠,最常用的函数是:

wait_event_interruptible(queue, condition);

queue是等待队列头,condition是一个条件表达式,进程进入休眠前和被唤醒后,都会检查condition的值是否为真,如果不为真,则进程会进入休眠。

对应wait_event_interruptible的唤醒函数是:

wake_up_interruptible(wait_queue_head_t *queue)

sleepy.c31行定义了等待队列头wq

  1. 31static DECLARE_WAIT_QUEUE_HEAD(wq);  


sleepy_read函数中,38行调用wait_event_interruptible(wq, flag != 0)进入休眠。所以只要有进程对sleepy执行读操作,就会进入休眠。

sleepy_write函数中,49行将flag设置为1,然后调用wake_up_interruptible(&wq)将等待在wq上的进程唤醒。

注意,因为在sleepy_read函数中,休眠进程被唤醒后,会把flag重新设置为0,所以虽然全部休眠进程都会被唤醒,但一次只有一个进程能真正继续执行,其它进程会重新休眠。但是为简单起见,这里没考虑并发处理等问题。

要测试sleepy模块,我们先创建sleepy_loadsleepy_unload脚本。

sleepy_load脚本的内容如下:

  1. #!/bin/sh  
  2. # $Id: complete_load,v 1.4 2004/11/03 06:19:49 rubini Exp $  
  3. module="sleepy"  
  4. device="sleepy"  
  5. mode="666"  
  6.   
  7. # Group: since distributions do it differently, look for wheel or use staff  
  8. if grep -q '^staff:' /etc/group; then  
  9.     group="staff"  
  10. else  
  11.     group="wheel"  
  12. fi  
  13.   
  14. # invoke insmod with all arguments we got  
  15. # and use a pathname, as insmod doesn't look in . by default  
  16. /sbin/insmod ./$module.ko $* || exit 1  
  17.   
  18. # retrieve major number  
  19. major=$(awk "\$2==\"$module\" {print \$1}" /proc/devices)  
  20.   
  21. # Remove stale nodes and replace them, then give gid and perms  
  22. # Usually the script is shorter, it's scull that has several devices in it.  
  23.   
  24. rm -f /dev/${device}  
  25. mknod /dev/${device} c $major 0  
  26.   
  27. chgrp $group /dev/${device}   
  28. chmod $mode  /dev/${device}  

sleepy_unload脚本的内容如下:

  1. #!/bin/sh  
  2. module="sleepy"  
  3. device="sleepy"  
  4.   
  5. # invoke rmmod with all arguments we got  
  6. /sbin/rmmod $module $* || exit 1  
  7.   
  8. # Remove stale nodes  
  9.   
  10. rm -f /dev/${device}  

sleepy模块的测试过程如下图所示:


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值