我的環(huán)境:
Fedora 14 內(nèi)核版本為2.6.38.1
開(kāi)發(fā)板:ARM9 TQ2440
移植內(nèi)核版本:linux-2.6.30.4
定時(shí)器在linux內(nèi)核中主要是采用一個(gè)結(jié)構(gòu)體實(shí)現(xiàn)的。但是需要注意定時(shí)器是一個(gè)只運(yùn)行一次的對(duì)象,也就是當(dāng)一個(gè)定時(shí)器結(jié)束以后,還需要重現(xiàn)添加定時(shí)器。但是可以采用mod_timer()函數(shù)動(dòng)態(tài)的改變定時(shí)器到達(dá)時(shí)間。
這個(gè)驅(qū)動(dòng)主要實(shí)現(xiàn)內(nèi)核定時(shí)器的基本操作。內(nèi)核定時(shí)器主要是是通過(guò)下面的結(jié)構(gòu)體struct timer_list實(shí)現(xiàn)。需要的頭文件包括#include<linux/timer.h>,但是在實(shí)際開(kāi)發(fā)過(guò)程中不需要包含該頭文件,因?yàn)樵趕ched.h中包含了該頭文件。
struct timer_list {
struct list_head entry;
unsigned long expires;
void (*function)(unsigned long);
unsigned long data;
struct tvec_base *base;
#ifdef CONFIG_TIMER_STATS
void *start_site;
char start_comm[16];
int start_pid;
#endif
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
定時(shí)器的實(shí)現(xiàn)主要是該結(jié)構(gòu)體的填充和部分函數(shù)的配合即可完成。其中紅色的部分是最主要的幾個(gè)元素,1、expires主要是用來(lái)定義定時(shí)器到期的時(shí)間,通常采用jiffies這個(gè)全局變量和HZ這個(gè)全局變量配合設(shè)置該元素的值。比如expires = jiffies + n*HZ,其中jiffies是自啟動(dòng)以來(lái)的滴答數(shù),HZ是一秒種的滴答數(shù)。
2、function可以知道是一個(gè)函數(shù)指針,該函數(shù)就是定時(shí)器的處理函數(shù),類似我們?cè)谥袛嘀械闹袛嗪瘮?shù),其實(shí)定時(shí)器和中斷有很大的相似性。定時(shí)器處理函數(shù)是自己定義的函數(shù)。
3、data通常是實(shí)現(xiàn)參數(shù)的傳遞,從function的參數(shù)類型可以知道,data可以作為定時(shí)器處理函數(shù)的參數(shù)。
其他的元素可以通過(guò)內(nèi)核的函數(shù)來(lái)初始化。
初始化函數(shù)為:
init_timer(struct timer_list * timer);
或者直接DEFINE_TIMER宏實(shí)現(xiàn)定義和初始化操作。
#define DEFINE_TIMER(_name, _function, _expires, _data) \
struct timer_list _name = \
TIMER_INITIALIZER(_function, _expires, _data)
添加定時(shí)器到內(nèi)核的函數(shù):
void add_timer(struct timer_list *timer)
{
BUG_ON(timer_pending(timer));
mod_timer(timer, timer->expires);
}
刪除定時(shí)器函數(shù),如果定時(shí)器的定時(shí)時(shí)間還沒(méi)有到達(dá),那么才可以刪除定時(shí)器:
int del_timer(struct timer_list *timer)
修改定時(shí)器的到達(dá)時(shí)間,該函數(shù)的特點(diǎn)是,不管定時(shí)器是否到達(dá)時(shí)間,都會(huì)重現(xiàn)添加一個(gè)定時(shí)器到內(nèi)核。所以可以在定時(shí)處理函數(shù)中可以調(diào)用該函數(shù)修改需要重新定義的到達(dá)時(shí)間。
int mode_timer(struct timer_list *timer,unsigned long expires)
int mod_timer(struct timer_list *timer, unsigned long expires)
{
/*
* This is a common optimization triggered by the
* networking code - if the timer is re-modified
* to be the same thing then just return:
*/
if (timer->expires == expires && timer_pending(timer))
return 1;
/*注意調(diào)用的條件,也就是說(shuō)明當(dāng)前的定時(shí)器為鏈表的最后一個(gè)*/
return __mod_timer(timer, expires, false);
}
static inline int
__mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)
{
struct tvec_base *base, *new_base;
unsigned long flags;
int ret;
ret = 0;
timer_stats_timer_set_start_info(timer);
BUG_ON(!timer->function);
base = lock_timer_base(timer, &flags);
if (timer_pending(timer)) {
detach_timer(timer, 0);
ret = 1;
} else {
if (pending_only)
goto out_unlock;
}
debug_timer_activate(timer);
new_base = __get_cpu_var(tvec_bases);
if (base != new_base) {
/*
* We are trying to schedule the timer on the local CPU.
* However we can't change timer's base while it is running,
* otherwise del_timer_sync() can't detect that the timer's
* handler yet has not finished. This also guarantees that
* the timer is serialized wrt itself.
*/
if (likely(base->running_timer != timer)) {
/* See the comment in lock_timer_base() */
timer_set_base(timer, NULL);
spin_unlock(&base->lock);
base = new_base;
spin_lock(&base->lock);
timer_set_base(timer, base);
}
}
timer->expires = expires;
internal_add_timer(base, timer);
out_unlock:
spin_unlock_irqrestore(&base->lock, flags);
return ret;
}
static void internal_add_timer(struct tvec_base *base, struct timer_list *timer)
{
unsigned long expires = timer->expires;
unsigned long idx = expires - base->timer_jiffies;
struct list_head *vec;
if (idx < TVR_SIZE) {
int i = expires & TVR_MASK;
vec = base->tv1.vec + i;
} else if (idx < 1 << (TVR_BITS + TVN_BITS)) {
int i = (expires >> TVR_BITS) & TVN_MASK;
vec = base->tv2.vec + i;
} else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) {
int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;
vec = base->tv3.vec + i;
} else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) {
int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;
vec = base->tv4.vec + i;
} else if ((signed long) idx < 0) {
/*
* Can happen if you add a timer with expires == jiffies,
* or you set a timer to go off in the past
*/
vec = base->tv1.vec + (base->timer_jiffies & TVR_MASK);
} else {
int i;
/* If the timeout is larger than 0xffffffff on 64-bit
* architectures then we use the maximum timeout:
*/
if (idx > 0xffffffffUL) {
idx = 0xffffffffUL;
expires = idx + base->timer_jiffies;
}
i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;
vec = base->tv5.vec + i;
}
/*
* Timers are FIFO:
*/
/*添加到鏈表的最后,這說(shuō)明mod_timer實(shí)現(xiàn)了重新注冊(cè)一個(gè)定時(shí)器的操作*/
list_add_tail(&timer->entry, vec);
}
從上面的分析可以看出,mod_timer的實(shí)現(xiàn)過(guò)程比較復(fù)雜,但是基本上說(shuō)明了mod_timer函數(shù)重新注冊(cè)定時(shí)器的操作過(guò)程。
一般而言定時(shí)器的基本操作主要是上面的幾個(gè)函數(shù)。
我的基于內(nèi)核定時(shí)器的驅(qū)動(dòng)函數(shù)如下,參考了宋寶華的Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解(第二版)。
驅(qū)動(dòng)程序:
#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/uaccess.h>
#include<linux/device.h>
/*采用宏定義設(shè)置設(shè)備的主設(shè)備號(hào)*/
#define SECOND_MAJOR 0
/*靜態(tài)的分別保存靜態(tài)主設(shè)備號(hào)的變量*/
static int second_major = SECOND_MAJOR;
/*設(shè)備結(jié)構(gòu)體,通常在設(shè)備中包含需要的設(shè)備,比如字符、塊等類型*/
struct second_dev{
/*添加設(shè)備類型,
我認(rèn)為可以采用一個(gè)聯(lián)合體,
包含塊設(shè)備或者字符設(shè)備,類似inode的實(shí)現(xiàn)方法,
這樣可以提高結(jié)構(gòu)體的通用性
*/
struct cdev cdev;
/*原子變量,用來(lái)統(tǒng)計(jì)*/
atomic_t counter;
/*添加內(nèi)核定時(shí)器結(jié)構(gòu)體變量*/
struct timer_list s_timer;
/*用于動(dòng)態(tài)創(chuàng)建設(shè)備文件的設(shè)備類*/
struct class *myclass;
};
/*結(jié)構(gòu)體指針或者采用全局變量直接定義結(jié)構(gòu)都可以*/
struct second_dev *second_devp;
/*如果定時(shí)時(shí)間到了,定時(shí)器的處理函數(shù)*/
static void second_timer_handler(unsigned long arg)
{
/*
修改定時(shí)器中的到期時(shí)間,增加時(shí)間為1s,
需要注意的是mod_timer函數(shù)是重新注冊(cè)定時(shí)器到內(nèi)核
而不管定時(shí)器是否被運(yùn)行過(guò)
*/
mod_timer(&second_devp->s_timer,jiffies + HZ);
/*原子變量的增加*/
atomic_inc(&second_devp->counter);
/*輸出jiffies值*/
printk(KERN_NOTICE "Current jiffies is %d\n",jiffies);
}
/*open函數(shù)實(shí)現(xiàn)*/
static int second_open(struct inode *inode,struct file *filp)
{
/*初始化定義的內(nèi)核定時(shí)器*/
init_timer(&second_devp->s_timer);
/*指定內(nèi)核定時(shí)器的處理函數(shù)是上面定義好的函數(shù)*/
second_devp->s_timer.function = second_timer_handler;
/*指定定時(shí)間隔是1s*/
second_devp->s_timer.expires = jiffies + HZ;
/*將定時(shí)器添加到內(nèi)核*/
add_timer(&second_devp->s_timer);
/*同時(shí)設(shè)備相關(guān)的統(tǒng)計(jì)值為0*/
atomic_set(&second_devp->counter,0);
return 0;
}
/*release函數(shù)的實(shí)現(xiàn)*/
static int second_release(struct inode *inode,struct file *filp)
{
/*如果沒(méi)有到時(shí)間就關(guān)閉設(shè)備,直接刪除定時(shí)器*/
del_timer(&second_devp->s_timer);
return 0;
}
/*read函數(shù)的實(shí)現(xiàn)*/
static ssize_t second_read(struct file *filp,char __user *buf,size_t count,loff_t *ppos)
{
int counter;
/*讀當(dāng)前的值*/
counter = atomic_read(&second_devp->counter);
/*
采用put_user實(shí)現(xiàn)數(shù)值的傳送
put_user函數(shù)存在對(duì)指針變量的檢查,
因此不需要檢測(cè)指針是否正確
*/
if(put_user(counter,(int *)buf))
return -EFAULT;
else
/*返回?cái)?shù)據(jù)大小*/
return sizeof(unsigned int);
}
/*具體的文件操作集合*/
static const struct file_operations second_fops =
{
/*這是擁有者*/
.owner = THIS_MODULE,
.open = second_open,
.release = second_release,
.read = second_read,
};
/*初始化函數(shù)*/
static int __init second_init(void)
{
int ret;
/*設(shè)備號(hào)的申請(qǐng),創(chuàng)建*/
dev_t devno = MKDEV(second_major,0);
/*靜態(tài)申請(qǐng)?jiān)O(shè)備號(hào)*/
if(second_major)
{
ret = register_chrdev_region(devno,1,"second");
}
/*動(dòng)態(tài)申請(qǐng)?jiān)O(shè)備號(hào)*/
else
{
ret = alloc_chrdev_region(&devno,0,1,"second");
second_major = MAJOR(devno);
}
if(ret < 0)
{
return ret;
}
/*分配設(shè)備結(jié)構(gòu)體的地址空間*/
second_devp = kmalloc(sizeof(struct second_dev),GFP_KERNEL);
/*檢查是否分配正確*/
if(!second_devp)
{
ret = -ENOMEM;
goto fail_malloc;
}
/*清零分配的空間*/
memset(second_devp,0,sizeof(struct second_dev));
/*創(chuàng)建設(shè)備類,用于自動(dòng)創(chuàng)建設(shè)備文件*/
second_devp->myclass = class_create(THIS_MODULE,"second_timer_class");
/*字符設(shè)備初始化,綁定相關(guān)操作到設(shè)備*/
cdev_init(&second_devp->cdev,&second_fops);
/*設(shè)備的擁有者*/
second_devp->cdev.owner = THIS_MODULE,
/*添加設(shè)備到內(nèi)核*/
ret = cdev_add(&second_devp->cdev,devno,1);
/*錯(cuò)誤處理*/
if(ret)
{
printk(KERN_NOTICE "ERROR %d\n ",ret);
goto fail_malloc;
}
/*依據(jù)以前創(chuàng)建的設(shè)備類,創(chuàng)建設(shè)備*/
device_create(second_devp->myclass,NULL,devno,NULL,"second%d",0);
return 0;
/*錯(cuò)誤操作*/
fail_malloc:
unregister_chrdev_region(devno,1);
return ret;
}
/*退出函數(shù)*/
static void __exit second_exit(void)
{
/*釋放設(shè)備*/
device_destroy(second_devp->myclass,MKDEV(second_major,0));
/*刪除字符設(shè)備*/
cdev_del(&second_devp->cdev);
/*釋放設(shè)備類*/
class_destroy(second_devp->myclass);
/*釋放分配的內(nèi)存空間大小*/
kfree(second_devp);
/*釋放設(shè)備號(hào)*/
unregister_chrdev_region(MKDEV(second_major,0),1);
}
/*卸載和加載*/
module_init(second_init);
module_exit(second_exit);
/*LICENSE和作者信息*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("GP-<gp19861112@yahoo.com.cn>");
應(yīng)用程序:
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<linux/fs.h>
#include<unistd.h>
#include<fcntl.h>
int main()
{
int fd;
int counter = 0;
int old_counter = 0;
fd = open("/dev/second0",O_RDONLY);
if(fd != -1)
{
while(1)
{
read(fd,&counter,sizeof(unsigned int));
if(counter != old_counter)
{
printf("second after open /dev/second0 : %d\n",counter);
old_counter = counter;
}
}
}
else
{
printf("Device open failure\n");
exit(1);
}
exit(0);
}
實(shí)驗(yàn)效果:
[root@EmbedSky Test]# ./app-timer
Current jiffies is 2137721
second after open /dev/second0 : 1
Current jiffies is 2137921
second after open /dev/second0 : 2
Current jiffies is 2138121
second after open /dev/second0 : 3
Current jiffies is 2138321
second after open /dev/second0 : 4
Current jiffies is 2138521
second after open /dev/second0 : 5
Current jiffies is 2138721
second after open /dev/second0 : 6
以上的結(jié)果表明內(nèi)核定時(shí)器基本實(shí)現(xiàn)了效果,但從實(shí)驗(yàn)結(jié)果看好像為每?jī)擅雽?shí)現(xiàn)一次顯示。具體的原因還有待于再次分析,因?yàn)閍rm中的HZ應(yīng)該為100,而不是200。