linux内核模块编程(二)----timer定时器

先给自己打个广告,本人的微信公众号正式上线了,搜索:张笑生的地盘,主要关注嵌入式软件开发,足球等等,希望大家多多关注,有问题可以直接留言给我,一定尽心尽力回答大家的问题
在这里插入图片描述

一 why

一般地,在我们嵌入式软件开发中,使用定时器的目的是为了实现周期性地执行某项工作;同样地,linux内核也实现了一种定时器,用于实现内核周期性执行某项工作。

二 what

linux内核实现的这种定时器,称之为动态定时器,是内核用来控制在未来某个时间点(基于jiffies)调度执行某个函数的一种机制,它的实现位于Linux/timer.h 和 kernel/timer.c 文件中。
被调度的函数是异步执行的,类似于一种“软件中断”,处于非进程的上下文中,所以这个被调度函数应该遵循如下规则

1. 没有 current 指针,不允许访问用户空间。因为没有进程上下文,相关代码和被中断的进程没有任何联系。
2. 不能执行休眠(或可能引起休眠的函数)和调度。
3. 任何被访问的数据结构都应该针对并发访问进行保护,以防止竞争条件。
4. 被调度函数运行过一次后就不会再被运行了(相当于自动注销),但可以通过在被调度的函数中重新调度自己来周期运行。

2.1 内核定时器的数据结构

struct timer_list {
    struct list_head entry;  // 当一个定时器被注册到内核之后,entry 字段用来连接该定时器到一个内核链表中
    unsigned long expires;   // 表示期望定时器执行的 jiffies 值,到达该 jiffies 值时,将调用 function 函数
    void (*function)(unsigned long);
    unsigned long data; 
    struct tvec_base *base; //内核内部实现所用的
    /* ... */
};

2.2 初始化
由于linux内核版本的不同,关于初始化部分的实现有不同的函数,低版本的用init_timer,高版本是timer_setup

#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
    init_timer(&dev->getIntrTimer);
    dev->getIntrTimer.data = (unsigned long) dev;
    dev->getIntrTimer.function = GetIntrTimerCallback;
    /* ... */
    add_timer(&dev->getIntrTimer);
#else
    timer_setup(&dev->getIntrTimer, GetIntrTimerCallback, 0);
    /* the third argument may include TIMER_* flags */
    /* ... */
#endif

比如当前我的linux版本是4.15,所以在我的实验中,使用的就是timer_setup
2.3 注册
初始化定时器成功之后,我们需要注册该定时器,函数为

add_timer

2.4 修改
定时运行过程中的定时器周期修改函数为

mod_timer

前面我们知道,当定时周期来到时,会执行被调函数,如果我们此时不修改定时器到期时间,则接下来就不会在执行被调函数了,我们可以在被调函数中修改定时器到期时间,实现周期性执行被调函数。

三 how

定时器内核编程示例

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/jiffies.h>


static struct timer_list test_timer;

static void irq_test_timer_function(struct timer_list  *timer)
{
	printk("22 again in irq_test_timer_function\n");
	mod_timer(&test_timer, jiffies + 2 * HZ);
}

static int timer_drv_init(void)
{
	test_timer.expires  = jiffies + 2 * HZ;
	timer_setup(&test_timer, irq_test_timer_function, 0);
	
	add_timer(&test_timer);
	
	printk(KERN_INFO "already init and add timer\n");
	
	return 0;
}

static void timer_drv_exit(void)
{
	printk(KERN_INFO "exit timer drv\n");
	del_timer(&test_timer);
}

module_init(timer_drv_init);
module_exit(timer_drv_exit);
MODULE_LICENSE("GPL");

Makefile文件

obj-m:=test_timer.o

KDIR:= /lib/modules/$(shell uname -r)/build
PWD:= $(shell pwd)

all:
	$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
	$(MAKE) -C $(KDIR) M=$(PWD) clean
	

四 test

1. 上传到Ubuntu系统
2. 编译 make
3. 加载 sudo insmod test_timer.ko
4. 查看运行情况 dmesg | tail
5. 卸载 sudo rmmod test_timer

加载时打印
在这里插入图片描述
卸载时打印
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章