Chinaunix首页 | 论坛 | 博客
  • 博客访问: 631139
  • 博文数量: 1008
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 5175
  • 用 户 组: 普通用户
  • 注册时间: 2012-07-31 09:44
文章分类
文章存档

2012年(1008)

我的朋友

分类:

2012-08-01 11:28:27

原文地址:定时器例程 作者:luozhiyong131

  1. /* 文件名:kgrunt.c */
  2. /* 说明:定时器例程 */

  3. #include <linux/module.h> /* 模块编程 */
  4. #include <linux/fs.h> /* 设备号、文件操作接口 */
  5. #include <linux/cdev.h> /* 字符设备编程接口 */
  6. #include <linux/uaccess.h> /* 访问用户态内存接口 */
  7. #include <linux/timer.h> /* 定时器接口 */
  8. #include <linux/sched.h> /* 进程调度相关 */
  9. #include <linux/wait.h> /* 等待队列接口 */

  10. MODULE_LICENSE("GPL"); /* 版权声明 */

  11. #define BUF_SIZE 256 /* 内核缓冲区的大小 */

  12. /* 以下类型表示 kgrunt 设备 */
  13. struct kgrunt_dev {
  14.     struct cdev cdev; /* 内嵌一个字符设备作为代表向内核注册 */
  15.     char *buf; /* 保存内核缓冲区的首地址 */
  16.     int len; /* 保存内核缓冲区的长度 */
  17.     int bytes; /* 保存内核缓冲区中字符的个数 */
  18.     struct timer_list timer; /* 定时器 */
  19.     int timer_flag; /* 控制是否继续注册定时器 */
  20.     wait_queue_head_t queue; /* 读操作等待队列 */
  21.     dev_t dev; /* 保存对应的设备号 */
  22. };

  23. static void kgrunt_timer_fn(unsigned long d)
  24. {
  25.     /* 从参数得到 kgrunt 设备的地址 */
  26.     struct kgrunt_dev *dev = (struct kgrunt_dev *)d;
  27.     /* 得到定时器变量的地址 */
  28.     struct timer_list *timer = &dev->timer;
  29.     /* 输出字符串到内核缓冲区 */
  30.     dev->bytes = scnprintf(dev->buf, dev->len,
  31.             "timer expires: %lu, jiffies: %lu, current pid: %d, comm: %s.\n",
  32.             timer->expires, jiffies, current->pid, current->comm);
  33.     /* 唤醒等待读的进程 */
  34.     wake_up_interruptible(&dev->queue);
  35.     /* 如果需要继续注册定时器,则继续注册,过期时间推后一秒 */
  36.     if (dev->timer_flag) mod_timer(timer, timer->expires+HZ);
  37. }

  38. /* 打开操作 */
  39. static int kgrunt_open(struct inode *inode, struct file *file)
  40. {
  41.     /* 由 inode 对应的字符设备指针得到 kgrunt 设备的地址 */
  42.     struct kgrunt_dev *dev =
  43.         container_of(inode->i_cdev, struct kgrunt_dev, cdev);
  44.     pr_debug("kgrunt_open: be called!\n");
  45.     /* 将 kgrunt 设备的地址保存在打开的文件中 */
  46.     file->private_data = dev;
  47.     /* 注册定时器,如是已经(被其他进程)注册,则修改它 */
  48.     mod_timer(&dev->timer, jiffies+HZ); /* 定时器将于一秒后触发 */
  49.     dev->timer_flag = 1; /* 设置标志,让定时器持续注册自身 */
  50.     /* 返回 0 表示打开成功 */
  51.     return 0;
  52. }

  53. /* 关闭操作 */
  54. static int kgrunt_release(struct inode *inode, struct file *file)
  55. {
  56.     /* 由 inode 对应的字符设备指针得到 kgrunt 设备的地址 */
  57.     struct kgrunt_dev *dev =
  58.         container_of(inode->i_cdev, struct kgrunt_dev, cdev);
  59.     pr_debug("kgrunt_release: be called!\n");
  60.     /* 先将继续注册定时器的标志清零,再注销定时器,否则在对称多处理器平台上,
  61.      * 当 del_timer_sync 返回时可能定时器已经再次注册上 */
  62.     dev->timer_flag = 0;
  63.     /* 注销定时器 */
  64.     del_timer_sync(&dev->timer);
  65.     return 0;
  66. }

  67. /* 读操作 */
  68. static ssize_t kgrunt_read(struct file *file, char __user *buf,
  69.         size_t count, loff_t *pos)
  70. {
  71.     int err; /* 用于保存错误码 */
  72.     /* 从 file 中得到对应的 kgrunt 设备 */
  73.     struct kgrunt_dev *dev = (struct kgrunt_dev *)file->private_data;
  74.     pr_debug("kgrunt_read: pos = %d, count = %d.\n", (int)*pos, count);
  75.     /* 参数合法性检查 */
  76.     if (count < 0) return -EINVAL; /* 非法参数 */
  77.     if (count == 0) return 0; /* 直接返回 0 */
  78.     if (dev->bytes == 0) { /* 无数据可读 */
  79.         /* 非阻塞方式下返回 -EAGAIN */
  80.         if (file->f_flags & O_NONBLOCK) return -EAGAIN;
  81.         /* 等待直到缓冲区不空 */
  82.         err = wait_event_interruptible(dev->queue, dev->bytes > 0);
  83.         /* 发生信号则返回 -EINTR */
  84.         if (err == -ERESTARTSYS) return -EINTR;
  85.     }
  86.     /* 控制读取的长度,不能超过当前有效数据的个数 */
  87.     if (count > dev->bytes) count = dev->bytes;
  88.     /* 从内核缓冲区复制数据到 buf 指向的用户态内存。
  89.      * 让每次读都从缓冲区开始复制数据,因此读写位置无意义 */
  90.     if (copy_to_user(buf, dev->buf, count) > 0) {
  91.         pr_debug("kgrunt_read: copy_to_user() FAILED!\n");
  92.         return -EFAULT;
  93.     }
  94.     /* 读取之后将缓冲区中的数据清空 */
  95.     dev->bytes = 0;
  96.     /* 返回读到的字节数 */
  97.     return count;
  98. }

  99. /* 写操作 */
  100. static ssize_t kgrunt_write(struct file *file, const char __user *buf,
  101.         size_t count, loff_t *pos)
  102. {
  103.     /* 不支持写操作,直接返回错误码 */
  104.     return -EPERM;
  105. }

  106. /* 定位操作 */
  107. static loff_t kgrunt_llseek(struct file *file, loff_t offset, int whence)
  108. {
  109.     /* 不支持定位操作,直接返回错误码 */
  110.     return -EPERM;
  111. }

  112. /* 设备的文件操作,全局变量 */
  113. static struct file_operations kgrunt_fops = {
  114.     .owner = THIS_MODULE, /* 所属模块 */
  115.     .open = kgrunt_open, /* 打开操作 */
  116.     .release = kgrunt_release, /* 关闭操作 */
  117.     .read = kgrunt_read, /* 读操作 */
  118.     .write = kgrunt_write, /* 写操作 */
  119.     .llseek = kgrunt_llseek, /* 定位操作 */
  120. };

  121. /* 全局变量,表示一个 kgrunt 设备 */
  122. static struct kgrunt_dev kgrunt;

  123. /* 模块的初始化函数 */
  124. static __init int kgrunt_init(void)
  125. {
  126.     int err; /* 用于保存错误码 */
  127.     pr_debug("kgrunt_init: be called!\n");
  128.     /* 分配设备号 */
  129.     if ((err = alloc_chrdev_region(&kgrunt.dev, 0, 1, "kgrunt")) < 0) {
  130.         pr_debug("kgrunt_init: alloc_chrdev_region() ERR = %d!\n", -err);
  131.         goto alloc_chrdev_region_fail;
  132.     }
  133.     pr_debug("kgrunt_init: dev = (%d, %d).\n",
  134.             MAJOR(kgrunt.dev), MINOR(kgrunt.dev));
  135.     /* 分配内核缓冲区 */
  136.     if ((kgrunt.buf = kmalloc(BUF_SIZE, GFP_KERNEL)) == NULL) {
  137.         pr_debug("kgrunt_init: kmalloc() ERR!\n");
  138.         err = -ENOMEM; /* 此错误号表示内存不足 */
  139.         goto kmalloc_kgrunt_buf_fail;
  140.     }
  141.     /* 保存缓冲区的长度 */
  142.     kgrunt.len = BUF_SIZE;
  143.     kgrunt.bytes = 0;
  144.     /* 初始化字符设备 */
  145.     cdev_init(&kgrunt.cdev, &kgrunt_fops);
  146.     kgrunt.cdev.owner = kgrunt_fops.owner;
  147.     /* 初始化定时器 */
  148.     setup_timer(&kgrunt.timer, kgrunt_timer_fn, (unsigned long)&kgrunt);
  149.     kgrunt.timer_flag = 0;
  150.     /* 初始化等待队列 */
  151.     init_waitqueue_head(&kgrunt.queue);
  152.     /* 注册字符设备 */
  153.     if ((err = cdev_add(&kgrunt.cdev, kgrunt.dev, 1)) < 0) {
  154.         pr_debug("kgrunt_init: cdev_add() ERR = %d!\n", -err);
  155.         goto cdev_add_fail;
  156.     }
  157.     return 0; /* 返回 0 表示初始化成功 */
  158. /* 以下为出错处理,各个清理操作按初始化时的倒序排列,出错时跳转到相应的位置,
  159.  * 这是驱动编程中常用的做法 */
  160. cdev_add_fail:
  161.     kfree(kgrunt.buf); /* 释放所分配的内存 */
  162. kmalloc_kgrunt_buf_fail:
  163.     unregister_chrdev_region(kgrunt.dev, 1); /* 注销所注册的设备号 */
  164. alloc_chrdev_region_fail:
  165.     return err;
  166. }
  167. module_init(kgrunt_init); /* 指定 kgrunt_init 为模块的初始化函数 */

  168. /* 模块的退出函数 */
  169. static __exit void kgrunt_exit(void)
  170. {
  171.     pr_debug("kgrunt_exit: be called!\n");
  172.     /* 模块退出时进行清理,按初始化时的倒序操作 */
  173.     cdev_del(&kgrunt.cdev); /* 注销字符设备 */
  174.     kfree(kgrunt.buf); /* 释放所分配的内存 */
  175.     unregister_chrdev_region(kgrunt.dev, 1); /* 注销所注册的设备号 */
  176. }
  177. module_exit(kgrunt_exit); /* 指定 kgrunt_exit 为模块的退出函数 */
  1. # 文件名:Makefile
  2. # 说明:定时器例程 Makefile
  3. # 模块名
  4. MOD_NAME := kgrunt
  5. # 模块源码(不能出现与模块名相同的文件名)
  6. SRCS := \
  7. EXTRA_CFLAGS = -DDEBUG
  8. OBJS := $(SRCS:.c=.o)
  9. ifeq ($(KERNELRELEASE),)
  10. KERNELDIR ?= /lib/modules/$(shell uname -r)/build
  11. PWD := $(shell pwd)
  12. default:
  13. $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
  14. clean:
  15. -rm -f $(MOD_NAME).ko
  16. -rm -f $(MOD_NAME).o
  17. -rm -f $(OBJS)
  18. -rm -rf .tmp_versions
  19. -rm -f .*.cmd
  20. -rm -f *.mod.c
  21. -rm -f *.mod.o
  22. -rm -f Module.symvers
  23. -rm -f modules.order
  24. else
  25. $(MOD_NAME)-objs := $(OBJS)
  26. obj-m := $(MOD_NAME).o
  27. endif
阅读(204) | 评论(0) | 转发(0) |
0

上一篇:获取 TID

下一篇:等待队列例程

给主人留下些什么吧!~~