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;

linux内核定时器实现_定时器linux_linux内核 定时器

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的时间单位。

linux内核 定时器_linux内核定时器实现_定时器linux

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;
}

定时器linux_linux内核 定时器_linux内核定时器实现

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函数进行注销,删掉早已激活的定时器。

Tagged:
Author

这篇优质的内容由TA贡献而来

刘遄

《Linux就该这么学》书籍作者,RHCA认证架构师,教育学(计算机专业硕士)。

发表回复