Chinaunix首页 | 论坛 | 博客
  • 博客访问: 221952
  • 博文数量: 34
  • 博客积分: 741
  • 博客等级: 上士
  • 技术积分: 606
  • 用 户 组: 普通用户
  • 注册时间: 2011-04-08 09:54
文章分类

全部博文(34)

文章存档

2016年(1)

2015年(1)

2014年(1)

2013年(1)

2012年(30)

分类: LINUX

2012-07-15 19:51:32

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

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

  1. #include <linux/module.h>
  2. #include <linux/init.h>

  3. #include <linux/sched.h> /* current and everything */
  4. #include <linux/kernel.h> /* printk() */
  5. #include <linux/fs.h> /* everything... */
  6. #include <linux/types.h> /* size_t */
  7. #include <linux/wait.h>

  8. MODULE_LICENSE("Dual BSD/GPL");

  9. static int sleepy_major = 0;

  10. static DECLARE_WAIT_QUEUE_HEAD(wq);
  11. static int flag = 0;

  12. ssize_t sleepy_read (struct file *filp, char __user *buf, size_t count, loff_t *pos)
  13. {
  14.     printk(KERN_DEBUG "process %i (%s) going to sleep\n",
  15.             current->pid, current->comm);
  16.     wait_event_interruptible(wq, flag != 0);
  17.     flag = 0;
  18.     printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);
  19.     return 0; /* EOF */
  20. }

  21. ssize_t sleepy_write (struct file *filp, const char __user *buf, size_t count,
  22.         loff_t *pos)
  23. {
  24.     printk(KERN_DEBUG "process %i (%s) awakening the readers...\n",
  25.             current->pid, current->comm);
  26.     flag = 1;
  27.     wake_up_interruptible(&wq);
  28.     return count; /* succeed, to avoid retrial */
  29. }


  30. struct file_operations sleepy_fops = {
  31.     .owner = THIS_MODULE,
  32.     .read = sleepy_read,
  33.     .write = sleepy_write,
  34. };


  35. int sleepy_init(void)
  36. {
  37.     int result;

  38.     /*
  39.      * Register your major, and accept a dynamic number
  40.      */
  41.     result = register_chrdev(sleepy_major, "sleepy", &sleepy_fops);
  42.     if (result < 0)
  43.         return result;
  44.     if (sleepy_major == 0)
  45.         sleepy_major = result; /* dynamic */
  46.     return 0;
  47. }

  48. void sleepy_cleanup(void)
  49. {
  50.     unregister_chrdev(sleepy_major, "sleepy");
  51. }

  52. module_init(sleepy_init);
  53. module_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.c14行定义了等待队列头wq

  1. 14  static DECLARE_WAIT_QUEUE_HEAD(wq);

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

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

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

另外还可以调用带计时功能的版本:

  1. ssize_t sleepy_read (struct file *filp, char __user *buf, size_t count, loff_t *pos)
  2. {
  3.     printk(KERN_DEBUG "process %i (%s) going to sleep\n",
  4.             current->pid, current->comm);
  5.     //wait_event_interruptible(wq, flag != 0);
  6.     wait_event_interruptible_timeout(wq, flag!=0, 3*HZ);
  7.     flag = 0;
  8.     printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);
  9.     return 0; /* EOF */
  10. }

  11. ssize_t sleepy_write (struct file *filp, const char __user *buf, size_t count,
  12.         loff_t *pos)
  13. {
  14.     printk(KERN_DEBUG "process %i (%s) awakening the readers...\n",
  15.             current->pid, current->comm);
  16.     flag = 1;
  17.     wake_up_interruptible(&wq);
  18.     return count; /* succeed, to avoid retrial */
  19. }

要测试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}
本文参考: http://blog.csdn.net/liuhaoyutz/article/details/7388163
阅读(944) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~