除了上一篇文章提到的一些方法,内核还提供了定时器API。其相关内容定义在<linux/timer.h>和kernel/time/timer.c中。
内核定时器可以在将来的某个时间点调度执行某个动作,同时在该点到达之前不会阻塞当前进程。它可以在未来的某个特定时间点(基于时钟滴答)调度执行某个函数,从而可用于完成许多任务。例如,在硬件无法产生中断时,可以周期性的轮询设备状态。
之前提到的schedule_timeout内部就使用了内核定时器的API实现。
其具体数据结构如下:
struct timer_list {
/*
* All fields that change during normal runtime grouped to the
* same cacheline
*/
struct list_head entry;
/* 超时的时间
* 在当前时间大于此时间值时执行function
* 如, jiffies + n*HZ
*/
unsigned long expires;
struct tvec_base *base;
/* 超时后执行的函数 */
void (*function)(unsigned long);
/* function的参数 */
unsigned long data;
int slack;
#ifdef CONFIG_TIMER_STATS
int start_pid;
void *start_site;
char start_comm[16];
#endif
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
定时器中调度运行的函数,其运行方式类似于硬件中断发生时的情景。实际上,内核定时器常常是作为"软件中断"的结构而运行的。在这种原子性的上下文中运行时,代码会受到许多的限制。
诸如:
- 不允许访问用户空间。因为没有进程上下文,无法将任何特定进程与用户空间关联起来;
- current指针在原子模式下是没有任何意义的,也是不可用的,因为相关代码和被中断的进程没有任何关联;
- 不能执行休眠和调度。原子代码不可以调用schedule或者wait_event,也不能调用任何可能引起休眠的函数。例如,kmalloc(..., GFP_KERNEL)就不符合本规则。信号量也不能用,因为可能引起休眠。
内核代码可以通过调用函数in_interrupt()来判断自己是否运行于中断上下文,该函数无需参数,如果处理器运行在中断上下文就返回非零值,而无论是硬件中断还是软件中断。
和in_interrupt()相关的函数是in_atomic()。当调度不被允许时,后者的返回值也是非零值;调度不被允许的情况包括硬件和软件中断上下文以及拥有自旋锁的任何时间点。在后一种情况下,current是可用的,但禁止访问用户空间,因为这会导致调度的发生。不管何时使用in_interrupt(),都应该考虑是否真正该使用的是in_atomic()。
定时器的常用API:
动态定义一个定时器:
#define init_timer(timer) \
__init_timer((timer), 0)
静态创建定时器:
#define DEFINE_TIMER(_name, _function, _expires, _data) \
struct timer_list _name = \
TIMER_INITIALIZER(_function, _expires, _data)
往系统中添加一个定时器:
extern void add_timer(struct timer_list *timer);
修改定时器的超时时间为expires:
extern int mod_timer(struct timer_list *timer, unsigned long expires);
查询定时器当前是否处于等待状态,如果在系统的定时器列表中则返回1,否则返回0:
/**
* timer_pending - is a timer pending?
* @timer: the timer in question
*
* timer_pending will tell whether a given timer is currently pending,
* or not. Callers must ensure serialization wrt. other operations done
* to this timer, eg. interrupt contexts, or other CPUs on SMP.
*
* return value: 1 if the timer is pending, 0 if not.
*/
static inline int timer_pending(const struct timer_list * timer)
{
return timer->entry.next != NULL;
}
从系统中删除定时器:
extern int del_timer(struct timer_list * timer);
一个循环调度自身的例子:
#include <linux/timer.h>
struct timer_list my_timer;
init_timer(my_timer);
my_timer.expire = jiffies + n * HZ;
my_timer.function = timer_func;
my_timer.data = func_param;
add_timer(&my_timer);
static void timer_func(unsigned long func_param) {
/* Do work */
/* ... */
init_timer(my_timer);
my_timer.expire = jiffies + n * HZ;
my_timer.function = timer_func;
my_timer.data = func_param;
add_timer(&my_timer);
}