在LinuxUWBStack的内核模块实现中,较多的使用了内核定时器,本文基于fakeMCPS实现中的应用为背景,介绍了内核定时器的使用。
1.内核定时器
Linux内核拿来控制在未来某个时间点【基于jiffies(节拍总量)】调度执行某个函数的一种机制,相关函数坐落和文件中。
当内核定时器定时时间抵达时,会步入用户指定的函数,相当于软中断。内核定时器注册开启后,运行一次就不会再运行(相当于手动注销),我们可以重新设置定时器的超时时间,让定时器重复运行。
每每时钟tick中断发生时,全局变量jiffies(一个32位的unsignedlong变量)就加1,因而jiffies记录了linux系统启动后时钟中断发生的次数,驱动程序常借助jiffies来估算不同风波间的时间间隔。内核每秒钟将jiffies变量降低HZ次。因而,对于HZ值为100的系统,jiffy+1等于隔了10ms,而对于HZ为1000的系统linux内核 定时器linux命令行,jiffy+1仅为1ms。
内核定时器使用
首先须要创建一个structtimer_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);
这样linux内核 定时器,定时器在设置的超时时间到以后,运行用户设定的函数(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);
相反的linux 关机命令,也可以将jiffies单位的时间转换为structtimespec的时间单位。
void jiffies_to_timespec(const unsigned long jiffies, struct timespec *value);
struct timespec {
time_t tv_sec; // 秒数
long tv_nsec; // 纳秒数
};
2.内核定时器应用——fakeMCPS实现
在uwbstack的mcps802154_fake.c的fakeMCPS实现代码如下:
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: initn");
// 分配底层硬件驱动
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: Exitn");
// 注销及释放底层硬件驱动
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函数进行注销,删掉早已激活的定时器。