产生的原因:Linux是一个多任务操作系统,肯定会存在多个任务共同操作同一段内存或者设备的情况,对于多个任务甚至中断都能访问的资源(共享资源)需要保护。也就是要处理对共享资源的并发访问
同时访问某个数据:同时就是并发 他们之间存在着竞争关系
并发的原因:多任务系统,中断,抢占,多核,线程 等竞争资源他们之间存在竞争关系
如何处理:即在他们可以共同访问的数据段中保证一次只要一个线程访问 即原子访问(这么称呼是因为原子是一个整体 不拆分)
保护的是:数据 是多个线程都会访问的共享数据(可以是全局变量)
——————————————————————————————
本案例使用自旋锁 spinlock_ ---一次只允许一个应用程序可以使用 LED 灯。
注意:被自旋锁保护的临界区一定不能调用任何能够引起睡眠和阻塞的 API 函数,否则的话会可能会导致死锁现象的发生
原因是:自旋锁会自动禁止抢占,也就说当线程 A 得到锁以后会暂时禁止内核抢占
如果A 在持有锁期间进入了休眠状态,那么A 会自动放弃 CPU 使用权。此时线程 B 开始运行,线程 B 也想要获取锁,但是此时锁被 A 线程持有,而 且自旋锁的内核抢占还被禁止,线程B就移植在循环无法被调度出去,线程 A 就无法运行,锁也就无法释放, 死锁发生。
如果此时有中断
——————————————————————————————
本案例使用自旋锁---一次只允许一个应用程序可以使用 LED 灯。
--linux系统提供了一系列的API函数
——————————————————
/*gpioled设备结构体*/
/*该设备需要的*/
struct gpioled_dev
{
dev_t devid; /*设备号 32位*/
int major ;/*主设备号 高12位*/
int minor ;/*次设备号 低20位*/
/*cdev表示字符设备 结构体初始化就是初始化结构体变量*/
struct cdev cdev ;//包含了操作函数集 和 设备号 包含头文件 #include <linux/cdev.h>
struct class *class; /*类*/
struct device *device; /*设备*/
struct device_node *nd; //节点
int led_gpio ;//led 所使用的 GPIO 编号
//自旋锁
int dev_status;//设备状态 标记驱动有没有被其他应用使用
//0表示设备可以使用 1 表示设备不可使用
spinlock_t lock;//
unsigned long irqflag;
};
struct gpioled_dev gpioled;
————————————
/*入口 -注册字符设备*/中注册设备号 /cdev 具体的字符设备操作集合 向内核添加字符设备 自动创建设备节点 不变
在驱动入口函数中:
1、 /*初始化自旋锁*/
spin_lock_init(&gpioled.lock);
gpioled.dev_status = 0;//标记驱动可被使用
————————————————————————
在操作函数集中
static struct file_operations gpioled_fops =
{
.owner = THIS_MODULE ,
.open = gpioled_open,
.write = gpioled_write,
.release = gpioled_release,
};
——————————————————————
static int gpioled_open(struct inode *inode, struct file *filp)
{
//私有数据
filp->private_data = &gpioled ;
//对自旋锁进行操作
/*先进行加锁*/
//保存中断状态,禁止本地中断,并获取自旋锁。
spin_lock_irqsave(&gpioled.lock,gpioled.irqflag);
/*加锁解锁之间是临界区 既代码保护区 保护的是数据*/
/*dev_status 的保护*/
if(gpioled.dev_status)//驱动不可用 = 1
{
/*先解锁 再 退出*/
/将中断状态恢复到以前的状态,并且激活本地中断, 释放自旋锁
spin_unlock_irqrestore(&gpioled.lock,gpioled.irqflag);
return -EBUSY;
}
/*能用 之后确保使用1次所以加1 标记被使用*/
gpioled.dev_status++;
/*解锁*/
//将中断状态恢复到以前的状态,并且激活本地中断, 释放自旋锁
spin_unlock_irqrestore(&gpioled.lock,gpioled.irqflag);
return 0;
}
——————————————————————————
static int gpioled_release(struct inode *inode, struct file *filp)
{
//可以通过私有数据访问struct gpioled_dev gpioled
struct gpioled_dev *dev = (struct gpioled_dev*)filp->private_data;
/*涉及到对数据的操作 所以也是要使用自旋锁*/
/*先进行加锁*/
spin_lock_irqsave(&dev->lock, dev->irqflag);
//表示应用程序的 执行完毕
/*加锁解锁之间是临界区 既代码保护区 保护的是数据*/
/*dev_status 的保护*/
if(dev->dev_status)//驱动不可用 = 1
{
dev->dev_status--;/*标记驱动可以使用 在驱动打开的时候+1之后要-1*/
}
/*解锁*/
spin_unlock_irqrestore(&dev->lock,dev->irqflag);
return 0;
}