核心定時器的使用
阿新 • • 發佈:2019-02-13
1. 包含的標頭檔案:linux/timer.h
2. 資料型別:struct timer_list;
包含的主要成員:
a. data:傳遞到超時處理函式的引數,主要在多個定時器同時使用時,區別是哪個timer超時。
b. expires:定時器超時的時間,以linux的jiffies來衡量。
c. void (*function)(unsigned long):定時器超時處理函式。
- 1
- 2
- 3
- 4
- 5
3. 主要相關的API函式:
a. init_timer(struct timer_list*):定時器初始化函式; b. add_timer(struct timer_list*):往系統新增定時器; c. mod_timer(struct timer_list *, unsigned long jiffier_timerout):修改定時器的超時時間為jiffies_timerout; d. timer_pending(struct timer_list *):定時器狀態查詢,如果在系統的定時器列表中則返回1,否則返回0; e. del_timer(struct timer_list*):刪除定時器。
- 1
- 2
- 3
- 4
- 5
- 6
4. 時間與jiffies的轉換函式:
Linux系統中的jiffies類似於Windows裡面的TickCount,它是定義在核心裡面的一個全域性變數,只是它的單位並不是秒或是毫秒。通常是250個jiffies為一秒,在核心裡面可以直接使用巨集定義:HZ。這裡有幾個時間和jiffies的相互轉換函式: unsigned int jiffies_to_msecs(unsigned long); unsigned int jiffies_to_usecs(unsigned long); unsigned long msecs_to_jiffies(unsigned int); unsigned long usecs_to_jiffies(unsigned int);
- 1
- 2
- 3
- 4
- 5
- 6
5. 使用簡例:
步驟:init_timer->[timer.expires=? & timer.function=?]->add_timer->[mod_timer]->del_timer.
- 1
- 2
#include <linux/init.h>
#include <linux/module.h>
#include <linux/timer.h>
struct timer_list timer;
void timer_handler(unsigned long data) {
printk(KERN_INFO"timer pending:%d \n", timer_pending(&timer));
mod_timer(&timer, jiffies+msecs_to_jiffies(1000));
printk(KERN_INFO"jiffies:%ld, data:%ld\n", jiffies, data);
}
int timer_init(void) {
printk(KERN_INFO"%s jiffies:%ld\n", __func__, jiffies);
printk(KERN_INFO"ji:%d,HZ:%d\n", jiffies_to_msecs(250), HZ);
init_timer(&timer);
timer.data = 45;
timer.function = timer_handler;
timer.expires = jiffies + HZ;
add_timer(&timer);
printk(KERN_INFO"timer pending:%d\n", timer_pending(&timer));
return 0;
}
void timer_exit(void) {
printk(KERN_INFO"%s jiffies:%ld\n", __func__, jiffies);
del_timer(&timer);
}
module_init(timer_init);
module_exit(timer_exit);
MODULE_LICENSE("GPL");
核心定時器的資料結構 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 <[email protected]>"); 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(