本文主要是介绍lv14 内核定时器 11,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
一、时钟中断
硬件有一个时钟装置,该装置每隔一定时间发出一个时钟中断(称为一次时钟嘀嗒-tick),对应的中断处理程序就将全局变量jiffies_64加1
jiffies_64 是一个全局64位整型, jiffies全局变量为其低32位的全局变量,程序中一般用jiffies
HZ:可配置的宏,表示1秒钟产生的时钟中断次数,一般设为100或200
二、延时机制
-
短延迟:忙等待
1. void ndelay(unsigned long nsecs) 2. void udelay(unsigned long usecs) 3. void mdelay(unsigned long msecs)
-
长延迟:忙等待
使用jiffies比较宏来实现
time_after(a,b) //a > b time_before(a,b) //a < b //返回值为布尔类型。例如 time_after(a,b) 表示时间戳 a 是否晚于时间戳 b//延迟100个jiffies unsigned long delay = jiffies + 100; while(time_before(jiffies,delay)) {; } //延迟2s unsigned long delay = jiffies + 2*HZ; while(time_before(jiffies,delay)) {; }
-
睡眠延迟----阻塞类
void msleep(unsigned int msecs); //深度睡眠unsigned long msleep_interruptible(unsigned int msecs); //浅度睡眠
延时机制的选择原则:
-
异常上下文中只能采用忙等待类
-
任务上下文短延迟采用忙等待类,长延迟采用阻塞类
三、定时器
(1)定义定时器结构体
struct timer_list
{struct list_head entry; //链表管理很多个定时器,每个定时器都有超时值unsigned long expires; // 期望的时间值 jiffies + x * HZvoid (*function)(unsigned long); // 时间到达后,执行的回调函数,软中断异常上下文unsigned long data; //传给回调函数的实参,常转换为地址来使用
};
其中list_head是链表,内核中通过链表管理很多个定时器,每个定时器都有一个超时值。硬件时钟每经过一个时间,都会进行一个计数,同时触发一个软中断,在软中断服务程序中(异常上下文,不能调用任何可能引起阻塞的函数),会进来查看定时器是否超时了,通过现在的tick值与超时tick值进行比较,如果超时了会触发回调函数,其中data用来给回调函数传实参。
(2)初始化定时器
init_timer(struct timer_list *)
(3)增加定时器 ------ 定时器开始计时
void add_timer(struct timer_list *timer);
加入到链表,定时器才开始工作
(4)删除定时器 -------定时器停止工作
int del_timer(struct timer_list * timer);
(5)修改定时器
一般如果想实现每隔x秒钟设置一个闹钟,需要把这个函数放在回调函数的最后。
int mod_timer(struct timer_list *timer, unsigned long expires);
模板
定义struct timer_list tl类型的变量init_timer(...);//模块入口函数//模块入口函数或open或希望定时器开始工作的地方
tl.expires = jiffies + n * HZ //n秒
tl.function = xxx_func;
tl.data = ...;add_timer(....);//不想让定时器继续工作时
del_timer(....);void xxx_func(unsigned long arg)
{......mod_timer(....);//如需要定时器继续隔指定时间再次调用本函数
}
四、课堂练习—秒设备
目标:在应用程序测试读取字符设备时打印秒计数值
second.c,中断定时器不能被多个应用打开,所以使用原子来防止被重复打开
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/wait.h> #include <linux/sched.h> #include <linux/poll.h> #include <asm/uaccess.h> #include <asm/atomic.h>int major = 11; int minor = 0; int mysecond_num = 1;struct mysecond_dev {struct cdev mydev;atomic_t openflag;//1 can open, 0 can not openstruct timer_list timer;int second;};struct mysecond_dev gmydev;void timer_func(unsigned long arg) {struct mysecond_dev * pmydev = (struct mysecond_dev *)arg;pmydev->second++;mod_timer(&pmydev->timer, jiffies + 1 * HZ);}int mysecond_open(struct inode *pnode,struct file *pfile) {struct mysecond_dev *pmydev = NULL;pfile->private_data =(void *) (container_of(pnode->i_cdev,struct mysecond_dev,mydev));pmydev = (struct mysecond_dev *)pfile->private_data;//运算后结果为0则返回真,否则返回假表示设备已经被打开if(atomic_dec_and_test(&pmydev->openflag)){pmydev->timer.expires = jiffies + 1 * HZ;pmydev->timer.function = timer_func;pmydev->timer.data = (unsigned long)pmydev;add_timer(&pmydev->timer);return 0;}else{atomic_inc(&pmydev->openflag);printk("The device is opened already\n");return -1;} }int mysecond_close(struct inode *pnode,struct file *pfile) {struct mysecond_dev *pmydev = (struct mysecond_dev *)pfile->private_data;del_timer(&pmydev->timer);atomic_set(&pmydev->openflag,1);return 0; }int mysecond_read(struct file *pfile,char __user *puser, size_t size, loff_t *p_pos) {struct mysecond_dev *pmydev = (struct mysecond_dev *)pfile->private_data;int ret = 0;if(size < sizeof(int)){printk("the expect read size is invalid\n");return -1;}if(size >= sizeof(int)){size= sizeof(int);}ret = copy_to_user(puser, &pmydev->second, size);if(ret){printk("copy to user failed\n");return -1;}return 0; }struct file_operations myops = {.owner = THIS_MODULE,.open = mysecond_open,.release = mysecond_close,.read = mysecond_read, };int __init mysecond_init(void) {int ret = 0;dev_t devno = MKDEV(major,minor);/*申请设备号*/ret = register_chrdev_region(devno,mysecond_num,"mysecond");if(ret){ret = alloc_chrdev_region(&devno,minor,mysecond_num,"mysecond");if(ret){printk("get devno failed\n");return -1;}major = MAJOR(devno);//容易遗漏,注意}/*给struct cdev对象指定操作函数集*/ cdev_init(&gmydev.mydev,&myops);/*将struct cdev对象添加到内核对应的数据结构里*/gmydev.mydev.owner = THIS_MODULE;cdev_add(&gmydev.mydev,devno,mysecond_num);//初始化置位1atomic_set(&gmydev.openflag,1);//初始化定时器init_timer(&gmydev.timer);; return 0; }void __exit mysecond_exit(void) {dev_t devno = MKDEV(major,minor);cdev_del(&gmydev.mydev);unregister_chrdev_region(devno,mysecond_num); }MODULE_LICENSE("GPL");module_init(mysecond_init); module_exit(mysecond_exit);
Makefile
ifeq ($(KERNELRELEASE),)ifeq ($(ARCH),arm)
KERNELDIR ?= /home/linux/Linux_4412/kernel/linux-3.14
ROOTFS ?= /opt/4412/rootfs
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
endif
PWD := $(shell pwd)modules:$(MAKE) -C $(KERNELDIR) M=$(PWD) modulesmodules_install:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_installclean:rm -rf *.o *.ko .*.cmd *.mod.* modules.order Module.symvers .tmp_versionselseCONFIG_MODULE_SIG=n
obj-m += mychar.o
obj-m += mychar_poll.o
obj-m += openonce_atomic.o
obj-m += openonce_spinlock.o
obj-m += mychar_sema.o
obj-m += mychar_mutex.o
obj-m += second.oendif
testsecond_app.c
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main(int argc, char * argv[])
{int fd = -1;int sec;if(argc < 2){printf("The argument is too few\n");return -1;}//打开字符驱动时,即会启动定时器fd = open(argv[1],O_RDONLY);if(fd < 0){printf("open %s failed \n", argv[1]);return 2;}//延迟sleep(3);read(fd, &sec, sizeof(sec));printf("The second is %d\n",sec);close(fd);fd = -1;return 0;}
编译测试效果
这篇关于lv14 内核定时器 11的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!