day05
linux中的中断处理
request_irq(irq, 핸들러, 플래그, 이름, dev)
irq, 中断号
핸들러, 处理函数
플래그, IRQF_TRIGGER_RISING
...
IRQF_공유
이름
dev, 调用handler时传递的参数
free_irq(irq, dev)
dev, 和注册时保持一致
裁剪掉内核中自带的按键驱动程序
中断处理函数的特点:
1)工作于中断下文
2)中断处理过程执行速度越快越好,尽快结束
3)在其中不能调用引起阻塞睡眠的函数
4)有独立的栈空间,一般为一个内存页
5)其中不能和用户空间进行数据的交互
希望中断处理尽快结束,但是有些中断处理起来就是耗时
为此内核引起了顶半部、底半部的概念
紧急的工作由顶半部完成,完成就返回被打断的位置继续执行
耗时的、不紧急的工作放在底半部完成
如何登记底半部?
1、登记底半部的方式
1.1 软中断的方式
1.2 tasklet的方式
struct tasklet_struct
{
func
data
...
}
1) 定义并初始化变量
struct tasklet_struct btn_tasklet
void tasklet_init(&btn_tasklet, func, data)
2) 登记底半部
tasklet_schedule(&btn_tasklet);
其特点: tasklet机制是基于软中断机制实现的
tasklet.func工作于中断上下文
也就是要求func中不能调用引起阻塞睡眠的函数
极有可能直接导致内核的崩溃
矛盾: 在底半部中应用需求必须要调用引起睡眠
或者阻塞的函数,怎么办?
工作者队列
1.3 工作者队列登记底半部
struct work_struct
{
work_func_t func;//底半部的工作封装在其中
...
}
struct delayed_work {
struct work_struct work;
struct timer_list timer; //延时时间
};
如何通过该数据结构完成底半部的登记,操作步骤:
1)定义变量
struct work_struct btn_work;
struct delayed_work btn_dwork;
2) 初始化变量
INIT_WORK(&btn_work, btn_work_func)
INIT_DELAYED_WORK(&btn_dwork, btn_dwork_func)
3) 登记底半部
schedule_work(&btn_work)
int schedule_delayed_work(&btn_dwork,unsigned long delay)
delay: 延时的时间
4) 确保登记的底半部被执行或者取消
void flush_scheduled_work(void)
作用:确保默认工作者队列中所有的工作都执行完毕
该函数才返回
bool flush_delayed_work(struct delayed_work *dwork)
作用: 确保指定的dwork被执行完毕才返回
flush_work(struct work_struct *work)
cancel_delayed_work_sync
cancel_work_sync
tasklet和work相比较:
1)work在其中可以调用引起阻塞睡眠函数
2)tasklet登记的底半部得到的响应更及时
tasklet登记的底半部一般会先于
work登记的底半部被执行到
work登记的底半部中为什么就可以调用引起阻塞或者睡眠的函数呢?
work对应的底半部函数是在进程上下文中被调用的
对于delayed_work,第一次登记的底半部如果未被执行
第二次登记无效。
对于tasklet和work的总结:
如果希望底半部得到更为及时的响应,并且
底半部中没有调用引起阻塞睡眠的函数,使用tasklet
如果底半部中需要调用引起阻塞睡眠的函数,使用work
如果希望底半部延时执行,使用delayed_work
注意:对于work的flush动作的理解
1)防止在底半部未执行,而发生模块卸载的动作
2)用于同步
内核中有一个逻辑
{
...
...
需要在你的某个work执行完毕后,
再去执行后序代码
flush_work(&work)
。。。
。。。
}
问题: 谈谈你对中断的理解?
中断是异常的一种
中断是计算机中处理异常事件的重要机制
ARM中中断异常的处理流程
设置按键按下能触发中断异常
中断源
中断控制器
ARM core
异常触发硬件干4件事
异常向量表
跳转真正中断处理程序
保护现场
调用c函数处理中断异常
恢复现场
中断处理函数的特点
1)执行速度越快越好
2)linux系统中的中断服务程序
不能调用引起阻塞睡眠的函数
有独立的栈空间
不能做和用户空间的数据交互
linux中可以将中断分为顶半部和底半部
如何登记底半部
tasklet work
2, 定时器
5초 : 5*HZ
系统时钟中断:
ARM 코어 会不停的接收系统时钟中断信号
아치/팔/mach-s5p6818/cpu.c
.timer = &nxp_cpu_sys_timer,
处理系统时钟中断信号:
更新墙上时间
判断当前线程的时间片是否耗尽,是否重新做任务调度
....
HZ: 1秒钟产生系统时钟中断的次数
当前版本的内核中HZ=1000
jiffies: 32bit无符号整型变量
记录自开机以来经历的系统时钟中断的次数
每次系统时钟中断到来在中断服务程序中
都会jiffies += 1;
4294967295
1s: 1000
1分钟:60000
1小时:3600000
大概不关机50天溢出
진드기: 记录每次系统时钟中断的间隔
틱 = 1/Hz
2.1 内核定时器
软件实现的定时器
struct timer_list
{
...
//定时器的超时时间
unsigned long expires;
//定时时间到后要调用的函数
void (*function)(unsigned long);
//调用function时传递的参数
unsigned long data;
}
定时器如何使用:
1)定义一个timer_list变量
struct timer_list btn_timer;
2) 初始化timer_list变量
init_timer(&btn_timer)
btn_timer.function = xxx_func
btn_timer.expires = ...
btn_timer.data = ...
3) 启动定时器开始计时
add_timer(&btn_timer)
4) 取消定时器
del_timer(&btn_timer)
5) 重新修改定时器的超时时间
int mod_timer(struct timer_list *timer, unsigned long expires)
作用:
1)修改处于计时状态的定时器的超时时间
2)启动定时器
练习1:使用timer_list让LED1 亮1s 灭1s 亮1s ....
练习2:闪烁的时间间隔改成0.5S
0.1s
0.2s
在用户空间调整闪烁的时间间隔
3、内核的竞态与并发
并发:多任务同时执行
对于单核的CPU来说 宏观上并行 微观上串行
共享资源:文件 硬件设备 共享内存 内核中的全局变量
linux内核中产生竞态的原因:
1)SMP 对称多处理器(多核)
都要操作LCD
2) 进程之间的抢·共享资源
3)进程和中断之间发生共享资源的抢…
LCD
网卡
可见的内存 (文件 共享内存 全局变量)
4)中断和中断之间的资源抢…
中断是有优先级的
linux内核中解决竞争状态的策略(LDD3)
1)中断屏蔽
2) 原子操作
3)자유로운
4)신용량
以串口的用为例:
串口设备同一时刻只能被一个进程所使用
问:该策略的实现是由应用程序保证还是
由串口的驱动程序保证?
回调函数
函数的可重入性
最明显的标志:该函数是否使用了全局变量
최대 정수 = 100;
정수 테스트(정수 값)
{
최대 = 값;
if(최대 >10)
...
또 다른
...
}
test 函数不具有可重入性
同一个进程内的两个线程
답:
...
테스트(5);
线程:
...
테스트(200);
Reference
이 문제에 관하여(day05), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/moonxu0722/day05-47gb텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)