【嵌入式环境下linux内核及驱动学习笔记-(9-内核定时器)】

news/2024/11/28 6:41:51/

目录

  • 1、时钟tick中断等概念
  • 2、延时机制
    • 2.1 短延时(忙等待类--非阻塞害)
      • 2.1.1 ndelay 忙等待延迟多少纳秒
      • 2.1.2 udelay 忙等待延迟多少微秒
      • 2.1.3 mdelay 忙等待延迟多少毫秒
    • 2.2 长延迟:忙等待 (非阻塞类)
      • 2.2.1 time_after 忙等待延迟到
      • 2.2.2 time_before 忙等待延迟到
    • 2.3 阻塞类睡眠延迟
      • 2.3.1 msleep 深睡
      • 2.3.2 msleep_interruptible 浅睡
  • 3、定时器
    • 3.1 定义定时器结构体
    • 3.2 init_timer 初始化定时器
    • 3.3 add_timer 加入链表
    • 3.4 del_timer 删除定时器
    • 3.5 mod_timer 修改定时器
    • 3.6 定时器的使用框架
  • 4 、实例
    • 4.1 计数器驱动代码
    • 4.2 以下是应用层程序
    • 4.3 测试过程

内核在运行中,需要处理两种时间应用,一个是延时,一个是定时。这些都要依赖于精确的tick中断来实现。

内核定时器机制主要包含两部分:

1、 定时器列表:内核维护一个定时器列表,用于保存所有激活的定时器。每个定时器都关联一个过期时间,定时器按过期时间排序在列表中。

2、定时器处理线程:内核有一个独立的线程运行定时器处理函数timer_expire_work_func()。这个函数不断从定时器列表头部取出已过期的定时器,调用定时器关联的回调函数。

1、时钟tick中断等概念

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

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

所以可以把jiffies看成是一个时间戳计数器

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

2、延时机制

按时间工短分为:短延时与长延时

按延时的方式分为:阻塞类延时和非阻塞类延时

延时机制的选择原则:

  1. 异常上下文中只能采用忙等待类
  2. 任务上下文短延迟采用忙等待类,长延迟采用阻塞类

2.1 短延时(忙等待类–非阻塞害)

2.1.1 ndelay 忙等待延迟多少纳秒

void ndelay(unsigned long nsecs) 延迟多少纳秒

2.1.2 udelay 忙等待延迟多少微秒

void udelay(unsigned long usecs) 延迟多少微秒

2.1.3 mdelay 忙等待延迟多少毫秒

void mdelay(unsigned long msecs) 延迟多少毫秒

2.2 长延迟:忙等待 (非阻塞类)

使用jiffies比较宏来实现

2.2.1 time_after 忙等待延迟到

time_after(a,b) //a > b

2.2.2 time_before 忙等待延迟到

time_before(a,b) //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))
{
;
}

2.3 阻塞类睡眠延迟

2.3.1 msleep 深睡

void msleep(unsigned int msecs);

2.3.2 msleep_interruptible 浅睡

unsigned long msleep_interruptible(unsigned int msecs);

3、定时器

3.1 定义定时器结构体

linux 3.14
#include <linux/timer.h>  struct timer_list 
{struct list_head entry;unsigned long expires;           // 期望的时间值   当前jiffies + x * HZ  做为超时时间void (*function)(unsigned long); // 时间到达后,执行的回调函数,该函数处于软中断异常上下文,因此该回调函数内不允许有阻塞操作unsigned long data;      //传给回调的参数
};
linux 4.15
struct timer_list {/** All fields that change during normal runtime grouped to the* same cacheline*/struct hlist_node	entry;unsigned long		expires;void			(*function)(struct timer_list *);u32			flags;#ifdef CONFIG_LOCKDEPstruct lockdep_map	lockdep_map;
#endif
};

这个函数完成了定时器结构体的初始化工作:

  • 将 entry.pprev 字段置为空,以标识定时器未插入任何链表。
  • 设置 function 字段为传入的回调函数 callback。
  • 设置 flags 字段为传入的定时器标志 flags。它会检查 flags 值的合法性。
  • 可选地设置 lockdep_map 字段用于锁验证,这需要 CONFIG_LOCKDEP 选项开启。
  • 不会设置 expires 字段,这需要在添加定时器时或手动设置。
    所以,这个函数将在静态初始化时为一个定时器结构体配置好回调函数和标志,以备随后添加使用。调用者只需要设置 expires 字段,就可以使用该定时器了。

3.2 init_timer 初始化定时器

init_timer(struct timer_list *)

注意:如果是linux4.15版以后,用的是timer_setup函数源码

static inline void timer_setup(struct timer_list *timer, void (*callback)(struct timer_list *), unsigned int flags)
{/* * check for invalid bits, the rest is stored as-is * Note that TIMER_DUMMY is silently ignored, so there is no* way to select it here.*/BUILD_BUG_ON(flags & ~TIMER_ADVANCED_BITS|TIMER_DEFERRED);timer->entry.pprev = NULL;timer->function = callback;timer->flags = flags;
#ifdef CONFIG_LOCKDEPtimer->lockdep_map = 0;
#endif
}

3.3 add_timer 加入链表

增加定时器到链表中------从而使该 定时器开始计时
void add_timer(struct timer_list *timer);

3.4 del_timer 删除定时器

删除定时器 -------定时器停止工作
int del_timer(struct timer_list * timer);

3.5 mod_timer 修改定时器

int mod_timer(struct timer_list *timer, unsigned long expires);
需要每次定时器到时后,调用这个函数重新设置定时器的expires参数。使定时器再次定时

3.6 定时器的使用框架

1、定义struct timer_list tl类型的变量
2、定义定时器回调函数
void xxx_func(unsigned long arg)
{

mod_timer(…);//如需要定时器继续隔指定时间再次调用本函数
}

3、在模块入口函数处初始化定时器
init_timer(…);//模块入口函数
如果是linux4.15以上版本,则用timer_setup()函数初始化

4、在模块入口函数或open或希望定时器开始工作的地方写如下代码
tl.expires = jiffies + n * HZ //n秒
tl.function = xxx_func;
tl.data = …;

add_timer(…);

5、不想让定时器继续工作时
del_timer(…);

4 、实例

这里做一个定时器驱动。该驱动会启动一个定时器,每一秒计一次数。另外,从应用层有一个程序,间隔去读这个驱动的计数器并显示出来:

4.1 计数器驱动代码

/*************************************************************************> File Name: timer-second.c   ************************************************************************/#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/timer.h>/*1、定义重要的变量及结构体*/
struct x_dev_t {struct cdev  my_dev;  //cdev设备描述结构体变量atomic_t have_open;   //记录驱动是否被打开的原子变量struct timer_list tlist;  //定时器结构体unsigned int second;    //计数器变量
};struct x_dev_t *pcdev;/*所有驱动函数声明*/
ssize_t read (struct file *, char __user *, size_t, loff_t *);
int open (struct inode *, struct file *);
int release (struct inode *, struct file *);
//驱动操作函数结构体,成员函数为需要实现的设备操作函数指针
//简单版的模版里,只写了open与release两个操作函数。
struct file_operations fops={.owner = THIS_MODULE,.open = open,.release = release,.read = read,
};//定时器的回调函数linux4.15版
void timer_back(struct timer_list *data){pcdev->second ++;//printk("timer_back second %d\n",pcdev->second);data->expires = jiffies + HZ;mod_timer(data , jiffies+HZ);//add_timer(data);
}static int __init my_init(void){int unsucc =0;dev_t devno;int major,minor;pcdev = kzalloc(sizeof(struct x_dev_t), GFP_KERNEL);/*2、创建 devno */unsucc = alloc_chrdev_region(&devno , 0 , 1 , "timer-second");if (unsucc){printk(" creating devno  faild\n");return -1;}major = MAJOR(devno);minor = MINOR(devno);printk("devno major = %d ; minor = %d;\n",major , minor);/*3、初始化 cdev结构体,并将cdev结构体与file_operations结构体关联起来*//*这样在内核中就有了设备描述的结构体cdev,以及设备操作函数的调用集合file_operations结构体*/cdev_init(&pcdev->my_dev , &fops);pcdev->my_dev.owner = THIS_MODULE;/*4、注册cdev结构体到内核链表中*/unsucc = cdev_add(&pcdev->my_dev,devno,1);if (unsucc){printk("cdev add faild \n");return 1;}//初始化原子量have_open为1atomic_set(&pcdev->have_open,1);printk("the driver timer-second initalization completed\n");return 0;
}
static void  __exit my_exit(void)
{cdev_del(&pcdev->my_dev);unregister_chrdev_region(pcdev->my_dev.dev , 1);printk("***************the driver timer-second  exit************\n");
}/*5、驱动函数的实现*/
/*file_operations结构全成员函数.open的具体实现*/
int open(struct inode *pnode , struct file *pf){struct x_dev_t *p = container_of(pnode->i_cdev,struct x_dev_t , my_dev);pf->private_data = (void *)p;//在open函数中对原子量have_open进行减1并检测。=0,允许打开文件,<0则不允许打开if (atomic_dec_and_test(&p->have_open)){printk("timer-second is opened\n");//初始化定时器并启动p->second = 0;timer_setup(&p->tlist , timer_back , 0);p->tlist.expires = jiffies + HZ;add_timer(&p->tlist);return 0;}else{printk("device timer-second can't be opened again\n");atomic_inc(&p->have_open);//原子量=-1,记得这里要把原子量加回到0return -1;}   
}
/*file_operations结构全成员函数.release的具体实现*/
int release(struct inode *pnode , struct file *pf){struct x_dev_t *p = (struct x_dev_t *)pf->private_data;printk("timer-second is closed \n");atomic_set(&p->have_open,1);del_timer(&p->tlist);return 0;
}
ssize_t  read (struct file * pf,  char __user * buf, size_t size ,  loff_t * pops){struct x_dev_t *p = pf->private_data;printk("driver: now timer is %d 's\n",p->second);return p->second;
}module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("");

4.2 以下是应用层程序

/*************************************************************************> File Name: test.c************************************************************************/#include<stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>int main (int argc , char **argv){int fd0,fd1;if (argc <2){printf("argument is too less\n");return -1;}else{fd0 = open(argv[1] , O_RDONLY );while (fd0){printf("read timer-second is %d\n",read(fd0 , NULL,0));sleep(2);}}close(fd0);return 0;}

4.3 测试过程

sudo insmod timer-second.ko

用dmesg显示,设备号为244 0

sudo mknod /dev/timer-second c 244 0
sudo chmod 777 /dev/timer-second
执行测试程序: ./test.elf /dev/timer-second

结果:每2秒读一次设备,显示驱动的计数值。


http://www.ppmy.cn/news/61829.html

相关文章

ChatGPT :国内免费可用 ChatGPT +Midjourney绘图

前言 ChatGPT&#xff08;全名&#xff1a;Chat Generative Pre-trained Transformer&#xff09;&#xff0c;美国OpenAI 研发的聊天机器人程序 &#xff0c;于2022年11月30日发布 。ChatGPT是人工智能技术驱动的自然语言处理工具&#xff0c;它能够通过理解和学习人类的语言来…

UFC718AE01 HIEE300936R0101什么是 ABB 分布式控制系统?

​ UFC718AE01 HIEE300936R0101什么是 ABB 分布式控制系统&#xff1f; 关于 ABB 类别 什么是 ABB 分布式控制系统&#xff1f; ABB 的分布式控制系统 (DCS) 旨在改变多方面、全天候 24 小时的工业流程。ABB 的控制架构持续分析和推动工厂生产力&#xff0c;最大限度地提高资产…

HTMLCSS

1、HTML 1.1 介绍 HTML 是一门语言&#xff0c;所有的网页都是用HTML 这门语言编写出来的&#xff0c;也就是HTML是用来写网页的&#xff0c;像京东&#xff0c;12306等网站有很多网页。 这些都是网页展示出来的效果。而HTML也有专业的解释 HTML(HyperText Markup Language)…

java版本工程项目管理系统平台源码,助力工程企业实现数字化管理

鸿鹄工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离构建工程项目管理系统 1. 项目背景 一、随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性&#xff0c;公司对内部工程管…

信息安全等级保护的基本概念,信息安全等级划分,等级保护测评,实施流程,所需材料

信息安全等级保护的基本概念 信息安全等级保护是国家信息安全保障的基本制度 网络安全等级保护是指对网络&#xff08;含信息系统、数据&#xff09;实施分等级保护、分等级监管。 信息系统安全等级测评是验证信息系统是否满足相应安全保护等级的评估过程。信息安全等级保护…

【自然语言处理 | Transformer】Transformer:Attention is All You Need论文讲解

Transformer由论文《Attention is All You Need》提出&#xff1a; 论文地址为&#xff1a; https://arxiv.org/pdf/1706.03762.pdf文章目录 一、Transformer 整体结构二、Transformer 的输入2.1 单词 Embedding2.2 位置 Embedding 三、Self-Attention&#xff08;自注意力机制…

2023年4月《中国数据库行业分析报告》正式发布(含精彩内容概览)

为了帮助大家及时了解中国数据库行业发展现状、梳理当前数据库市场环境和产品生态等情况&#xff0c;从2022年4月起&#xff0c;墨天轮社区行业分析研究团队出品将持续每月为大家推出最新《中国数据库行业分析报告》&#xff0c;持续传播数据技术知识、努力促进技术创新与行业生…

2.sql server数据表的管理(实验报告)

目录 一﹑实验目的 二﹑实验平台 三﹑实验内容和步骤 四﹑命令(代码)清单 五﹑运行结果 一﹑实验目的 掌握使用SQL Server管理平台和Transact-SQL语句Create table和Alter table创建和修改表的方法&#xff1b;掌握在SQL Server管理平台中对表进行插入、修改和删除数据操作…