/** * Author:hasen * 參考 :《linux设备驱动开发具体解释》 * 简单介绍:android小菜鸟的linux * 设备驱动开发学习之旅 * 主题:时钟 * Date:2014-11-15 */一、内核定时器
Linux设备驱动编程中。能够利用内核中提供的一组函数和数据结构来完毕定时触发工作或者完毕某周期
性的事务。这组函数和数据使得驱动project师多数情况下不用关心详细的软件定时器到底相应着如何的内核和硬件
行为。
Linux内核所提供的用于操作定时器的数据结构和函数例如以下:
(1)timer_list
在Linux内核中,timer_list结构体的一个实例相应一个定时器。
struct timer_list {
struct list_head entry ;/*定时器列表*/
unsigned long expires ; /*定时器到期时间(jiffies)*/
void (*function)(unsigned long );/*定时器处理函数*/
unsigned long data ;/*作为參数传入定时器处理函数*/
struct timer_base_s base ;
} 以下定义一个my_timer的定时器struct timer_lsit 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_INITIALIZER(_function,_expires,_data){ .entry = {.prev = TIMER_ENTRY_STATIC} , .funciton = (_function), .expires = (_expires) , .data = (_data) , .base = &boot_tvec_bases , } DEFINE_TIMER(_name,_function,_espires,_data)宏是定义并初始化定时器成员的“快捷方式”。这个#define DEFINE_TIMER(_name,_function,_expires,_data) struct timer_list _name = TIMER_INITIALIZER(_function,_expires,_data)此外,setup_timer()函数也能够用来初始化定时器并给其成员赋值。其代码例如以下:
static inline void setup_timer(struct timer_list *timer,
void (*function)(unsigned long),unsigned long data)
{
timer->function = function ;
timer->data = data ;
init_timer(timer) ;
} (3)添加定时器void add_timer(struct timer_list *timer) ;上述函数用于注冊内核定时器,将定时器接入到内核动态定时器链表中。
int del_timer(struct timer_lsit *timer) ;上述函数用于删除定时器。
(5)改动定时器的expire
int mod_timer(struct timer_list *timer,unsigned long expires) ;上述函数用于改动定时器的到期时间。在新的被传入的expires到来后才会运行定时器函数。
演示样例:内核定时器使用模板
/*xxx设备结构体*/
struct xx_dev{
struct cdev cdev ;
...
timer_lsit xxx_timer ;/*设备要使用的定时器*/
}
/*xxx驱动中的模函数*/
xxx_func1(...)
{
struct xxx_dev *dev = filp->private_data ;
...
/*初始化定时器*/
init_timer(&dev->xxx_timer) ;
dev->xxx_timer.function = &xxx_do_timer ;
dev->xxx_timer.data = (unsigned long)dev ;/*设备结构体指针作为定时器处理函数參数*/
dev->xxx_timer.expires = jiffies + delay ;
/*加入(注冊)定时器*/
add_timer(&dev->xxx_timer) ;
...
}
/*xxx驱动中的某函数*/
xxx_func2(...)
{
...
/*删除定时器*/
del_timer(&dev->xxx_timer) ;
...
}
/*定时器处理函数*/
static void xxx_do_timer(unsigned long arg)
{
struct xxx_device *dev = (struct xxx_device*)(arg) ;
...
/*调度定时器再运行*/
dev->xxx_timer.expires = jiffies + delay ;
add_timer(&dev->xxx_timer) ;
...
}
定时器的到期时间往往是在眼下的jiffies的基础上加入一个时延,若为Hz。则表示延迟1秒。struct delayed_work{
struct work_struct work ;
struct timer_list timer ;
};
struct work_struct {
atimic_long_t data ;
#define WORK_STRUCT_PENDING 0
#define WORK_STRUCT_FLAG_MASK (3UL)
#define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK)
struct list_head entry ;
work_func_t func ;
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map ;
#endif
}; 我们能够通过例如以下的函数调度一个delayed_work在指定的延时后运行。int schedule_delayed_work(struct delayed_work *work,unsigned long delay) ;当指定的delay到来时delayed_work结构体中work成员的work_func_t类型成员func()会被运行。
typedef void (*work_func_t) (struct work_sturct *work);当中delay參数的单位是jiffies,因此一种常见的与使用方法例如以下:
schedule_delayed_work(&work,msecs_to_jiffies(poll_interval)) ;当中的msecs_to_jiffies()用于将毫秒转化为jiffies。
int cancel_delayed_work(struct delayed_work *work) ; int cancel_delayed_work_sync(struct delayed_work *work) ;实例:秒字符设备
#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>
#define SECOND_MAJOR 248 /*预设的second的主设备号*/
static int second_major = SECONG_MAJOR ;
/*second设备结构体*/
struct second_dev{
struct cdev cdev ;/*cdev结构体*/
atomic_t counter ;/*一共经历了多少秒*/
struct timer_list s_timer ;/*设备要使用的定时器*/
}
struct second_dev *second_devp ;/*设备结构体指针*/
struct void second_timer_handle(unsigned long arg)
{
mod_timer(&second_devp->s_timer,jiffies + Hz) ;
atomic_inc(&second_devp->counter) ;
printk(KERN_NOTICE "current jiffies is %d\n",jiffies) ;
}
/*文件打开函数*/
int second_open(struct inode *inode ,struct file *filp)
{
/*初始化定时器*/
init_timer(&second_devp->s_timer);
second_devp->s_timer.function = &second_timer_handle ;
second_devp->s_timer.expires = jiffies + Hz ;
add_timer(&second_devp->s_timer) ;/*加入(注冊)定时器*/
atomic_set(&second_devp->count,0) ; //计数清0
return 0 ;
}
/*文件释放函数*/
int second_release(struct inode *inode ,struct file *filp)
{
del_timer(&second_devp->s_timer) ;
return 0 ;
}
/*读函数*/
static ssize_t second_read(struct file *filp ,char __user *buf,
size_t count,loff_t *ppos)
{
int counter ;
counter = atomic_read(&second_devp->counter) ;
if(put_user(counter,(int *)buf))
return -EFAULT ;
else
return sizeof(unsigned int) ;
}
/*文件操作结构体*/
static const struct file_operations second_fops = {
.owner = THIS_MODULE ,
.open = second_open ,
.release = second_release ,
.read = second_read ,
} ;
/*初始化并注冊cdev*/
static void second_setup_cdev(struct second_dev *dev,int index)
{
int err,devno = MKDEV(second_major,index) ;
cdev_init(&dev->cdev,&second_fops) ;
dev->cdev.owner = THIS_MODULE ;
err = cdev_add(&dev->cdev,devno,1) ;
if(err)
printk(KERN_NOTICE,"Error %d adding LED%d",err,index) ;
}
/*设备驱动模块载入函数*/
int second_init(void)
{
int ret ;
dev_t devno = MKDEV(second_major,0) ;
/*申请设备号*/
if(second_major)
ret = register_chrdev_region(devno,1,"second") ;
else{
ret = alloc_chrdev_region(&devno,0,1,"second") ;
second_major = MAJOR(devno) ;
}
if(ret < 0)
return ret ;
/*动态申请设备结构体的内存*/
second_devp = kmalloc(sizeof(struct second_dev),GFP_KERN) ;
if(!second_devp){/*申请失败*/
ret = -ENOMEM ;
goto fail_malloc ;
}
memset(second_devp,0,sizeof(struct second_dev)) ;
second_setup_cdev(second_devp,0) ;
fail_malloc:
unregister_chrdev_region(devno,1) ;
return ret ;
}
/*模块卸载函数*/
void second_exit(void)
{
cdev_del(&second_devp->cdev) ; /*注销cdev*/
kfree(second_devp) ;/*释放设备结构体内存*/
unregister_chrdev_reigon(MKDEV(second_major,0),1) ;
}
MODULE_AUTHOR("Hasen<hasen.dc@gmail.com>") ;
MODULE_LICENSE("Dual BSD/GPL");
module_param(second_major,int,S_IRUGO) ;
module_init(second_init) ;
module_exit(second_exit) ; 在second的open()函数中,将启动定时器。此后每一秒会再次执行定时器处理函数。在second的#include ...
main()
{
int fd ;
int counter = 0 ;
int old_counter = 0 ;
/*打开/dev/second设备文件*/
fd= open("/dev/second",O_RDONLY) ;
if(fd != -1){
while(1){
read(fd,&counter,sizeof(unsigned int)) ;/*读取眼下经历的秒数*/
if(counter != old_counter){
printf("seconds after open /dev/second :%d\n",counter) ;
old_counter = counter ;
}
}
}else{
printf("Device open failure\n") ;
}
} 执行second_test之后。内核将不断地输出眼下的jiffies值。而应用程序将不断地输出自打开void ndelay(unsigned long nsecs) ; void udelay(unsigned long usecs) ; void mdelay(unsigned long msecs) ;上述延迟的实现原理本质上是忙等待。它依据CPU频率进行一定次数的循环,软件中进行这种延迟:
void delay(unsigned int time)
{
while(time--) ;
} ndelay()、udelay()和mdelay()函数的实现方式机理与此类似。内核在启动是,会执行一个延迟測试Calibrating delay loop... 530.84 BogoMIPS (lpj=1327104)毫秒时延(以及更大的秒时延)已经比較大了,在内核其中。最好不要直接使用mdelay()函数。这将无
void msleep(unsigned int millisecs) ; unsigned long msleep_interruptible(unsigned int millisecs) ; void ssleep(unsigned int seconds) ;上述函数将使得调用它的进程睡眠參数指定的时间,msleep()、ssleep()不能被打断。而
演示样例:使用忙等待先延迟100个jiffies再延迟2s
/*延迟100个jiffies*/ unsigned long delay = jiffies + 100 ; while(time_before(jiffies,delay)) ; /*延迟2s*/ unsigned long delay = jiffies + 2*Hz ; while(time_before(jiffies,delay)) ;与time_before()相应的另一个time_after()。它们在内核中定义为(实际上仅仅是将传入的未来时
#define time_after(a,b) (typecheck(unsigned long ,a)) && typecheck(unsigned long ,b) && ((long (b)-(long)(a)<0)) #define time_before(a,b) time_after(b,a)为了防止timer_before()和timer_after()的比較过程中编译优化器对jiffies的优化,内核将其定义
3、睡着延迟
睡着延迟无疑是比忙等待更好的方式。睡着延迟在等待时间到来之间进程处于睡眠状态。CPU资源
被其它进程使用。schedule_timeout()能够使当前任务睡眠指定的jiffies之后又一次被调度运行,msleep()和
msleep_interruptible()在本质上都是依靠包括了schedule_timeout()的schedule_timeout_uninterruptible()和
schedule_timeout_interruptible()实现的。
void msleep(unsigned int msecs)
{
unsigned long timeout = msecs_to_jiffies(msecs) +1;
while(timeout)
timeout = schedule_timeout_uninterruptible(timeout) ;
}
unsigned long msleep_interruptible(unsigned int msecs)
{
unsigned long timeout = msecs_to_jiffies(msecs) + 1 ;
while(timeout && !signal_pending(current))
timeout = schedule_timeout_interruptible(timeout) ;
return jiffies_to_msecs(timeout) ;
} 实际上。schedule_timeout()的实现原理是向系统加入一个定时器,在定时器处理函数中唤醒參数对
signed long __sched schedule_timeout_interruptible(signed long timeout)
{
__set_current_state(TASK_INTERRUPTIBLE) ;
return schedule_timeout(timeout) ;
}
signed long __sched schedule_timeout_uninterruptible(signed long timeout)
{
__set_current_state(TASK_UNINTERRUPTIBLE) ;
return schedule_timeout(timeout) ;
} 另外。以下两个函数能够将当前进程加入到等待队列中,从而在等待队列上睡眠。当超时发生时,进sleep_on_timeout(wait_queue_head_t *q,unsigned long timeout) ; interruptible_sleep_on_timeout(wait_queue_head_t *q,unsigned long timeout) ;总结
原文:http://www.cnblogs.com/wzzkaifa/p/7326995.html