解决竞态引起异常的方法之中断屏蔽
中断屏蔽的特点
中断屏蔽能够解决进程与进程之间的抢占引起的异常(进程之间的抢占本身基于软中断实现),中断屏蔽能够解决中断和进程的抢占引起的异常,能够解决 中断和中断引起的异常。
但是中断屏蔽无法解决多核引起的异常。
注意:使用中断屏蔽保护的临界区的代码执行速度要越快越好,更不能进行休眠操作。因为Linux系统的很多机制都跟中断密切相关(tasklet、软件定时器、硬件定时器等),长时间屏蔽中断非常危险。
中断屏蔽解决竞态引起的异常编程步骤
- 找出代码中哪些是共享资源。
- 找出代码中哪些是临界区。
- 确定所保护的临界区中是否有休眠操作,如果有休眠操作则不能考虑中断屏蔽的方式,如果没有且没有多核参与竞态,则可以考虑使用中断屏蔽。
- 访问临界区之前屏蔽中断:
unsigned long flags;
local_irq_save(flags);//屏蔽中断,保存中断标志到flags(内核来完成)
- 正常访问临界区,此时不会有其他进程抢占、也不会有其他中断打断。
- 访问临界区之后,恢复中断。
local_irq_restore(flags);//恢复中断
- 屏蔽中断和恢复中断务必逻辑上成对使用。
示例(保证led灯同一时刻只能有一个进程操作打开)
使用全局变量open_cnt来记录当前操作使用设备者的个数,初始值为1,open之后先自减,判断是否不为0,保证同一时刻只能有一个进程在操作设备。(具体实现如下)
- led_drv.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
//共享资源
static int open_cnt = 1; //记录LED打开的状态开关
static int led_open(struct inode *inode,
struct file *file)
{
unsigned long flags;
//屏蔽中断
local_irq_save(flags);
//临界区
if(--open_cnt != 0) {
printk("设备已被打开!\n");
open_cnt++;
//恢复中断
local_irq_restore(flags);
return -EBUSY;//返回设备忙错误码
}
//恢复中断
local_irq_restore(flags);
printk("设备打开成功!\n");
return 0; //open返回成功
}
static int led_close(struct inode *inode,
struct file *file)
{
unsigned long flags;
//屏蔽中断
local_irq_save(flags);
//临界区
open_cnt++;
//恢复中断
local_irq_restore(flags);
return 0;
}
//定义初始化硬件操作接口对象
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.release = led_close
};
//定义初始化混杂设备对象
static struct miscdevice led_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "myled",
.fops = &led_fops
};
static int led_init(void)
{
misc_register(&led_misc);
return 0;
}
static void led_exit(void)
{
misc_deregister(&led_misc);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
- led_test.c
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
int main(void)
{
int fd;
fd = open("/dev/myled", O_RDWR);
if (fd < 0) {
printf("打开设备失败!\n");
return -1;
}
sleep(100000000);
close(fd);
return 0;
}
- Makefile
obj-m += led_drv.o
all:
make -C /opt/kernel SUBDIRS=$(PWD) modules
clean:
make -C /opt/kernel SUBDIRS=$(PWD) clean
- 执行结果(先后台运行一个led_test程序,再尝试运行一个进程试一下):
- 可以看到,当后台运行了一个进程在使用设备的时候,再有一个进程尝试打开设备就会失败(: Device or resource busy)。
- 前一章描述的关于按键中断处理存在竞态的问题是单独一个进程在多核情况下导致的,所以使用中断屏蔽没不能解决问题,所以中断屏蔽并不适用于多核导致的竞态问题。