在Linux UWB Stack的内核模块实现中,较多的使用了内核定时器,本文基于fake MCPS实现中的应用为背景,介绍了内核定时器的使用。
Linux内核用来控制在未来某个时间点【基于jiffies(节拍总数)】调度执行某个函数的一种机制,相关函数位于
当内核定时器定时时间到达时,会进入用户指定的函数,相当于软中断。内核定时器注册开启后,运行一次就不会再运行(相当于自动注销),我们可以重新设置定时器的超时时间,让定时器重复运行。
每当时钟tick中断发生时,全局变量jiffies(一个32位的unsigned long 变量)就加1,因此jiffies记录了linux系统启动后时钟中断发生的次数,驱动程序常利用jiffies来计算不同事件间的时间间隔。内核每秒钟将jiffies变量增加HZ次。因此,对于HZ值为100的系统,jiffy+1等于隔了10ms,而对于HZ为1000的系统,jiffy+1仅为1ms。
首先需要创建一个struct timer_list
实例,并通过timer_setup设置定时器的超时处理函数,并启动。
static struct timer_list g_timer;
void timer_setup(struct timer_list *timer, void (*callback)(struct timer_list *), unsigned int flags);
timer_setup
函数的实现相对比较简单:
void timer_setup(struct timer_list *timer, void (*callback)(struct timer_list *), unsigned int flags)
{
memset(timer, 0, sizeof(*timer));
INIT_LIST_HEAD(&timer->entry);
timer->function = callback;
timer->flags = flags;
}
2)定时器设置好之后,配置超时时间启动定时器
// 设置定时器的超时时间,在全局jiffies变量基础上增加,jiffies与系统设置的HZ相关
void mod_timer(struct timer_list *timer, unsigned long expires);
这样,定时器在设置的超时时间到之后,运行用户设定的函数(callback),若要保证周期性的运行,则可以在周期函数中,重复设置定时器的超时时间。
void del_timer(struct timer_list * timer);
del_timer 函数用于删除一个已经激活的定时器。其中,timer 是一个指向定时器结构体的指针。如果定时器已经被取消或者还未被激活,则不会有任何操作。在删除定时器之后,定时器的定时操作也会被取消。
//将输入的微秒时间转换为jiffies单位
unsigned long usecs_to_jiffies(const unsigned int m);
//将输入的毫秒时间转换为jiffies单位
unsigned long msecs_to_jiffies(const unsigned int m);
相反的,也可以将jiffies单位的时间转换为struct timespec
的时间单位。
void jiffies_to_timespec(const unsigned long jiffies, struct timespec *value);
struct timespec {
time_t tv_sec; // 秒数
long tv_nsec; // 纳秒数
};
在uwb stack的mcps802154_fake.c
的fake MCPS实现代码如下:
static struct timer_list g_timer;
// 1s = 1000 ms
static const int g_time_interval = 1000;
// 周期任务,在每次任务中,重新设定定时器的超时时间
static void periodic_task(struct timer_list *unused)
{
// ...
/*Restarting the timer...*/
mod_timer(&g_timer, jiffies + msecs_to_jiffies(g_time_interval));
if (rx_enabled) {
rx_enabled = false;
mcps802154_rx_frame(driver_llhw);
}
if (tx_queued) {
tx_queued = false;
mcps802154_tx_done(driver_llhw);
}
}
static int __init fake_init(void)
{
int r;
pr_info("fake_mcps: init\n");
// 分配底层硬件驱动
driver_llhw = mcps802154_alloc_llhw(0, &fake_ops);
if (driver_llhw == NULL) {
return -ENOMEM;
}
// ...
// mcps802154注册底层硬件驱动
r = mcps802154_register_llhw(driver_llhw);
if (r) {
mcps802154_free_llhw(driver_llhw);
driver_llhw = NULL;
return r;
}
// 设置定时器,启动周期定时器
timer_setup(&g_timer, periodic_task, 0);
mod_timer(&g_timer, jiffies + msecs_to_jiffies(g_time_interval));
return 0;
}
static void __exit fake_exit(void)
{
pr_info("fake_mcps: Exit\n");
// 注销及释放底层硬件驱动
mcps802154_unregister_llhw(driver_llhw);
mcps802154_free_llhw(driver_llhw);
driver_llhw = NULL;
/*Deleting the timer aka the periodic task.*/
stop_timer = true;
del_timer(&g_timer);
}
module_init(fake_init);
module_exit(fake_exit);
通过fake_init完成必要的mcps802154相关的注册,同时设置内核定时器,周期性的启动模拟的收发操作。
模块退出时,通过fake_exit函数进行注销,删除已经激活的定时器。