Linux定时器

1.硬件定时器

一.硬件定时器

特点:
1.一旦上电,硬件定时器周期性的给CPU产生定时器中断信号;
2.给CPU产生中断信号的周期和频率可以通过软件来设置;
3.对应的linux内核定时器中断处理函数也将会被周期性的被内核调用;
此函数由内核已经实现;
4.定时器中断处理函数将会做以下主要工作:
1.更新系统的运行时间;
2.更新实际时间(wall time);
3.检查进程时间片是否用尽,决定进程是否发生调度;
4.检查是否有超时的内核软件定时器,如果有,处理这个超时的软件定时器;
5.更新一些系统资源的统计信息;
等;

源文件:arch/arm/mach-s5pv210/mach-cw210.c
.timer = &s5p_systimer //跟踪


二.内核时间相关的概念

1.1.HZ:
特点:
1.内核常量
2.ARM架构:HZ=100;X86架构:HZ=1000
3.例如HZ=100,表示硬件定时器1秒钟将会给CPU产生100次定时器中断;定时器中断的时间间隔为10ms;
4.例如:
5*HZ:就是表示5秒钟;

1.2.jiffies:
特点:
1.内核全局变量
2.用来记录自开机依赖发生了多少次硬件定时器中断,硬件定时器每发生一次,内核定时器中断处理函数将jiffies加1;
3.一般使用jiffies来记录流失时间,例如:
unsigned long timeout = jiffies + 5*HZ;
说明:
5*HZ:表示5秒钟;
jiffies:表示当前时间
timeout:表示5秒以后的时间

2.Linux内核软件定时器

一.linux内核软件定时器

特点:
1.内核软件定时器指定了一个超时处理函数,一旦定时器到期,内核就会执行此超时处理函数;
2.内核软件定时器基于软中断实现,所以切记对应的超时处理函数千万不能做休眠操作;

数据结构:
struct timer_list {
unsigned long expires;
void (*function)(unsigned long data);
unsigned long data;
...
}; 
成员:
expires:指定定时器的超时时候的时间
例如超时时间为5秒:
expires = jiffies + 5*HZ;
function:超时处理函数,不能休眠,形参data保存传递的参数
data:给超时处理函数传递的参数

二.内核使用定时器的步骤

1.定义初始化定时器对象
struct timer_list timer;
init_timer(&timer);

//其余三个字段单独初始化
timer.expires = jiffies + 5*HZ;
timer.function = mytimer_function;

2.向内核注册,启动定时器
add_timer(&timer);
3.删除定时器
del_timer(&timer);
如果定时器到期,内核会帮你删除定时器;

4.修改定时器
mod_timer(&timer, jiffies + 20*HZ);
此函数的执行顺序:
1.先删除定时器:del_timer
2.再修改超时时候的时间:
expires = jiffies + 20*HZ;
3.最后添加:add_timer(&timer);

5.注意:定时器一旦到期,内核执行它的超时处理函数,但是此函数只执行一次;

涉及头文件:#include <linux/timer.h>


案例:

利用内核定时器,要求每隔2秒打印一句话;

#include <linux/init.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <asm/gpio.h>
#include <plat/gpio-cfg.h>

//声明描述LED硬件相关的数据结构
//定义初始化LED硬件相关的信息

//定义定时器对象
static struct timer_list led_timer;
static int g_data = 0x5555; 

//static int time = 2000;
//module_param(time, int, 0664);

//超时处理函数
//基于软中断实现,千万不能进行休眠操作
static void led_timer_function(unsigned long data)
{
    //data保存传递的参数信息(g_data的首地址)
    printk("%s: data = %#x\n", __func__, *(int *)data);
    
    //如果LED为开,则关;为关,则开;建议不要使用if ... else
    
    //重新添加定时器
    mod_timer(&led_timer, jiffies + msecs_to_jiffies(2000));
    //mod_timer(&led_imter, jiffies + msecs_to_jiffies(time));

    //建议不要采用以下方式重启定时器
    //因为以下代码的执行路径不具备原子性
    //原子性:不可再分,不允许CPU资源发生切换
    //如果要采用,必须加以互斥保护!
    //上锁
    //led_timer.expires = jiffies + msecs_to_jiffies(2000);
    //add_timer(&led_timer);
    //解锁
}

static int led_init(void)
{
    //申请GPIO资源,配置为输出口,输出0
    
    //初始化定时器对象
    init_timer(&led_timer);

    //指定定时器的超时时间
    led_timer.expires = jiffies + msecs_to_jiffies(2000);
    //指定定时器的超时处理函数
    led_timer.function = led_timer_function;
    //给定时器处理函数传递参数,不传递不用写
    led_timer.data = (unsigned long)&g_data;

    //注册和启动定时器,一旦注册完毕,定时器开始倒计时
    //一旦定时器到期,内核执行超时处理函数,并且内核删除定时器
    add_timer(&led_timer);
    printk("入口函数执行完毕,定时器开始倒计时!\n");
    return 0;
}

static void led_exit(void)
{
    //删除定时器
    del_timer(&led_timer);
    //输出0,释放GPIO资源
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");


3.Linux内核延时方法

一.linux内核延时方法

明确:
延时分为忙延时和休眠延时;
如果等待的时间较短采用忙延时(CPU原地空转)
如果等待的时间较长采用休眠延时(当前进程会释放CPU资源给别的任务使用,当前进程进入某个休眠状态中,等待着再次获取CPU资源)

忙延时的函数:

ndelay(纳秒数);//纳秒级延时
例如:ndelay(10); //延时10纳秒

udelay(微秒数);//微秒级延时
例如:udelay(10); //延时10微秒

mdelay(毫秒数);//毫秒级延时
例如:mdelay(1); //延时1毫米
注意:如果延时时间大于10ms,采用休眠延时;

休眠延时的函数:

msleep(毫秒数);//毫秒数休眠延时
例如:msleep(100); //休眠延时100毫秒

ssleep(秒数);//秒级延时
例如:ssleep(1000); //休眠延时1000秒

schedule(); //永久性休眠
schedule_timeout(5*HZ);//休眠延时5秒

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值