ServidoresLinux

ServidoresLinux.com
Linux服务器网——精选每一篇高品质的技术干货
  1. 首页
  2. 开源快讯
  3. 正文

Linux中定时器超时处理函数的基本知识点解析

2023年6月23日 362点热度

1、综述

基于硬件工程师的提出的一个测试需求:每隔5秒钟拉高PA的使能脚,间隔5秒钟再拉低PA使能脚(这儿的PA指的是poweramplifier->即功率放大器的意思)

换句话说,就是播放音乐时,每隔5秒钟有声音,5秒钟没声音。

学习本文你会把握以下知识点:

1.linux中定时器的概念和使用

2.linux工作队列的概念和使用

3.怎样使能PA脚

4.定时器和工作队列在linux中的实际运用

5.需求实现

2、基本知识一、linux中定时器的概念和使用

Linux内核中,假如想要周期性的做一件事情,或则在某个特定的时间点去做一件事,例如每过5秒让闪光灯亮一下等,应当怎样办呢?

Linux给我们提供了timer_list(内核定时器)来实现相应的功能。

timer_list结构体:(路径:kernel-3.18/include/linux/timer.h)

timer.h

包含的主要成员:

a.data:传递到超时处理函数的参数linux 定时器程序linux开发培训,主要在多个定时器同时使用时,区别是那个timer超时。

b.expires:定时器超时的时间,以linux的jiffies来评判。

c.void(*function)(unsignedlong):定时器超时处理函数。

1.相关API函数

a. init_timer(struct timer_list*):定时器初始化函数;
b. add_timer(struct timer_list*):往系统添加定时器;
c. mod_timer(struct timer_list *, unsigned long jiffier_timerout):
修改定时器的超时时间为jiffies_timerout;
(Linux系统中的jiffies类似于Windows里面的TickCount,
它是定义在内核里面的一个全局变量,
只是它的单位并不是秒或是毫秒。
通常是250个jiffies为一秒,在内核里面可以直接使用宏定义:HZ
)
d. timer_pending(struct timer_list *):定时器状态查询,
如果在系统的定时器列表中则返回1,否则返回0;
e. del_timer(struct timer_list*):删除定时器。

2.相关API函数源码解析

a)init_timer函数

a)init_timer函数(路径: kernel-3.18/include/linux/timer.h)
#define init_timer(timer) 
__init_timer((timer), 0)
#define __init_timer(_timer, _flags)
init_timer_key((_timer), (_flags), NULL, NULL)
可以看出 实际上是调用init_timer_key()函数去初始化
(路径: kernel-3.18/kernel/time/timer.c)
void init_timer_key(struct timer_list *timer, unsigned int flags,  
            const char *name, struct lock_class_key *key)
{
    debug_init(timer);
    do_init_timer(timer, flags, name, key);
}

*注意:必须先调用init_timer_key()进行初始化,之后能够调用其他跟定时器相关的方式,比如add_timer,mod_timer等

b)add_timer函数

b)add_timer函数(路径: kernel-3.18/kernel/time/timer.c)
void add_timer(struct timer_list *timer)
{
    BUG_ON(timer_pending(timer));//打印相关log
    mod_timer(timer, timer->expires);//调用mod_timer设置时间
}

剖析:add_timer用于往系统中添加一个定时器,参数timer为要添加的定时器(timer_list)

,当系统时间经过timer->expires那么多时间,都会去调用timer->function反弹方式去完成相应的任务。

注意:必须先初始化timer->expires,timer->function,timer->data这三个成员变量linux 定时器程序,能够调用add_timer()这个技巧

c)mod_timer函数

c)mod_timer函数(路径: kernel-3.18/kernel/time/timer.c)
int mod_timer(struct timer_list *timer, unsigned long expires)
{
    expires = apply_slack(timer, expires);
    if (timer_pending(timer) && timer->expires == expires)
        return 1;
    return __mod_timer(timer, expires, false, TIMER_NOT_PINNED);
}

剖析:mod_timer()是更新活动计时器过期数组(即expires参数)的一种更有效的方式(假如计时器没有被激活,mod_timer会先激活计时器,之后在重新设定超时时间)

实际上调用mod_timer(timer,expires)相当于

del_timer(timer);timer->expires=expires;add_timer(timer);

注意:倘若有多个未序列化的并发用户使用相同的计时器,则mod_timer()是更改超时的惟一安全方式,由于add_timer()不能更改早已运行的计时器。

该函数返回是否早已更改了一个待定定时器

假如调用mod_timer去更改一个定时器,

假如当前定时器处于非激活状态,则该函数返回0,

假如当前定时器处于激活状态,则该函数返回1

Ps:调用了add_timer(),就表示该定时器处于激活状态

d)add_timer函数

d)add_timer函数 (路径: kernel-3.18/include/linux/timer.h)
static inline int timer_pending(const struct timer_list * timer)   
{
    return timer->entry.next != NULL;
}

timer_pending会告诉给定的计时器是否正在等待

返回值:假如计时器挂起,则为1;倘若不是,则为0

假如timer->entry.next为NULL,表示计时器没有挂起linux课程,返回0

假如timer->entry.next不等于NULL,表示计时器挂起,返回1

e)del_timer函数

e)del_timer函数 (路径: kernel-3.18/kernel/time/timer.c)
int del_timer(struct timer_list *timer)
{
    struct tvec_base *base;
    unsigned long flags;
    int ret = 0;
    debug_assert_init(timer);
    timer_stats_timer_clear_start_info(timer);
    if (timer_pending(timer)) {
        base = lock_timer_base(timer, &flags);
        ret = detach_if_pending(timer, base, true);
        spin_unlock_irqrestore(&base->lock, flags);
    }
    return ret;
}

剖析:del_timer()禁用计时器——这对激活的和非激活的计时器都有效。

函数返回是否早已禁用了一个待定定时器。

(即。一个非激活计时器的del_timer()返回0,激活计时器返回1)

3.使用定时器的通常流程为:

实例:
static struct timer_list  timer;//定义计时器
/*回调函数*/
static void miki_test_callback(unsigned long a)
{
    //这里添加相应的逻辑,比如每隔5秒让闪关灯亮一次等
}
//初始化相关参数
static void miki_init(void)
{
    init_timer(&timer);//先初始化timer
    test_timer.expires = jiffies + (20 * HZ);//设置超时 20*HZ 表示20秒
    test_timer.function = &miki_test_callback;//设置回调函数
    test_timer.data = ((unsigned long)0);//设置data参数,一般传入0即可
    add_timer(&test_timer);//把定时器添加到系统中,激活定时器
}
/*主函数*/
void mian()
{
    miki_init();
    //如果需要修改定时器的时间,则调用mod_timer
mod_timer(&test_timer, jiffies + (10 * HZ));
}

二、linux中工作队列(workqueue)的概念和使用

1.哪些是workqueue(工作队列)

Linux中的Workqueue机制就是为了简化内核线程的创建。通过调用workqueue的插口能够创建内核线程。而且可以按照当前系统CPU的个数创建线程的数目,致使线程处理的事务才能并行化。workqueue是内核中实现简单而有效的机制,他似乎简化了内核daemon的创建,便捷了用户的编程.

工作队列(workqueue)是另外一种将工作推后执行的方式.工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部份可以在进程上下文中执行。最重要的就是工作队列容许被重新调度甚至是睡眠

2.相关数据结构

Linux中的Workqueue机制就是为了简化内核线程的创建。通过调用workqueue的插口能够创建内

我们把推后执行的任务称作工作(work),描述它的数据结构为work_struct

路径:kernel-3.18/include/linux/workqueue.h

workqueue.h

这种工作以队列结构组织成工作队列(workqueue),其数据结构为workqueue_struct:

路径:kernel-3.18/include/linux/workqueue.h

workqueue_struct

3.相关API(插口)函数:

路径:kernel-3.18/kernel/workqueue.c

路径:kernel-3.18/include/linux/workqueue.h

1) create_workqueue(name)
用于创建一个workqueue队列,为系统中的每个CPU都创建一个内核线程。
输入参数:@name:workqueue的名称
2) create_singlethread_workqueue(name)
用于创建workqueue,只创建一个内核线程。输入参数:
输入参数:@name:workqueue名称
 
3)destroy_workqueue(struct workqueue_struct *wq)
释放workqueue队列。输入参数:
输入参数:@ workqueue_struct:需要释放的workqueue队列指针
4) schedule_work(struct work_struct *work);
调度执行一个具体的任务
输入参数:
@ work_struct:具体任务对象指针
5) schedule_delayed_work(struct delayed_work *dwork, unsigned long delay)
延迟一定时间去执行一个具体的任务,功能与schedule_work类似,多了一个延迟时间,
输入参数:
@work_struct:具体任务对象指针
@delay:延迟时间
6)queue_work(struct workqueue_struct *wq, struct work_struct *work)
调度执行一个指定workqueue中的任务。
输入参数:
@ workqueue_struct:指定的workqueue指针
@work_struct:具体任务对象指针
7)queue_delayed_work(struct workqueue_struct *wq,
                           struct delayed_work *dwork, unsigned long delay)
延迟调度执行一个指定workqueue中的任务,
功能与queue_work类似,输入参数多了一个delay。 

4使用工作队列的通常流程为:

实例:
static struct workqueue_struct *miki_test_wq;//声明工作队列
static struct work_struct miki_test_work;;//声明工作
/*工作处理函数*/
static void miki_test_work_callback ()
{
    //这里添加相应的逻辑,比如每隔5秒让闪关灯亮一次等
}
/*主函数*/
void mian()
{
    //创建自己的工作队列
miki_test_wq = create_singlethread_workqueue("miki_test");
//初始化工作,实际上是让工作work绑定工作处理函数miki_test_work_callback
INIT_WORK(&miki_test_work, miki_test_work_callback);
    //调度执行一个指定(miki_test_wq)中的任务miki_test_work
queue_work(miki_test_wq, &miki_test_work); 
}

三.怎样使能PA脚

定时喷香器设置_linux 脚本定时执行_linux 定时器程序

在mt_soc_codec_mt63xx.c中Ext_Speaker_Amp_Change函数中进行外部PA的gpio控制就可以。

路径:
kernel-3.18/sound/soc/mediatek/mt_soc_audio_v3/mt_soc_codec_63xx.c

为此,

使能PA:Ext_Speaker_Amp_Change(true)

关掉PA:Ext_Speaker_Amp_Change(false)

提示:关于mt_soc_codec_63xx.c文件的路径

【7.0】

kernel-3.18/sound/soc/mediatek/mt_soc_audio_v3/mt_soc_codec_63xx.c

7.0out目录

【8.0】

kernel-3.18/sound/soc/mediatek/mt6735/mt_soc_codec_63xx.c

可以通过编译生成的out目录来查看系统编译了什么文件

8.0out目录

四.定时器和工作队列在linux中的实际运用

kernel-3.18/drivers/misc/mediatek/accdet/mt6580/accdet.c

在麦克风驱动中,可以看见定时器和工作队列的使用

剖析:设置了一个定时器micbias_timer,设置时间为6秒,最后调用mod_timer去激活定时器,6秒后会手动调用disable_micbias函数

创建了一个名称为accdet工作队列,

为accdet_work设置反弹方式accdet_work_callback

我们晓得工作是要调用queue_work()这个方式把工作递交到工作队列中,就会反弹

accdet_work_callback方式去完成相应的任务,这么在那里调用了该方式呢?

这就要去看disable_micbias函数了

小结:定时器micbias_timer每隔6秒钟才会去调用queue_work方式,告诉系统,你要去调用accdet_work_callback方式去完成相应的任务accdet_work_callback函数->主要用于测量而且设置麦克风的状态

五、需求实现

依葫芦画瓢,我们可以模仿在麦克风驱动中,Linux的使用定时器和工作队列的方法去完成这个需求。

步骤一:声明变量
static struct timer_list  test_timer;//定义定时器
static struct workqueue_struct *miki_test_wq;//定义工作
static struct work_struct miki_test_work;//定义工作队列
步骤二:编写回调方法
//工作队列的回调方法
static void miki_test_work_callback(struct work_struct *work)
{
   Ext_Speaker_Amp_Change(true);//打开PA
   msleep(5 * 1000);//休眠5秒
   Ext_Speaker_Amp_Change(false);//关闭PA
   mod_timer(&test_timer, jiffies + (5* HZ));//重新激活定时器
}
//定时器的回调方法
static void miki_test_callback(unsigned long a)
{
    queue_work(miki_test_wq, &miki_test_work);
}
步骤三:初始化定时器和工作队列
static void miki_init (void)
{
    miki_test_wq = create_singlethread_workqueue("miki_test");
    INIT_WORK(&miki_test_work, miki_test_work_callback);
    init_timer(&test_timer);
    test_timer.expires = jiffies + (5 * HZ);//时间设置为5秒
    test_timer.function = &miki_test_callback;//设置定时器回调方法
    test_timer.data = ((unsigned long)0);
    mod_timer(&test_timer, test_timer.expires);//激活定时器
}
步骤四:在模块入口函数中调用miki_init()方法
static int __init mtk_mt6331_codec_init(void)->模块入口函数
{
    //省略部分源码
    miki_init();
    return platform_driver_register(&mtk_codec_6331_driver);
}

到此,本文就结束了,希望有所收获,lol开启,嘻嘻(#^.^#)。

Stayhungry,Stayfoolish!

荆轲刺秦王

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: 内核 定时 计时 调用 队列
最后更新:2023年6月23日

Linux服务器网

每日更新,欢迎收藏♥ 不积跬步无以至千里,加油,共勉。

点赞
< 上一篇
下一篇 >

Linux服务器网

每日更新,欢迎收藏♥
不积跬步无以至千里,加油,共勉。

最新 热点 随机
最新 热点 随机
AI智能模型生成,DV型SSL证书不支持真实性 通过命令行在Linux系统中运行C和C++程序 如何查看Node.js版本的三种方法查看 编程语言之C++程序文件验证已安装的工具 Linux下修改文件创建时间的时间记录和使用方法介绍 互亿无线SSL证书申请平台无线提供高性价比护航 如何查看Linux内核版本的命令?(3种方式) Win/Ubuntu双系统安装图形界面的过程及注意事项 如何查看Node.js版本号的版本更新频繁的技巧 小编经验分享:Linux下bin文件的打开方法详解 常用的Linux系统版本的方法,简单易懂且实用 Linux系统默认乱码的原因及解决方法!! 《Node.js》如何查看版本号Node Windows和Linux系统之间的文件名乱码问题有哪些? macOS/Linux用户首选CLion+Rust插件,折腾VSCode收益太低 Linux的桌面操作系统适合新手吗? Linux内核加密文件系统公布加入加密功能 工信部:加大力度支持Linux的国产操作系统研发和应用 Linux中如何查看内存使用情况?命令是什么? (Linux基础知识)linux下的路径Linux中的写法
虚拟机网络适配器中没有虚拟网卡的解决方法通过UI和命令行,您可以摆脱Ubuntu上这些应用程序非抱歉,我无法提供完整的红帽Linux安装手册ubuntu读取u盘命令 你可以尝试以下方法来解决UbuntuU盘权限的问题linux下刻录光盘 GHO和ISO镜像的特点和适用范围你知道多少?Xfce4基于GTK+2工具包开发的轻量级桌面环境Linux操作系统的主要特点开放性和开放源代码软件微软官方发布指南:安装Linux的步骤和步骤介绍!在Linux上安装安卓x86Oreo系统的x86架构移植准备Linux发行版如何集成显卡驱动?Nvidia闭源驱动是怎么做的?旧的安卓手机不要扔,可以拿来做一个微型的linux服务器(STM32)字符设备驱动框架:hello驱动基础篇4102你可以在fstab里变更系统目录成根目5261录吧Linux内核源码/内存调优/文件系统/进程管理/设备驱动(初中英语)home目录的恢复和恢复Linux是个什么样的系统?Linux系统安装详系教程,centOS7支持Linux容器安装如果您想要使用GNUGRUB自动检测光盘并启动,您需要在GRUB配置文件中添加一些设置2016年上海事业单位医疗招聘:Linux安装CD的内容CMDebug编程调试命令增强版25中文版CMD编程
压缩要传输的文件(建议使用tar.gz/tgz) (每日一题)Linux内核和发行版本号的命名 Linux下DVD创建一个目录/fcy3.添加如下 解压命令tar-linux-gcc-4.4.3-C/(自动解压到系统根目录下路径) 搜狗输入法输入汉字时候选栏乱码 清华源为例安装Linux虚拟机 Linux下最常用的C/C++编译器 Linux上的一种解决办法及进入救援模式 科技创新破解受制于人的困境,操作系统被预留“后门” Linux系统的版本升级到RedHatEnterpriseEnterprise7.4/CentOS7.4 【】一系列神秘的命令和命令 Linux系统上创建用户并设置口令的重要性及对策 常用命令整理:显示全部信息这样将输出所有的信息 Linux查看端口占用情况可以使用lsof和netstat命令的命令 移动公证录音专业版-最好用的录音机及电话录音软件系列软件 Linux最常用的命令之一演示效果:cd 虚拟四级缓存可有效地降低内存访问的延迟,Intel平台服务器 游戏 linux 除SteamOS以外为游戏而生的众多Linux发行版 Linux的文件系统LinuxI/O的基本组成和组成 Unix网络编程卷2:进程间通信标准库
标签聚合
命令 软件 文件 linux系统 内核 linux服务器 应用 文件目录 操作 虚拟机
书籍
课程
技术群
技术干货大合集↓
  • 2023年11月 / 82篇
  • 2023年10月 / 93篇
  • 2023年9月 / 90篇
  • 2023年8月 / 93篇
  • 2023年7月 / 93篇
  • 2023年6月 / 90篇
  • 2023年5月 / 92篇
  • 2023年4月 / 90篇
  • 2023年3月 / 126篇
  • 2023年2月 / 84篇
  • 2023年1月 / 161篇
  • 2022年12月 / 186篇
  • 2022年11月 / 77篇
友情链接:

Linux书籍 | Linux命令 | Linux系统 | RHCE红帽认证 | Linux软件 | Linux教程 | CentOS系统 | Linux内核 | Linux服务器 | Linux大神 | IT资源

COPYRIGHT © 2024 ServidoresLinux.Com ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang