赞
踩
内核在运行中,需要处理两种时间应用,一个是延时,一个是定时。这些都要依赖于精确的tick中断来实现。
内核定时器机制主要包含两部分:
1、 定时器列表:内核维护一个定时器列表,用于保存所有激活的定时器。每个定时器都关联一个过期时间,定时器按过期时间排序在列表中。
2、定时器处理线程:内核有一个独立的线程运行定时器处理函数timer_expire_work_func()。这个函数不断从定时器列表头部取出已过期的定时器,调用定时器关联的回调函数。
硬件有一个时钟装置,该装置每隔一定时间发出一个时钟中断(称为一次时钟嘀嗒-tick),对应的中断处理程序就将全局变量jiffies_64加1。
jiffies_64 是一个全局64位整型, jiffies全局变量为其低32位的全局变量,程序中一般用jiffies。
所以可以把jiffies看成是一个时间戳计数器
HZ:可配置的宏,表示1秒钟产生的时钟中断次数,一般设为100或200
按时间工短分为:短延时与长延时
按延时的方式分为:阻塞类延时和非阻塞类延时
延时机制的选择原则:
void ndelay(unsigned long nsecs) 延迟多少纳秒
void udelay(unsigned long usecs) 延迟多少微秒
void mdelay(unsigned long msecs) 延迟多少毫秒
使用jiffies比较宏来实现
time_after(a,b) //a > b
time_before(a,b) //a < b 意为从a时间延迟到b时间
//延迟100个jiffies
unsigned long delay = jiffies + 100;
while(time_before(jiffies,delay))
{
;
}
//延迟2s
unsigned long delay = jiffies + 2*HZ;
while(time_before(jiffies,delay))
{
;
}
void msleep(unsigned int msecs);
unsigned long msleep_interruptible(unsigned int msecs);
linux 3.14
#include <linux/timer.h>
struct timer_list
{
struct list_head entry;
unsigned long expires; // 期望的时间值 当前jiffies + x * HZ 做为超时时间
void (*function)(unsigned long); // 时间到达后,执行的回调函数,该函数处于软中断异常上下文,因此该回调函数内不允许有阻塞操作
unsigned long data; //传给回调的参数
};
linux 4.15
struct timer_list {
/*
* All fields that change during normal runtime grouped to the
* same cacheline
*/
struct hlist_node entry;
unsigned long expires;
void (*function)(struct timer_list *);
u32 flags;
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
这个函数完成了定时器结构体的初始化工作:
init_timer(struct timer_list *)
注意:如果是linux4.15版以后,用的是timer_setup函数源码
static inline void timer_setup(struct timer_list *timer, void (*callback)(struct timer_list *), unsigned int flags) { /* * check for invalid bits, the rest is stored as-is * Note that TIMER_DUMMY is silently ignored, so there is no * way to select it here. */ BUILD_BUG_ON(flags & ~TIMER_ADVANCED_BITS|TIMER_DEFERRED); timer->entry.pprev = NULL; timer->function = callback; timer->flags = flags; #ifdef CONFIG_LOCKDEP timer->lockdep_map = 0; #endif }
增加定时器到链表中------从而使该 定时器开始计时
void add_timer(struct timer_list *timer);
删除定时器 -------定时器停止工作
int del_timer(struct timer_list * timer);
int mod_timer(struct timer_list *timer, unsigned long expires);
需要每次定时器到时后,调用这个函数重新设置定时器的expires参数。使定时器再次定时
1、定义struct timer_list tl类型的变量
2、定义定时器回调函数
void xxx_func(unsigned long arg)
{
…
mod_timer(…);//如需要定时器继续隔指定时间再次调用本函数
}
3、在模块入口函数处初始化定时器
init_timer(…);//模块入口函数
如果是linux4.15以上版本,则用timer_setup()函数初始化
4、在模块入口函数或open或希望定时器开始工作的地方写如下代码
tl.expires = jiffies + n * HZ //n秒
tl.function = xxx_func;
tl.data = …;
add_timer(…);
5、不想让定时器继续工作时
del_timer(…);
这里做一个定时器驱动。该驱动会启动一个定时器,每一秒计一次数。另外,从应用层有一个程序,间隔去读这个驱动的计数器并显示出来:
/************************************************************************* > File Name: timer-second.c ************************************************************************/ #include <linux/kernel.h> #include <linux/module.h> #include <linux/cdev.h> #include <linux/fs.h> #include <asm/uaccess.h> #include <linux/slab.h> #include <linux/types.h> #include <linux/timer.h> /*1、定义重要的变量及结构体*/ struct x_dev_t { struct cdev my_dev; //cdev设备描述结构体变量 atomic_t have_open; //记录驱动是否被打开的原子变量 struct timer_list tlist; //定时器结构体 unsigned int second; //计数器变量 }; struct x_dev_t *pcdev; /*所有驱动函数声明*/ ssize_t read (struct file *, char __user *, size_t, loff_t *); int open (struct inode *, struct file *); int release (struct inode *, struct file *); //驱动操作函数结构体,成员函数为需要实现的设备操作函数指针 //简单版的模版里,只写了open与release两个操作函数。 struct file_operations fops={ .owner = THIS_MODULE, .open = open, .release = release, .read = read, }; //定时器的回调函数linux4.15版 void timer_back(struct timer_list *data){ pcdev->second ++; //printk("timer_back second %d\n",pcdev->second); data->expires = jiffies + HZ; mod_timer(data , jiffies+HZ); //add_timer(data); } static int __init my_init(void){ int unsucc =0; dev_t devno; int major,minor; pcdev = kzalloc(sizeof(struct x_dev_t), GFP_KERNEL); /*2、创建 devno */ unsucc = alloc_chrdev_region(&devno , 0 , 1 , "timer-second"); if (unsucc){ printk(" creating devno faild\n"); return -1; } major = MAJOR(devno); minor = MINOR(devno); printk("devno major = %d ; minor = %d;\n",major , minor); /*3、初始化 cdev结构体,并将cdev结构体与file_operations结构体关联起来*/ /*这样在内核中就有了设备描述的结构体cdev,以及设备操作函数的调用集合file_operations结构体*/ cdev_init(&pcdev->my_dev , &fops); pcdev->my_dev.owner = THIS_MODULE; /*4、注册cdev结构体到内核链表中*/ unsucc = cdev_add(&pcdev->my_dev,devno,1); if (unsucc){ printk("cdev add faild \n"); return 1; } //初始化原子量have_open为1 atomic_set(&pcdev->have_open,1); printk("the driver timer-second initalization completed\n"); return 0; } static void __exit my_exit(void) { cdev_del(&pcdev->my_dev); unregister_chrdev_region(pcdev->my_dev.dev , 1); printk("***************the driver timer-second exit************\n"); } /*5、驱动函数的实现*/ /*file_operations结构全成员函数.open的具体实现*/ int open(struct inode *pnode , struct file *pf){ struct x_dev_t *p = container_of(pnode->i_cdev,struct x_dev_t , my_dev); pf->private_data = (void *)p; //在open函数中对原子量have_open进行减1并检测。=0,允许打开文件,<0则不允许打开 if (atomic_dec_and_test(&p->have_open)){ printk("timer-second is opened\n"); //初始化定时器并启动 p->second = 0; timer_setup(&p->tlist , timer_back , 0); p->tlist.expires = jiffies + HZ; add_timer(&p->tlist); return 0; }else{ printk("device timer-second can't be opened again\n"); atomic_inc(&p->have_open);//原子量=-1,记得这里要把原子量加回到0 return -1; } } /*file_operations结构全成员函数.release的具体实现*/ int release(struct inode *pnode , struct file *pf){ struct x_dev_t *p = (struct x_dev_t *)pf->private_data; printk("timer-second is closed \n"); atomic_set(&p->have_open,1); del_timer(&p->tlist); return 0; } ssize_t read (struct file * pf, char __user * buf, size_t size , loff_t * pops){ struct x_dev_t *p = pf->private_data; printk("driver: now timer is %d 's\n",p->second); return p->second; } module_init(my_init); module_exit(my_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("");
/************************************************************************* > File Name: test.c ************************************************************************/ #include<stdio.h> #include <unistd.h> #include <string.h> #include <fcntl.h> int main (int argc , char **argv){ int fd0,fd1; if (argc <2){ printf("argument is too less\n"); return -1; }else{ fd0 = open(argv[1] , O_RDONLY ); while (fd0){ printf("read timer-second is %d\n",read(fd0 , NULL,0)); sleep(2); } } close(fd0); return 0; }
sudo insmod timer-second.ko
用dmesg显示,设备号为244 0
sudo mknod /dev/timer-second c 244 0
sudo chmod 777 /dev/timer-second
执行测试程序: ./test.elf /dev/timer-second
结果:每2秒读一次设备,显示驱动的计数值。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。