Chinaunix首页 | 论坛 | 博客
  • 博客访问: 259363
  • 博文数量: 74
  • 博客积分: 1470
  • 博客等级: 上尉
  • 技术积分: 793
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-25 21:01
文章分类

全部博文(74)

文章存档

2011年(1)

2010年(32)

2009年(32)

2008年(9)

我的朋友

分类: LINUX

2009-03-11 14:11:20

在内核中,如果我们想在将来的某个时间点调度执行某个动作,同时在该时间点到达之前不会阻塞当前进程,那么内核定时器是个不错的选择。例如,一些周期性的操作等等。
那么到底什么是定时器呢,要实现一个定时器,都需要哪些操作?
内核定时器是一个数据结构,它告诉内核在一个用户定义的时间点,使用用户定义的参数来执行一个用户定义的函数。其数据结构是这样的:
#include
struct timer_list {
struct list_head entry;

   unsigned long expires;
    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和工作队列,参见


阅读(903) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~