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秒
221

被折叠的 条评论
为什么被折叠?



