内核定时器

 在内核中,如果我们想在将来的某个时间点调度执行某个动作,同时在该时间点到达之前不会阻塞当前进程,那么内核定时器是个不错的选择。例如,一些周期性的操作等等。
那么到底什么是定时器呢,要实现一个定时器,都需要哪些操作?
内核定时器是一个数据结构,它告诉内核在一个用户定义的时间点,使用用户定义的参数来执行一个用户定义的函数。其数据结构是这样的:
#include 
struct timer_list {
    struct list_head entry;//内核使用
    unsigned long expires;//超时的jiffies值
    void (*function)(unsigned long);//超时处理函数
    unsigned long data;//超时处理函数参数
    struct tvec_t_base_s *base;//内核使用
#ifdef CONFIG_TIMER_STATS
    void *start_site;
    char start_comm[16];
    int start_pid;
#endif
};
其中,expires是表示定时器执行是的时间值(即jiffies值);在到达该jiffies时间的时候,函数function将被调用,并传递data参数。
当然,如果要传递的参数不止一个的时候,可以将参数捆绑成一个数据结构,然后将该数据结构的指针强制转换成unsigned long传入。
定时器timer的基本操作也比较直观。可能读者已经猜到一二了,初始化、加入到定时器链表、在必要的时候(比如卸载模块)delet掉就行了。
1. 初始化时应该首先调用init_timer初始化除expires、function、data之外的参数,然后再调用宏TIMER_INITIALIZER来初始化这三个参数:
void fastcall init_timer(struct timer_list *timer)
{
    timer->entry.next = NULL;
    timer->base = __raw_get_cpu_var(tvec_bases);
#ifdef CONFIG_TIMER_STATS
    timer->start_site = NULL;
    timer->start_pid = -1;
    memset(timer->start_comm, 0, TASK_COMM_LEN);
#endif
}
#define TIMER_INITIALIZER(_function, _expires, _data) {        \
        .function = (_function),            \
        .expires = (_expires),                \
        .data = (_data),                \
        .base = &boot_tvec_bases,            \
    }
另外一个宏DEFINE_TIMER也可以用来初始化
#define DEFINE_TIMER(_name, _function, _expires, _data)        \
    struct timer_list _name =                \
        TIMER_INITIALIZER(_function, _expires, _data)
2. 加载定时器
extern void add_timer(struct timer_list *timer);
3. 删除定时器
extern int del_timer(struct timer_list * timer);
当然,针对定时器还有一些其他的函数:
extern int mod_timer(struct timer_list *timer, unsigned long expires);更新一个定时器的到期时间
extern void add_timer_on(struct timer_list *timer, int cpu);将定时器加载到指定的cpu上,用于对称多处理器(smp)
static inline int timer_pending(const struct timer_list * timer)返回定时器是否正在被调度运行
还有很多函数,具体参见linux/timer.h
下面给出一个实例,在2.6.24内核测试通过:
#include 
#include 
#include 
#include 
#include 
static void timer_handler (unsigned long data)
{
    printk(KERN_ALERT "The jiffies in timer_handler is %lu\n",jiffies);
        printk(KERN_ALERT "timer_handler is running.\n");
}
DEFINE_TIMER(my_timer,timer_handler,HZ,0);
static int __init test_init(void)
{
    printk(KERN_ALERT "The jiffies is %lu\n",jiffies);
    add_timer(&my_timer);
        return 0;
}
static  void __exit test_exit(void)
{
    del_timer(&my_timer);
        printk(KERN_ALERT "test_exit is running.\n");
}
MODULE_LICENSE("GPL");
module_init(test_init);
module_exit(test_exit);
测试结果:
The jiffies is 2425862
The jiffies in timer_handler is 2425863
timer_handler is running.
test_exit is running.
最后,还有几点关于内核定时器的说明:
  • 内核定时器通常作为“软中断”在中断上下文执行,所以不能访问用户空间、此时current指针没有任何意义、不能调度和休眠。可用in_interrupt()来判断是否正在运行在中断上下文。
  • 在SMP系统中,定时器函数会由注册它的同一CPU执行。
  • 内核定时器也可以将自己注册以在稍后的时间重新运行。
  • 定时器也可能是竞态的来源,也就是说定时器也可能导致并发与竞争。

其实中断中的tasklet机制很类似于内核定时器。
关于tasklet和工作队列,参见
http://www.kerneltravel.net/?p=143
http://www.kerneltravel.net/?p=257
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值