lv14 内核定时器 11

2024-01-21 21:36
文章标签 定时器 内核 lv14

本文主要是介绍lv14 内核定时器 11,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、时钟中断

硬件有一个时钟装置,该装置每隔一定时间发出一个时钟中断(称为一次时钟嘀嗒-tick),对应的中断处理程序就将全局变量jiffies_64加1

jiffies_64 是一个全局64位整型, jiffies全局变量为其低32位的全局变量,程序中一般用jiffies

HZ:可配置的宏,表示1秒钟产生的时钟中断次数,一般设为100或200

二、延时机制

  1. 短延迟:忙等待

    1. void ndelay(unsigned long nsecs)
    2. void udelay(unsigned long usecs)
    3. void mdelay(unsigned long msecs)
  2. 长延迟:忙等待

    使用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))
    {;
    }

  3. 睡眠延迟----阻塞类

    void msleep(unsigned int msecs);  //深度睡眠unsigned long msleep_interruptible(unsigned int msecs); //浅度睡眠

延时机制的选择原则:

  1. 异常上下文中只能采用忙等待类

  2. 任务上下文短延迟采用忙等待类,长延迟采用阻塞类

三、定时器

(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的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/630893

相关文章

新特性抢先看! Ubuntu 25.04 Beta 发布:Linux 6.14 内核

《新特性抢先看!Ubuntu25.04Beta发布:Linux6.14内核》Canonical公司近日发布了Ubuntu25.04Beta版,这一版本被赋予了一个活泼的代号——“Plu... Canonical 昨日(3 月 27 日)放出了 Beta 版 Ubuntu 25.04 系统镜像,代号“Pluc

Springboot如何配置Scheduler定时器

《Springboot如何配置Scheduler定时器》:本文主要介绍Springboot如何配置Scheduler定时器问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录Springboot配置Scheduler定时器1.在启动类上添加 @EnableSchedulin

Linux内核之内核裁剪详解

《Linux内核之内核裁剪详解》Linux内核裁剪是通过移除不必要的功能和模块,调整配置参数来优化内核,以满足特定需求,裁剪的方法包括使用配置选项、模块化设计和优化配置参数,图形裁剪工具如makeme... 目录简介一、 裁剪的原因二、裁剪的方法三、图形裁剪工具四、操作说明五、make menuconfig

如何安装HWE内核? Ubuntu安装hwe内核解决硬件太新的问题

《如何安装HWE内核?Ubuntu安装hwe内核解决硬件太新的问题》今天的主角就是hwe内核(hardwareenablementkernel),一般安装的Ubuntu都是初始内核,不能很好地支... 对于追求系统稳定性,又想充分利用最新硬件特性的 Ubuntu 用户来说,HWEXBQgUbdlna(Har

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

笔记整理—内核!启动!—kernel部分(2)从汇编阶段到start_kernel

kernel起始与ENTRY(stext),和uboot一样,都是从汇编阶段开始的,因为对于kernel而言,还没进行栈的维护,所以无法使用c语言。_HEAD定义了后面代码属于段名为.head .text的段。         内核起始部分代码被解压代码调用,前面关于uboot的文章中有提到过(eg:zImage)。uboot启动是无条件的,只要代码的位置对,上电就工作,kern

Ubuntu22.04回退系统内核

文章目录 起因回退操作卸载内核禁止内核升级 起因 最近因为系统内核自动升级,导致显卡驱动检测不到,炼丹环境被破坏。无奈只能重装驱动,于是跟着手册操作发现驱动要求的是内核版本是5.15.0-25-generic,而我通过uname -r发现这时候的内核版本是6.8.0-40-generic,看来只能回退了。 我搜索了网上很多的文章,没有一篇文章能够完全解决这个问题,所以在我多次尝

跟我一起玩《linux内核设计的艺术》第1章(四)——from setup.s to head.s,这回一定让main滚出来!(已解封)

看到书上1.3的大标题,以为马上就要见着main了,其实啊,还早着呢,光看setup.s和head.s的代码量就知道,跟bootsect.s没有可比性,真多……这确实需要包括我在内的大家多一些耐心,相信见着main后,大家的信心和干劲会上一个台阶,加油! 既然上篇已经玩转gdb,接下来的讲解肯定是边调试边分析书上的内容,纯理论讲解其实我并不在行。 setup.s: 目标:争取把setup.

编译linux内核出现 arm-eabi-gcc: error: : No such file or directory

external/e2fsprogs/lib/ext2fs/tdb.c:673:29: warning: comparison between : In function 'max2165_set_params': -。。。。。。。。。。。。。。。。。。 。。。。。。。。。。。。。 。。。。。。。。 host asm: libdvm <= dalvik/vm/mterp/out/Inte