day07
来源:互联网 发布:阿里云服务器搭建免流 编辑:程序博客网 时间:2024/05/29 05:03
回顾:
面试题:谈谈对中断的理解
1.谈为什么有中断
2.中断的硬件触发流程
画图
3.中断的软件编程
画图
4.linux内核中断编程
5.linux内核对中断处理函数的要求
6.linux内核中断编程的顶半部和底半部机制
7.顶半部特点
8.底半部特点
9.底半部实现方式
tasklet特点
工作队列特点
软中断特点
2.linux内核软件定时器
硬件定时器特点
硬件定时器的中断处理函数
HZ/jiffies
struct timer_list
.expires
.function
.data
配套函数
init_timer/add_timer/del_timer/mod_timer
软件定时器基于软中断实现,不能进行休眠操作
3.linux内核并发和竞态
3.1.掌握笔记中的两个经典案例
3.2.概念
并发
竞态
共享资源
临界区
互斥访问
执行路径具有原子性
3.3.竞态引起异常的四种情形
多核
同一CPU上的进程与进程的抢占
中断和进程
中断和中断
画图
3.4.解决竞态引起异常的四种方法
中断屏蔽
自旋锁
信号量
原子操作
4.解决竞态引起异常的方法之中断屏蔽
中断屏蔽特点:
能够解决进程与进程之前的抢占引起的异常(进程之前的抢占本身基于软中断实现)
能够解决中断和进程的抢占引起的异常
能够解决中断和中断引起的异常
无法解决多核引起的异常
中断屏蔽保护的临界区的代码执行速度要越快越好,更不能进行休眠操作
因为linux系统的很多机制跟中断密切相关(tasklet,软件定时器,硬件定时器等)
长时间的屏蔽中断非常危险
利用中断屏蔽解决竞态引起的异常的编程步骤:
1.明确驱动代码中哪些是共享资源
2.明确驱动代码中哪些是临界区
3.明确保护的临界区中是否有休眠操作
如果有休眠操作,势必不考虑中断屏蔽
如果没有,并且没有多核参与竞态,可以考虑使用中断屏蔽
4.访问临界区之前屏蔽中断
unsigned long flags;
local_irq_save(flags);//屏蔽中断,保存中断标志到flags(内核来完成)
5.任务踏踏实实的访问临界区
此时会有其他进程的抢占吗?没有
此时会有其他的中断来打断吗?没有
6.访问临界区之后,记得将中断进行恢复
local_irq_restore(flags);//恢复中断
7.屏蔽中断和恢复中断务必要逻辑上成对使用
案例:利用中断屏蔽来解决之前案例1中的代码漏洞
实施步骤:同上
案例:利用中断屏蔽解决LCD显示屏的问题
void lcd_config(void) {
unsigned long flags;
local_irq_save(flags);
//临界区
gpio_set_value(..., 1);
mdelay(5);
gpio_set_value(..., 0);
mdelay(5);
local_irq_restore(flags);
}
5.解决竞态引起异常的方法之自旋锁
自旋锁特点:
自旋锁能够解决多核引起的竞态问题
自旋锁能够解决进程与进程之前的抢占引起的竞态问题
自旋锁无法解决中断引起的竞态问题
自旋锁保护的临界区的代码执行速度要快,更不能进行休眠操作
没有获取自旋锁的任务将会原地忙等待(原地空转)
linux内核描述自旋锁的数据类型:spinlock_t
利用自旋锁解决竞态引起异常的编程步骤:
1.明确驱动代码中哪些是临界区
2.明确驱动代码中哪些是共享资源
3.明确临界区中是否有休眠操作
如果有,势必不考虑此方法
如果没有,还要考虑是否有中断参与
如果有中断参与,势必不考虑
如果没有中断参与,可以考虑使用
4.访问临界区之前先获取自旋锁
//定义初始化一个自旋锁对象
spinlock_t lock; //定义
spin_lock_init(&lock); //初始化
spin_lock(&lock);//获取自旋锁,如果获取成功,立马返回即可访问临界区
如果获取失败,任务将在此函数中进行循环忙等待
直到持有自旋锁的任务释放自旋锁
5.任务获取自旋锁以后,即可踏踏实实的访问临界区
6.访问临界区之后,记得要释放自旋锁
spin_unlock(&lock);
7.注意:获取自旋锁和释放自旋锁务必在逻辑上成对使用
案例:利用自旋锁来解决案例1中的代码漏洞
实验步骤同上
案例:利用中断屏蔽解决LCD显示屏的问题
void lcd_config(void) {
spin_lock(&lock);
//临界区
gpio_set_value(..., 1);
mdelay(5);
gpio_set_value(..., 0);
mdelay(5);
spin_unlock(&lock);
}
//如果用自旋锁进行保护,发现LCD显示照样有问题,拿示波器抓取
波形发现周期同样超过10ms,说明引起问题的原因在于中断参与抢占
CPU资源
6.解决竞态引起异常的方法之衍生自旋锁
衍生自旋锁特点:
衍生自旋锁能够解决所有的竞态引起的异常问题
衍生自旋锁=屏蔽中断+自旋锁
衍生自旋锁保护的临界区的代码执行速度要快,更不能进行休眠操作
没有获取衍生自旋锁的任务将会原地忙等待(原地空转)
linux内核描述衍生自旋锁的数据类型:spinlock_t
利用衍生自旋锁解决竞态引起异常的编程步骤:
1.明确驱动代码中哪些是临界区
2.明确驱动代码中哪些是共享资源
3.明确临界区中是否有休眠操作
如果有,势必不考虑此方法
如果没有,可以考虑使用
4.访问临界区之前先获取自旋锁
//定义初始化一个衍生自旋锁对象
spinlock_t lock; //定义
spin_lock_init(&lock); //初始化
unsigned long flags;
spin_lock_irqsave(&lock, flags);//屏蔽中断,获取衍生自旋锁,如果获取成功,立马返回即可访问临界区
如果获取失败,任务将在此函数中进行循环忙等待
直到持有自旋锁的任务释放自旋锁
5.任务获取衍生自旋锁以后,即可踏踏实实的访问临界区
6.访问临界区之后,记得要释放衍生自旋锁,恢复中断
spin_unlock_irqrestore(&lock);
7.注意:获取衍生自旋锁和释放衍生自旋锁务必在逻辑上成对使用
案例:利用衍生自旋锁来解决案例1中的代码漏洞
实验步骤同上
案例:利用中断屏蔽解决LCD显示屏的问题
void lcd_config(void) {
unsigned long flags
spin_lock_irqsave(&lock, flags);
//临界区
gpio_set_value(..., 1);
mdelay(5);
gpio_set_value(..., 0);
mdelay(5);
spin_unlock_irqrestore(&lock,flags);
}
利用衍生自旋锁能够解决异常问题!
7.解决竞态引起异常的方法之信号量
内核信号量和用户的信号量一模一样
信号量特点:
信号量又称睡眠锁,基于自旋锁实现的
信号量就是解决自旋锁保护的临界区不能休眠问题,有些场合
临界区中需要进行休眠操作,此时此刻只能用信号量
"休眠操作"仅仅存在于进程的世界中,进程休眠是指
当前进程会释放占用的CPU资源给他们进程使用,信号量仅用于进程
如果进程获取信号量,在访问临界区时,是可以进行休眠操作
如果进程获取信号量失败,那么进程将进行休眠操作
linux内核描述信号量的数据结构:struct semaphore
利用信号量来解决竞态引起异常的编程步骤:
1.明确驱动代码中哪些是共享资源
2.明确驱动代码中哪些是临界区
3.明确临界区中是否有休眠
如果有,必须使用信号量
如果没有,可以考虑使用信号量
4.访问临界区之前,先获取信号量
//定义初始化信号量对象
struct seamphore sema; //定义信号量对象
sema_init(&sema, 1); //初始化信号量对象
//获取信号量
down(&sema);
说明:获取信号量,如果获取信号量成功,进程从此函数中立马返回
然后可以踏踏实实的访问临界区
如果获取信号量失败,进程将进入此函数中进入不可中断的
休眠状态(释放CPU资源,在休眠期间接收到信号不会立即处理信号)
直到持有信号量的进程释放了信号量并且唤醒这个休眠的等待进程
"不可中断的休眠状态":进程在休眠期间,如果接收到了一个kill信号
进程不会立即处理接收到的信号,而是获取信号量的
任务释放信号量以后唤醒这个休眠的进程,进程一旦
被唤醒以后会处理之前接收到的信号
“可中断的休眠状态”:进程在休眠期间,如果接收到了一个信号
进程会被立即唤醒并且处理接收到的信号
或者
down_interruptible(&sema);//获取信号量,如果获取信号量成功,进程从此函数中立马
返回,然后去访问临界区
如果获取信号量失败,进程将进入可中断的休眠状态
(休眠期间会立即处理接收到的信号)
直到获取信号被唤醒或者持有信号量的任务
释放信号量唤醒之前休眠的进程
5.一旦获取信号量成功,进程可以踏踏实实的访问临界区
6.访问临界区之后,记得释放信号量并且唤醒休眠的进程
up(&sema);
7.获取信号量和释放信号量一定要在逻辑上成对使用
案例:利用信号量down,实现一个设备只能被打开一次
上位机实施步骤:同上
下位机测试步骤:
cd /home/drivers/
insmod led_drv.ko
./led_test & //启动A进程,A进程打开成功,A进程调用sleep进行休眠
不关闭
./led_test & //启动B进程,B进程获取信号量失败,进入不可中断的休眠状态
等待A进程来唤醒
ps //查看A,B的PID
top //查看A,B的进程状态
S:进程进入可中断的休眠状态
D:进程进入不可中断的休眠状态
按Q键退出top命令
kill B的PID //给B进程发送kill信号
ps
top
kill A的PID //杀死A进程,A进程关闭设备,释放信号量并且唤醒
B进程,B进程一旦被唤醒,B还要去处理之前接收到
的信号
ps
top
./led_test & //启动A进程,A进程打开成功,A进程调用sleep进行休眠
不关闭
./led_test & //启动B进程,B进程获取信号量失败,进入不可中断的休眠状态
等待A进程来唤醒
ps //查看A,B的PID
top //查看A,B的进程状态
S:进程进入可中断的休眠状态
D:进程进入不可中断的休眠状态
按Q键退出top命令
kill A的PID //给A进程发送kill信号
ps
top //查看B进程的状态是否还是D,不是,变成了S,因为应用程序调用sleep
kill B的PID
问题:内核吐核问题:
INFO: task led_test:1032 blocked for more than 10 seconds.
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message
解决办法:下位机执行:
echo 0 > /proc/sys/kernel/hung_task_timeout_secs
面试题:谈谈对中断的理解
1.谈为什么有中断
2.中断的硬件触发流程
画图
3.中断的软件编程
画图
4.linux内核中断编程
5.linux内核对中断处理函数的要求
6.linux内核中断编程的顶半部和底半部机制
7.顶半部特点
8.底半部特点
9.底半部实现方式
tasklet特点
工作队列特点
软中断特点
2.linux内核软件定时器
硬件定时器特点
硬件定时器的中断处理函数
HZ/jiffies
struct timer_list
.expires
.function
.data
配套函数
init_timer/add_timer/del_timer/mod_timer
软件定时器基于软中断实现,不能进行休眠操作
3.linux内核并发和竞态
3.1.掌握笔记中的两个经典案例
3.2.概念
并发
竞态
共享资源
临界区
互斥访问
执行路径具有原子性
3.3.竞态引起异常的四种情形
多核
同一CPU上的进程与进程的抢占
中断和进程
中断和中断
画图
3.4.解决竞态引起异常的四种方法
中断屏蔽
自旋锁
信号量
原子操作
4.解决竞态引起异常的方法之中断屏蔽
中断屏蔽特点:
能够解决进程与进程之前的抢占引起的异常(进程之前的抢占本身基于软中断实现)
能够解决中断和进程的抢占引起的异常
能够解决中断和中断引起的异常
无法解决多核引起的异常
中断屏蔽保护的临界区的代码执行速度要越快越好,更不能进行休眠操作
因为linux系统的很多机制跟中断密切相关(tasklet,软件定时器,硬件定时器等)
长时间的屏蔽中断非常危险
利用中断屏蔽解决竞态引起的异常的编程步骤:
1.明确驱动代码中哪些是共享资源
2.明确驱动代码中哪些是临界区
3.明确保护的临界区中是否有休眠操作
如果有休眠操作,势必不考虑中断屏蔽
如果没有,并且没有多核参与竞态,可以考虑使用中断屏蔽
4.访问临界区之前屏蔽中断
unsigned long flags;
local_irq_save(flags);//屏蔽中断,保存中断标志到flags(内核来完成)
5.任务踏踏实实的访问临界区
此时会有其他进程的抢占吗?没有
此时会有其他的中断来打断吗?没有
6.访问临界区之后,记得将中断进行恢复
local_irq_restore(flags);//恢复中断
7.屏蔽中断和恢复中断务必要逻辑上成对使用
案例:利用中断屏蔽来解决之前案例1中的代码漏洞
实施步骤:同上
案例:利用中断屏蔽解决LCD显示屏的问题
void lcd_config(void) {
unsigned long flags;
local_irq_save(flags);
//临界区
gpio_set_value(..., 1);
mdelay(5);
gpio_set_value(..., 0);
mdelay(5);
local_irq_restore(flags);
}
5.解决竞态引起异常的方法之自旋锁
自旋锁特点:
自旋锁能够解决多核引起的竞态问题
自旋锁能够解决进程与进程之前的抢占引起的竞态问题
自旋锁无法解决中断引起的竞态问题
自旋锁保护的临界区的代码执行速度要快,更不能进行休眠操作
没有获取自旋锁的任务将会原地忙等待(原地空转)
linux内核描述自旋锁的数据类型:spinlock_t
利用自旋锁解决竞态引起异常的编程步骤:
1.明确驱动代码中哪些是临界区
2.明确驱动代码中哪些是共享资源
3.明确临界区中是否有休眠操作
如果有,势必不考虑此方法
如果没有,还要考虑是否有中断参与
如果有中断参与,势必不考虑
如果没有中断参与,可以考虑使用
4.访问临界区之前先获取自旋锁
//定义初始化一个自旋锁对象
spinlock_t lock; //定义
spin_lock_init(&lock); //初始化
spin_lock(&lock);//获取自旋锁,如果获取成功,立马返回即可访问临界区
如果获取失败,任务将在此函数中进行循环忙等待
直到持有自旋锁的任务释放自旋锁
5.任务获取自旋锁以后,即可踏踏实实的访问临界区
6.访问临界区之后,记得要释放自旋锁
spin_unlock(&lock);
7.注意:获取自旋锁和释放自旋锁务必在逻辑上成对使用
案例:利用自旋锁来解决案例1中的代码漏洞
实验步骤同上
案例:利用中断屏蔽解决LCD显示屏的问题
void lcd_config(void) {
spin_lock(&lock);
//临界区
gpio_set_value(..., 1);
mdelay(5);
gpio_set_value(..., 0);
mdelay(5);
spin_unlock(&lock);
}
//如果用自旋锁进行保护,发现LCD显示照样有问题,拿示波器抓取
波形发现周期同样超过10ms,说明引起问题的原因在于中断参与抢占
CPU资源
6.解决竞态引起异常的方法之衍生自旋锁
衍生自旋锁特点:
衍生自旋锁能够解决所有的竞态引起的异常问题
衍生自旋锁=屏蔽中断+自旋锁
衍生自旋锁保护的临界区的代码执行速度要快,更不能进行休眠操作
没有获取衍生自旋锁的任务将会原地忙等待(原地空转)
linux内核描述衍生自旋锁的数据类型:spinlock_t
利用衍生自旋锁解决竞态引起异常的编程步骤:
1.明确驱动代码中哪些是临界区
2.明确驱动代码中哪些是共享资源
3.明确临界区中是否有休眠操作
如果有,势必不考虑此方法
如果没有,可以考虑使用
4.访问临界区之前先获取自旋锁
//定义初始化一个衍生自旋锁对象
spinlock_t lock; //定义
spin_lock_init(&lock); //初始化
unsigned long flags;
spin_lock_irqsave(&lock, flags);//屏蔽中断,获取衍生自旋锁,如果获取成功,立马返回即可访问临界区
如果获取失败,任务将在此函数中进行循环忙等待
直到持有自旋锁的任务释放自旋锁
5.任务获取衍生自旋锁以后,即可踏踏实实的访问临界区
6.访问临界区之后,记得要释放衍生自旋锁,恢复中断
spin_unlock_irqrestore(&lock);
7.注意:获取衍生自旋锁和释放衍生自旋锁务必在逻辑上成对使用
案例:利用衍生自旋锁来解决案例1中的代码漏洞
实验步骤同上
案例:利用中断屏蔽解决LCD显示屏的问题
void lcd_config(void) {
unsigned long flags
spin_lock_irqsave(&lock, flags);
//临界区
gpio_set_value(..., 1);
mdelay(5);
gpio_set_value(..., 0);
mdelay(5);
spin_unlock_irqrestore(&lock,flags);
}
利用衍生自旋锁能够解决异常问题!
7.解决竞态引起异常的方法之信号量
内核信号量和用户的信号量一模一样
信号量特点:
信号量又称睡眠锁,基于自旋锁实现的
信号量就是解决自旋锁保护的临界区不能休眠问题,有些场合
临界区中需要进行休眠操作,此时此刻只能用信号量
"休眠操作"仅仅存在于进程的世界中,进程休眠是指
当前进程会释放占用的CPU资源给他们进程使用,信号量仅用于进程
如果进程获取信号量,在访问临界区时,是可以进行休眠操作
如果进程获取信号量失败,那么进程将进行休眠操作
linux内核描述信号量的数据结构:struct semaphore
利用信号量来解决竞态引起异常的编程步骤:
1.明确驱动代码中哪些是共享资源
2.明确驱动代码中哪些是临界区
3.明确临界区中是否有休眠
如果有,必须使用信号量
如果没有,可以考虑使用信号量
4.访问临界区之前,先获取信号量
//定义初始化信号量对象
struct seamphore sema; //定义信号量对象
sema_init(&sema, 1); //初始化信号量对象
//获取信号量
down(&sema);
说明:获取信号量,如果获取信号量成功,进程从此函数中立马返回
然后可以踏踏实实的访问临界区
如果获取信号量失败,进程将进入此函数中进入不可中断的
休眠状态(释放CPU资源,在休眠期间接收到信号不会立即处理信号)
直到持有信号量的进程释放了信号量并且唤醒这个休眠的等待进程
"不可中断的休眠状态":进程在休眠期间,如果接收到了一个kill信号
进程不会立即处理接收到的信号,而是获取信号量的
任务释放信号量以后唤醒这个休眠的进程,进程一旦
被唤醒以后会处理之前接收到的信号
“可中断的休眠状态”:进程在休眠期间,如果接收到了一个信号
进程会被立即唤醒并且处理接收到的信号
或者
down_interruptible(&sema);//获取信号量,如果获取信号量成功,进程从此函数中立马
返回,然后去访问临界区
如果获取信号量失败,进程将进入可中断的休眠状态
(休眠期间会立即处理接收到的信号)
直到获取信号被唤醒或者持有信号量的任务
释放信号量唤醒之前休眠的进程
5.一旦获取信号量成功,进程可以踏踏实实的访问临界区
6.访问临界区之后,记得释放信号量并且唤醒休眠的进程
up(&sema);
7.获取信号量和释放信号量一定要在逻辑上成对使用
案例:利用信号量down,实现一个设备只能被打开一次
上位机实施步骤:同上
下位机测试步骤:
cd /home/drivers/
insmod led_drv.ko
./led_test & //启动A进程,A进程打开成功,A进程调用sleep进行休眠
不关闭
./led_test & //启动B进程,B进程获取信号量失败,进入不可中断的休眠状态
等待A进程来唤醒
ps //查看A,B的PID
top //查看A,B的进程状态
S:进程进入可中断的休眠状态
D:进程进入不可中断的休眠状态
按Q键退出top命令
kill B的PID //给B进程发送kill信号
ps
top
kill A的PID //杀死A进程,A进程关闭设备,释放信号量并且唤醒
B进程,B进程一旦被唤醒,B还要去处理之前接收到
的信号
ps
top
./led_test & //启动A进程,A进程打开成功,A进程调用sleep进行休眠
不关闭
./led_test & //启动B进程,B进程获取信号量失败,进入不可中断的休眠状态
等待A进程来唤醒
ps //查看A,B的PID
top //查看A,B的进程状态
S:进程进入可中断的休眠状态
D:进程进入不可中断的休眠状态
按Q键退出top命令
kill A的PID //给A进程发送kill信号
ps
top //查看B进程的状态是否还是D,不是,变成了S,因为应用程序调用sleep
kill B的PID
问题:内核吐核问题:
INFO: task led_test:1032 blocked for more than 10 seconds.
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message
解决办法:下位机执行:
echo 0 > /proc/sys/kernel/hung_task_timeout_secs
阅读全文
0 0
- day07
- day07
- day07
- day07
- day07
- day07
- day07
- day07
- day07
- day07
- day07
- Day07
- day07
- day07
- day07
- day07-tomcat
- day07 homework
- 作业day07
- Android7.0拍照失败FileUriExposedException异常的解决
- 通过mysql-proxy完成mysql读写分离
- 中科爱讯WiFi探针在贵宾客户提醒领域的应用
- Mac下安装mysql服务及基于workbench的使用方法
- gensim 实践篇
- day07
- 【机器学习】coursera学习笔记(三) 多变量线性回归
- 图像融合(二)-- 简单加权融合
- c#文件流上传图片
- 纯CSS实现坦克运动及开炮效果
- 在linux系统上安装Nginx
- 单播、多播(组播)和广播的区别
- day08
- iOS CBPeripheral和CBPeripheralDelegate