1.内核定时器:

  Linux 内核所提供的用于操作定时器的数据结构和函数如下:

(1) timer_list

  在 Linux 内核中,timer_list 结构体的一个实例对应一个定时器

  1 struct timer_list {
2          struct list_head entry; /* 定时器列表 */
3          unsigned long expires; /*定时器到期时间*/
4           void (*function)(unsigned long); /* 定时器处理函数 */
5          unsigned long data; /* 作为参数被传入定时器处理函数 */
6          struct timer_base_s *base;
7   ...
8 };

当定时器期满后,其中第 5 行的 function()成员将被执行,而第 4 行的 data 成员则是传入其中的参数,第 3 行的 expires 则是定时器到期的时间(jiffies)。

如下代码定义一个名为 my_timer 的定时器:
struct timer_list my_timer;

  (2)初始化定时器

    void init_timer(struct timer_list * timer);

    上述 init_timer()函数初始化 timer_list 的 entry 的 next 为 NULL,并给 base 指针赋值

    TIMER_INITIALIZER(_function, _expires, _data)宏用于赋值定时器结构体的function、expires、
    data 和 base 成员

    DEFINE_TIMER(_name,  _function,  _expires,  _data)宏是定义并初始化定时器成员的“快捷方
    式”。

    此外,static inline void setup_timer(struct timer_list * timer, void (*function)(unsigned long),unsigned long data)
    也可用于初始化定时器并赋值其成员

  (3)增加定时器

    void add_timer(struct timer_list* timer);

   (4)删除定时器

    int  del_timer(struct timer_list* timer);

  del_timer_sync()是 del_timer()的同步版,在删除一个定时器时需等待其被处理完,因此该函数的调用不能发生在中断上下文。

   (5).修改定时器的 expire

    int mod_timer(struct timer_list *timer, unsigned long expires);

  上述函数用于修改定时器的到期时间,在新的被传入的 expires 到来后才会执行定时器函数。

2.内核中延时的工作delayed_work

注意,对于这种周期性的任务,Linux 内核还提供了一套封装好的快捷机制,其本质利用工作队列
和定时器实现,这套快捷机制是就是delayed_work,delayed_work结构体的定义如代码清单10.11所示。
代码清单 10.11   delayed_work 结构体
1  struct delayed_work {
2                struct work_struct work;
3                struct timer_list timer;
4  };
5  struct work_struct {

6                atomic_long_t data;
7  #define WORK_STRUCT_PENDING 0 
8  #define WORK_STRUCT_FLAG_MASK (3UL)
9  #define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK)
10               struct list_head entry;
11               work_func_t func;
12 #ifdef CONFIG_LOCKDEP
13               struct lockdep_map lockdep_map;
14 #endif
15 };

我们可以通过如下函数调度一个 delayed_work 在指定的延时后执行:
int schedule_delayed_work(struct delayed_work *work, unsigned long delay);
当指定的 delay 到来时 delayed_work 结构体中 work 成员的 work_func_t 类型成员 func()会被
执行。work_func_t 类型定义为:
typedef void (*work_func_t)(struct work_struct *work);
其中 delay 参数的单位是 jiffies,因此一种常见的用法如下:
schedule_delayed_work(&work, msecs_to_jiffies(poll_interval));
其中的 msecs_to_jiffies()用于将毫秒转化为 jiffies。
如果要周期性的执行任务,通常会在 delayed_work 的工作函数中再次调用 schedule_delayed_
work(),周而复始。
如下函数用来取消 delayed_work:
int cancel_delayed_work(struct delayed_work *work);
int cancel_delayed_work_sync(struct delayed_work *work);

3.内核延时

Linux 内核中提供了如下 3 个函数分别进行纳秒、微秒和毫秒延迟:
void ndelay(unsigned long nsecs);
void udelay(unsigned long usecs);
void mdelay(unsigned long msecs);
上述延迟的实现原理本质上是忙等待,它根据 CPU 频率进行一定次数的循环。

毫秒时延(以及更大的秒时延)已经比较大了,在内核中,最好不要直接使用 mdelay()函数,这将无谓地耗费 CPU 资源,对于毫秒级以上时延,内核提供了下述函数:
void msleep(unsigned int millisecs);
unsigned long msleep_interruptible(unsigned int millisecs);
void ssleep(unsigned int seconds);
上述函数将使得调用它的进程睡眠参数指定的时间,msleep()、ssleep()不能被打断,而msleep_interruptible()则可以被打断。

秒设备驱动程序:

#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/platform_device.h> static unsigned char timermajor = ;
#define TIMERNAME "mytimer" static struct class *timer_class;
static struct device *timer_device; struct timer_dev {
struct cdev cdev;
atomic_t counter;
struct timer_list mytimer;
}; struct timer_dev *my_timer_dev; static void my_timer_fun(unsigned int arg)
{
mod_timer(&my_timer_dev->mytimer, jiffies + HZ);
atomic_inc(&my_timer_dev->counter);
printk(KERN_NOTICE "current jiffies is %ld\n", jiffies);
} static int my_timer_open(struct inode * inode, struct file * file)
{
init_timer( &my_timer_dev->mytimer);
my_timer_dev->mytimer.expires = jiffies+ HZ;
my_timer_dev->mytimer.function = & my_timer_fun; add_timer(&my_timer_dev->mytimer);
atomic_set(&my_timer_dev->counter, );
return ;
} static ssize_t my_timer_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
int counter; counter = atomic_read(&my_timer_dev->counter); if(copy_to_user(buf, &counter, sizeof(int)))
printk("copy_to_user error\n"); return ;
} static int my_timer_close(struct inode *inode, struct file *file)
{
del_timer( &my_timer_dev->mytimer);
return ;
} struct file_operations timer_fops = {
.owner = THIS_MODULE,
.open = my_timer_open,
.read = my_timer_read,
.release = my_timer_close,
}; static void timer_setup_cdev(struct timer_dev *dev, int minor )
{
unsigned char err;
dev_t deno;
deno = MKDEV(timermajor, minor);
cdev_init(&dev->cdev, &timer_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &timer_fops;
err = cdev_add(&dev->cdev, deno, );
if(err)
printk(KERN_ALERT"cdev_addd error\n");
} static int __init my_timer_init(void)
{
int err = ;
dev_t deno;
if(timermajor) {
register_chrdev_region(deno, , TIMERNAME);
} else {
err = alloc_chrdev_region(&deno, , , TIMERNAME);
timermajor = MAJOR(deno);
}
if(err)
printk(KERN_ALERT"alloc_chrdev_region error\n"); printk(KERN_ALERT"timermajor is %d\n", timermajor); my_timer_dev = kmalloc(sizeof(struct timer_dev), GFP_KERNEL);
if(!my_timer_dev) {
printk(KERN_ALERT"kmalloc error\n");
return -ENOMEM;
}
memset(my_timer_dev, , sizeof(struct timer_dev) ); timer_setup_cdev(my_timer_dev, ); timer_class = class_create(THIS_MODULE, TIMERNAME);
if(IS_ERR(timer_class)) {
printk(KERN_ALERT"class_create error\n");
return -EBUSY;
} timer_device = device_create(timer_class, NULL, deno, NULL, TIMERNAME);
if(IS_ERR(timer_device)) {
printk(KERN_ALERT"device_create error\n");
return -EBUSY;
} return ;
} static void __exit my_key_exit(void)
{
cdev_del(&my_timer_dev->cdev);
unregister_chrdev_region(MKDEV(timermajor, ), );
   kfree(my_timer_dev);
} MODULE_LICENSE("GPL");
MODULE_AUTHOR("qigaohua");
module_init(my_timer_init);
module_exit(my_key_exit);

测试程序:

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <stdio.h>

int main()
{
int fd;
int counter = ;
int old_counter = ;
fd = open("/dev/mytimer", O_RDWR);
if(fd < ) {
printf("open /dev/mytimer error\n");
return ;
} while() {
read(fd, &counter, sizeof(int));
if(counter != old_counter) {
old_counter = counter;
printf("counter is %d\n", counter);
}
}
}

Smart210学习记录-----linux定时器的更多相关文章

  1. Smart210学习记录-----Linux i2c驱动

    一:Linux i2c子系统简介: 1.Linux 的 I2C 体系结构分为 3 个组成部分: (1) I2C 核心. I2C 核心提供了 I2C 总线驱动和设备驱动的注册.注销方法,I2C 通信方法 ...

  2. Smart210学习记录-------linux驱动中断

    Linux中断 Linux 的中断处理分为两个半部,顶半部处理紧急的硬件操作,底半部处理不紧急的耗时操作.tasklet 和工作队列都是调度中断底半部的良好机制,tasklet 基于软中断实现.内核定 ...

  3. Smart210学习记录-------linux内核模块

    Linux 驱动工程师需要牢固地掌握 Linux 内核的编译方法以为嵌入式系统构建可运行的Linux 操作系统映像.在编译 LDD6410 的内核时,需要配置内核,可以使用下面命令中的 一个: #ma ...

  4. Smart210学习记录------linux串口驱动

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=27025492&id=327609 一.核心数据结构 串口驱动有 ...

  5. Smart210学习记录-------Linux设备驱动结构

    cdev结构体 1 struct cdev { 2 struct kobject kobj; /* 内嵌的 kobject 对象 */ 3 struct module *owner; /*所属模块*/ ...

  6. Smart210学习记录----beep linux字符设备驱动

    今天搞定了beep linux字符设备驱动,心里还是很开心的,哈哈...但在完成的过程中却遇到了一个非常棘手的问题,花费了我大量的时间,,,, 还是把问题描述一下吧,好像这个问题很普遍的,网上许多解决 ...

  7. Smart210学习记录------块设备

    转自:http://bbs.chinaunix.net/thread-2017377-1-1.html 本章的目的用尽可能最简单的方法写出一个能用的块设备驱动.所谓的能用,是指我们可以对这个驱动生成的 ...

  8. Smart210学习记录-----SD/MMC/SDIO驱动

    转自:http://jingpin.jikexueyuan.com/article/23369.html http://blog.csdn.net/evilcode/article/details/7 ...

  9. Smart210学习记录------paltform总线

    转自http://blog.csdn.net/xiaochongtou123/article/details/7752328 1.概述: 通常在Linux中,把SoC系统中集成的独立外设单元(如:I2 ...

随机推荐

  1. 报错总结_java.lang.RuntimeException: Invalid action class configuration that references an unknown class name

    在使用SSH进行项目开发时,一不小心就可能出现以上的错误提示. 这样的问题可以简单理解为未找到名字为XXX的action 1)xxxAction没有在Struts.xml中配置相应的action: 大 ...

  2. WCF练习小程序总结

    1.什么是WCF 严格的说,WCF就是专门用于服务定制.发布与运行以及消息传递和处理的一组专门类的集合,也就是所谓的“类库”.这些类通过一定方式被组织起来,共同协 作,并为开发者提供了一个统一的编程模 ...

  3. 如何在Quagga BGP路由器中设置IPv6的BGP对等体和过滤

    在本教程中,我们会向你演示如何创建IPv6 BGP对等体并通过BGP通告IPv6前缀.同时我们也将演示如何使用前缀列表和路由映射特性来过滤通告的或者获取到的IPv6前缀. 拓扑 服务供应商A和B希望在 ...

  4. 转: ORACLE索引介绍和使用

    1.什么是索引 索引是建立在表的一列或多个列上的辅助对象,目的是加快访问表中的数据: Oracle存储索引的数据结构是B*树,位图索引也是如此,只不过是叶子节点不同B*数索引: 索引由根节点.分支节点 ...

  5. 告别硬编码-发个获取未导出函数地址的Dll及源码

    还在为找内核未导出函数地址而苦恼嘛? 还在为硬编码通用性差而不爽吗? 还在为暴搜内核老蓝屏而痛苦吗? 请看这里: 最近老要用到内核未导出的函数及一些结构,不想再找特征码了,准备到网上找点符号文件解析的 ...

  6. 走进AngularJs(七) 过滤器(filter) - 吕大豹

    时间 2013-12-15 16:22:00  博客园-原创精华区 原文  http://www.cnblogs.com/lvdabao/p/3475426.html 主题 AngularJS 过滤器 ...

  7. plist 和 Xib

    plist文件 mainbudin加载时候有后缀 xib文件  mainbudin加载时候无需后缀

  8. Flux Demo解析

    最近学习了阮一峰老师的博文 "Flux入门教程",博文中详细介绍了Flux框架和Controller view模式,并提供了Demo,受益匪浅. 现特参考阮老师的Demo,绘制了一 ...

  9. Linux摄像头驱动学习之:(二)通过虚拟驱动vivi分析摄像头驱动

    一.通过指令 "strace -o xawtv.log xawtv" 得到以下调用信息:// 1~7都是在v4l2_open里调用1. open2. ioctl(4, VIDIOC ...

  10. 打饭助手之NABC

    Need: 同学们在早上跑操后要吃早饭,还有中午打饭时人更是多.常常要排很长的队伍,造成时间的浪费,和焦急的等待.因此我们需要错开打饭的高峰期,来避免打饭排队的悲哀. Approach: 通过获取摄像 ...