自旋锁的使用注意事项:
①、
自旋锁保护的临界区要尽可能的短
,因此在
open
函数中申请自旋锁,然后在
release
函
数中释放自旋锁的方法就不可取。我们可以使用一个变量来表示设备的使用情况,如果设备被
使用了那么变量就加一,设备被释放以后变量就减
1
,我们只需要使用
自旋锁保护这个变量
即
可。
②、考虑驱动的兼容性,合理的选择
API
函数。
综上所述,在本节例程中,我们通过定义一个变量
dev_stats
表示设备的使用情况,
dev_stats
为
0
的时候表示设备没有被使用,
dev_stats
大于
0
的时候表示设备被使用。驱动
open
函数中先
判断
dev_stats
是否为
0
,也就是判断设备是否可用,如果为
0
的话就使用设备,并且将
dev_stats
加
1
,表示设备被使用了。使用完以后在
release
函数中将
dev_stats
减
1
,表示设备没有被使用
了。因此真正实现设备互斥访问的是变量
dev_stats
,但是我们要使用自旋锁对
dev_stats
来做保
护。
设备结构体:
/* gpioled
设备结构体
*/
34
struct
gpioled_dev
{
35
dev_t devid
;
/*
设备号
*/
36
struct
cdev cdev
;
/* cdev */
37
struct
class
*
class
;
/*
类
*/
38
struct
device
*
device
;
/*
设备
*/
39
int
major
;
/*
主设备号
*/
40
int
minor
;
/*
次设备号
*/
41
struct
device_node
*
nd
;
/*
设备节点
*/
42
int
led_gpio
;
/* led
所使用的
GPIO
编号
*/
43
int
dev_stats
;
/*
设备状态,
0
,设备未使用
;>0,
设备已经被使用
*/
44
spinlock_t lock
;
/*
自旋锁
*/
45
};
open函数:
spin_lock_irqsave
(&
gpioled
.
lock
,
flags
);
/*
上锁
*
/*调用
spin_lock_irqsave
函数获
取锁,为了考虑到驱动兼容性,这里并没有使用
spin_lock
函数来获取锁。*/
if
(
gpioled
.
dev_stats
) {
/*
如果设备被使用了
spin_unlock_irqrestore
(&
gpioled
.
lock
,
flags
);
/*
解锁
*/
return
-
EBUSY
;
}
gpioled
.
dev_stats
++;
/*
如果设备没有打开,那么就标记已经打开了
*/
spin_unlock_irqrestore
(&
gpioled
.
lock
,
flags
);
/*
解锁
*/ *
判断 dev_stats 是否大于
0
,如果是的话表示设备已经被使用了,那么就调用
spin_unlock_irqrestore 函数释放锁,并且返回-EBUSY
。如果设备没有被使用的话就在第
66
行将
dev_stats
加
1
,表 示设备要被使用了,然后调用 spin_unlock_irqrestore
函数释放锁。自旋锁的工作就是保护 dev_stats 变量,真正实现对设备互斥访问的是
dev_stats
。
release
函数:
/*
关闭驱动文件的时候将
dev_stats
减
1 */
spin_lock_irqsave
(&
dev
->
lock
,
flags
);
/*
上锁
*/
if
(
dev
->
dev_stats
)
{
dev
->
dev_stats
--;
}
spin_unlock_irqrestore
(&
dev
->
lock
,
flags
);
/*
解锁
*/
将
dev_stats
减
1
,表示设备被释放了,可以被其他的应用 程序使用。将 dev_stats
减
1
的时候需要自旋锁对其进行保护。
init
函数:
/*
初始化自旋锁
*/
spin_lock_init
(&
gpioled
.
lock
);