轉自:http://blog.csdn.net/jidonghui/article/details/7449546node
LINUX內核定時器是內核用來控制在將來某個時間點(基於jiffies)調度執行某個函數的一種機制,其實現位於 <linux/timer.h> 和 kernel/timer.c 文件中。被調度的函數確定是異步執行的,它相似於一種「軟件中斷」,並且是處於非進程的上下文中,因此調度函數必須遵照如下規則: 1) 沒有 current 指針、不容許訪問用戶空間。由於沒有進程上下文,相關代碼和被中斷的進程沒有任何聯繫。 2) 不能執行休眠(或可能引發休眠的函數)和調度。 3) 任何被訪問的數據結構都應該針對併發訪問進行保護,以防止競爭條件。 內核定時器的調度函數運行過一次後就不會再被運行了(至關於自動註銷),但能夠經過在被調度的函數中從新調度本身來週期運行。 在SMP系統中,調度函數老是在註冊它的同一CPU上運行,以儘量得到緩存的局域性。 定時器API 內核定時器的數據結構 struct timer_list { struct list_head entry; unsigned long expires; void (*function)(unsigned long); unsigned long data; struct tvec_base *base; /* ... */ }; 其中 expires 字段表示指望定時器執行的 jiffies 值,到達該 jiffies 值時,將調用 function 函數,並傳遞 data 做爲參數。當一個定時器被註冊到內核以後,entry 字段用來鏈接該定時器到一個內核鏈表中。base 字段是內核內部實現所用的。 須要注意的是 expires 的值是32位的,由於內核定時器並不適用於長的將來時間點。 初始化 在使用 struct timer_list 以前,須要初始化該數據結構,確保全部的字段都被正確地設置。初始化有兩種方法。 方法一: DEFINE_TIMER(timer_name, function_name, expires_value, data); 該宏會靜態建立一個名叫 timer_name 內核定時器,並初始化其 function, expires, name 和 base 字段。 方法二: struct timer_list mytimer; setup_timer(&mytimer, (*function)(unsigned long), unsigned long data); mytimer.expires = jiffies + 5*HZ; 方法3: struct timer_list mytimer; init_timer(&mytimer); mytimer ->timer.expires = jiffies + 5*HZ; mytimer ->timer.data = (unsigned long) dev; mytimer ->timer.function = &corkscrew_timer; /* timer handler */ 經過init_timer()動態地定義一個定時器,此後,將處理函數的地址和參數綁定給一個timer_list, 注意,不管用哪一種方法初始化,其本質都只是給字段賦值,因此只要在運行 add_timer() 以前,expires, function 和 data 字段均可以直接再修改。 關於上面這些宏和函數的定義,參見 include/linux/timer.h。 註冊 定時器要生效,還必須被鏈接到內核專門的鏈表中,這能夠經過 add_timer(struct timer_list *timer) 來實現。 從新註冊 要修改一個定時器的調度時間,能夠經過調用 mod_timer(struct timer_list *timer, unsigned long expires)。mod_timer() 會從新註冊定時器到內核,而無論定時器函數是否被運行過。 註銷 註銷一個定時器,能夠經過 del_timer(struct timer_list *timer) 或 del_timer_sync(struct timer_list *timer)。其中 del_timer_sync 是用在 SMP 系統上的(在非SMP系統上,它等於del_timer),當要被註銷的定時器函數正在另外一個 cpu 上運行時,del_timer_sync() 會等待其運行完,因此這個函數會休眠。另外還應避免它和被調度的函數爭用同一個鎖。對於一個已經被運行過且沒有從新註冊本身的定時器而言,註銷函數其實也沒什麼事可作。 int timer_pending(const struct timer_list *timer) 這個函數用來判斷一個定時器是否被添加到了內核鏈表中以等待被調度運行。注意,當一個定時器函數即將要被運行前,內核會把相應的定時器從內核鏈表中刪除(至關於註銷) 一個簡單的例子 #include <linux/module.h> #include <linux/timer.h> #include <linux/jiffies.h> struct timer_list mytimer; static void myfunc(unsigned long data) { printk("%s/n", (char *)data); mod_timer(&mytimer, jiffies + 2*HZ); } static int __init mytimer_init(void) { setup_timer(&mytimer, myfunc, (unsigned long)"Hello, world!"); mytimer.expires = jiffies + HZ; add_timer(&mytimer); return 0; } static void __exit mytimer_exit(void) { del_timer(&mytimer); } module_init(mytimer_init); module_exit(mytimer_exit); 例子2 static struct timer_list power_button_poll_timer; static void power_button_poll(unsigned long dummy) { if (gpio_line_get(N2100_POWER_BUTTON) == 0) { ctrl_alt_del(); return; } power_button_poll_timer.expires = jiffies + (HZ / 10); add_timer(&power_button_poll_timer); } static void __init n2100_init_machine(void) { ; ; init_timer(&power_button_poll_timer); power_button_poll_timer.function = power_button_poll; power_button_poll_timer.expires = jiffies + (HZ / 10); add_timer(&power_button_poll_timer); } 例子3 設備open時初始化和註冊定時器 static int corkscrew_open(struct net_device *dev) { ; ; init_timer(&vp->timer); vp->timer.expires = jiffies + media_tbl[dev->if_port].wait; vp->timer.data = (unsigned long) dev; vp->timer.function = &corkscrew_timer; /* timer handler */ add_timer(&vp->timer); : ; } 定時器超時處理函數,對定時器的超時時間從新賦值 static void corkscrew_timer(unsigned long data) { ; ; vp->timer.expires = jiffies + media_tbl[dev->if_port].wait; add_timer(&vp->timer); ; ; } 設備close時刪除定時器 static int corkscrew_close(struct net_device *dev) { ; ; del_timer(&vp->timer); ; ; } 例子4 本例子用DEFINE_TIMER靜態建立定時器 #include <linux/module.h> #include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/timer.h> #include <linux/leds.h> static void ledtrig_ide_timerfunc(unsigned long data); DEFINE_LED_TRIGGER(ledtrig_ide); static DEFINE_TIMER(ledtrig_ide_timer, ledtrig_ide_timerfunc, 0, 0); static int ide_activity; static int ide_lastactivity; void ledtrig_ide_activity(void) { ide_activity++; if (!timer_pending(&ledtrig_ide_timer)) mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10)); } EXPORT_SYMBOL(ledtrig_ide_activity); static void ledtrig_ide_timerfunc(unsigned long data) { if (ide_lastactivity != ide_activity) { ide_lastactivity = ide_activity; led_trigger_event(ledtrig_ide, LED_FULL); mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10)); } else { led_trigger_event(ledtrig_ide, LED_OFF); } } static int __init ledtrig_ide_init(void) { led_trigger_register_simple("ide-disk", &ledtrig_ide); return 0; } static void __exit ledtrig_ide_exit(void) { led_trigger_unregister_simple(ledtrig_ide); } module_init(ledtrig_ide_init); module_exit(ledtrig_ide_exit); ================================================================================== add_timer() -- 將定時器添加到定時器等待隊列中 2007年08月04日 星期六 15:30 用add_timer()函數來看timer_base的做用 static inline void add_timer(struct timer_list *timer) { BUG_ON(timer_pending(timer)); __mod_timer(timer, timer->expires); } int __mod_timer(struct timer_list *timer, unsigned long expires) { tvec_base_t *base, *new_base; unsigned long flags; int ret = 0; timer_stats_timer_set_start_info(timer); BUG_ON(!timer->function); base = lock_timer_base(timer, &flags); 若是timer已經放到定時鏈表中,則釋放開 |--------------------------------| | if (timer_pending(timer)) { -| | detach_timer(timer, 0); -| | ret = 1; | | } | |--------------------------------| 獲取當前CPU的timer base |-----------------------------------------| | new_base = __get_cpu_var(tvec_bases); | |-----------------------------------------| 若是當前CPU的timer base不是當前timer中的base, 更新timer的base |----------------------------------------------------| | if (base != new_base) { | | if (likely(base->running_timer != timer)) { -| | timer->base = NULL; | | spin_unlock(&base->lock); | | base = new_base; | | spin_lock(&base->lock); | | timer->base = base; | | } | | } | |----------------------------------------------------| 給定時器timer設置超時時間;並添加該時鐘 |-------------------------------------| | timer->expires = expires; | | internal_add_timer(base, timer); -| |-------------------------------------| spin_unlock_irqrestore(&base->lock, flags); return ret; } MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>"); MODULE_DESCRIPTION("LED IDE Disk Activity Trigger"); MODULE_LICENSE("GPL"); 總的來講,timer的用法仍是很簡單的。主要須要定義一個timer_list變量timer、先初始化timer init_timer(&timer); then 對timer的相關參數賦值: timer.function = fun; timer.expires = jiffies + TIMER_DELAY; add_timer(&timer); 在定時器時間到的時候,會執行fun,若是繼續定時,能夠經過 在fun中執行 mod_timer(&timer, jiffies + TIMER_DELAY); 在不須要的時候經過調用 del_timer(&timer); 刪除定時器。 簡單吧。這樣一個簡單的定時器就完成了。 呵呵。 附程序: #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/timer.h> #include <asm/atomic.h> #define SECOND_MAJOR 0 static int second_major = SECOND_MAJOR; struct second_dev { struct cdev cdev; atomic_t counter; struct timer_list s_timer; }; struct second_dev *second_devp; static void second_timer_handle(unsigned long arg) { mod_timer(&second_devp->s_timer, jiffies + HZ); atomic_inc(&second_devp->counter); printk(KERN_ERR "current jiffies is %ld/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->counter, 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, }; 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; dev->cdev.ops = &second_fops; err = cdev_add(&dev->cdev, devno, 1); if (err) { printk(KERN_NOTICE "Error %d add second%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_KERNEL); if (!second_devp) { ret = -ENOMEM; goto fail_malloc; } memset(second_devp, 0, sizeof(struct second_dev)); second_setup_cdev(second_devp, 0); return 0; fail_malloc: unregister_chrdev_region(devno, 1); } void second_exit(void) { cdev_del(&second_devp->cdev); kfree(second_devp); unregister_chrdev_region(MKDEV(second_major, 0), 1); } MODULE_AUTHOR("Song Baohua"); MODULE_LICENSE("Dual BSD/GPL"); module_param(second_major, int, S_IRUGO); module_init(second_init); module_exit(second_exit); 附上用戶端的測試程序: #include <stdio.h> #include <unistd.h> #include <fcntl.h> int main(void) { int fd, i; int data; fd = open("/dev/second",O_RDONLY); if (fd < 0) { printf("open /dev/second error/n"); } for(i = 0; i < 20; i++) { read(fd, &data, sizeof(data)); printf("read /dev/second is %d/n",data); sleep(1); } close(fd); }