1、资源与竞争
一、自旋锁
1、自旋锁的定义
和信号量不同的是自旋锁可在不能休眠的代码中使用,如中断处理例程。在正确使用时,自旋锁通常比信号量具有更高的性能。
如果锁可用,则锁定位被设置,代码继续进入临界区;相反则代码进入忙循环并重复检查锁,直到该锁可用。
所有自旋锁在本质上是不可中断的,一旦调用了spin_lock
,在获得锁之前一直处于自旋状态。
自旋锁的实现由于linux
所支持的架构不同而不同。
1、自旋锁的核心规则是:任何拥有自旋锁的代码必须是原子的。自旋锁不能休眠。
2、自旋锁必须在可能的最短时间内拥有。
2、自旋锁的死锁
在多核cpu
或者支持的单核cpu
中,被自旋锁保护的临界区不能调用任何能够引起睡眠或者阻塞的函数,否则可能会发生死锁。
使用自旋锁会禁止抢占,比如在单核的cpu
下,A进程获取到自旋锁以后暂时关闭了内核抢占,如果A进程此时进入了休眠状态,(放弃了CPU
的使用权)B进程此时也想获取到自旋锁,但是此时的自旋锁已经被A进程所持有,而且此时的cpu
的抢占也已经被禁止了,因为是单核,进程B无法被调度出去,只能在原地旋转,等在锁被A释放,但是进程A无法运行,锁也就无法释放,死锁也就发生了。
多核CPU
不会发生上边的情况,因为其他核也会调度其他的进程,
当进程A获取的自旋锁以后,如果产生了中断,并且在中断里面也要访问共享资源,(中断里边可以使用自旋锁)此时中断里边无法获取到自旋锁,只能原地旋转,产生死锁,为了避免这种情况发生,可以使用spin_lock_irqsave
等API
来禁止中断并获取自旋锁
3、自旋锁的API
函数 | 描述 |
---|---|
DDFINE_SPINLOCK(spinlock_t lock) | 定义并初始化一个变量 |
int spin_lock_init(spinlock_t *lock) | 初始化自旋锁 |
void spin_lock(spinlock_t *lock) | 获取自旋锁,也叫做加锁 |
void spin_unlock(spinlock_t *lock) | 释放自旋锁,也叫做解锁 |
int spin_trylock(spinlock_t *lock) | 尝试获取自旋锁,如果没有获取就返回0 |
int spin_is_locked(spinlock_t *lock) | 检查自旋锁是否被获取,如果没有被获取就返回非0,否则返回0 |
void spin_lock_irq(spinlock_t *lock) | 关闭中断并获取自旋锁 |
void spin_unlock_irq(spinlock_t *lock) | 打开中断并获取自旋锁 |
void spin_lock_irqrestore(spinlock_t *lock, unsigned long flags) | 保存中断状态,关闭中断并获取自旋锁 |
void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags) | 恢复之前的中断状态,打开中断并释放自旋锁 |
void spin_lock_bh(spinlock_t *lock) | 关闭下半部,获取自旋锁 |
void spin_unlock_bh(spinlock_t *lock) | 打开下半部,获取自旋锁 |
4、自旋锁例子
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/spinlock.h>
static int major = 277;
static int minor = 0;
static dev_t helloNum;
static struct class *helloClass = NULL;
static struct device *helloDev = NULL;
static spinlock_t lock;
int busyFlag = 0;
int hello_open(struct inode *pinode, struct file *pfile)
{
printk("hello_open, minor:%d, major:%d\n", iminor(pinode), imajor(pinode));
spin_lock(&lock);
if (1 == busyFlag)
{
spin_unlock(&lock);
return -EBUSY;
}
busyFlag = 1;
spin_unlock(&lock);
return 0;
}
int hello_release(struct inode *pinode, struct file *pfile)
{
printk("hello_release, minor:%d, major:%d\n", iminor(pinode), imajor(pinode));
busyFlag = 0;
return 0;
}
static struct file_operations hello_ops = {
.open = hello_open,
.release = hello_release,
};
static int hello_init(void)
{
int ret = 0;
printk("hello_init\n");
ret = register_chrdev( major, "hello", &hello_ops);
if(ret < 0)
{
printk("register_chrdev failed.\n");
return ret;
}
helloClass = class_create(THIS_MODULE, "hellocls");
if (IS_ERR(helloClass))
{
printk("class_create failed.\n");
ret = PTR_ERR(helloClass);
goto error_exit1;
}
helloNum = MKDEV(major,minor);
printk("major:%d, minor:%d\n", MAJOR(helloNum), MINOR(helloNum));
helloDev = device_create(helloClass, NULL, helloNum, NULL, "hello0");
if (IS_ERR(helloDev))
{
printk("device_create failed.\n");
ret = PTR_ERR(helloDev);
goto error_exit2;
}
return 0;
error_exit2:
class_destroy(helloClass);
error_exit1:
unregister_chrdev(major,"hello");
return ret;
}
static void hello_exit(void)
{
printk("hello_exit\n");
device_destroy(helloClass, helloNum);
class_destroy(helloClass);
unregister_chrdev(major,"hello");
}
MODULE_LICENSE("GPL");
module_init(hello_init);
module_exit(hello_exit);
二、原子操作
1、原子的定义
Linux 内核提供了一组原子操作 API 函数来完成此功能,Linux 内核提供了两组原子操作 API 函数,一组 是对整型变量进行操作的,一组是对位进行操作的。
Linux 内核提供特殊的类型 atomic_t
定义原子变量。此结构体定义在 include/linux/types.h
文件中,定义如下:
# 这是32位操作系统的
typedef struct {
int counter;
} atomic_t;
# 这是64位操作系统的
typedef struct {
int counter;
} atomic64_t;
2、64位原子操作API
函数 | 描述 |
---|---|
ATOMIC64_INIT(int i) | 定义原子变量的时候对其初始化 |
int atomic64_read(atomic_t *v) | 读取 v 的值,并且返回 |
void atomic64_set(atomic_t *v, int i) | 向 v 写入 i 值 |
void atomic64_add(int i, atomic_t *v) | 给 v 加上 i 值 |
void atomic64_sub(int i, atomic_t *v) | 从 v 减去 i 值 |
void atomic64_inc(atomic_t *v) | 给 v 加 1,也就是自增 |
void atomic64_dec(atomic_t *v) | 从 v 减 1,也就是自减 |
int atomic64_sub_and_test(int i, atomic_t *v) | 从 v 减 i,如果结果为 0 就返回真,否则返回假 |
int atomic64_add_negative(int i, atomic_t *v) | 给 v 加 i,如果结果为负就返回真,否则返回假 |
int atomic64_add_return(atomic_t *v) | 从 v 加 i,并且返回 v 的值 |
int atomic64_sub_return(atomic_t *v) | 从 v 减 i,并且返回 v 的值 |
int atomic64_inc_return(atomic_t *v) | 给 v 加 1,并且返回 v 的值 |
int atomic64_dec_return(atomic_t *v) | 从 v 减 1,并且返回 v 的值 |
int atomic64_dec_and_test(atomic_t *v) | 从 v 减 i,如果结果为 0 就返回真,否则返回假 |
int atomic64_inc_and_test(atomic_t *v) | 从 v 加 1,如果结果为 0 就返回真,否则返回假 |
2、32位原子操作API
函数 | 描述 |
---|---|
ATOMIC32_INIT(int i) | 定义原子变量的时候对其初始化 |
int atomic32_read(atomic_t *v) | 读取 v 的值,并且返回 |
void atomic32_set(atomic_t *v, int i) | 向 v 写入 i 值 |
void atomic32_add(int i, atomic_t *v) | 给 v 加上 i 值 |
void atomic32_sub(int i, atomic_t *v) | 从 v 减去 i 值 |
void atomic32_inc(atomic_t *v) | 给 v 加 1,也就是自增 |
void atomic32_dec(atomic_t *v) | 从 v 减 1,也就是自减 |
int atomic32_sub_and_test(int i, atomic_t *v) | 从 v 减 i,如果结果为 0 就返回真,否则返回假 |
int atomic32_add_negative(int i, atomic_t *v) | 给 v 加 i,如果结果为负就返回真,否则返回假 |
int atomic32_add_return(atomic_t *v) | 从 v 加 i,并且返回 v 的值 |
int atomi32_sub_return(atomic_t *v) | 从 v 减 i,并且返回 v 的值 |
int atomic32_inc_return(atomic_t *v) | 给 v 加 1,并且返回 v 的值 |
int atomic32_dec_return(atomic_t *v) | 从 v 减 1,并且返回 v 的值 |
int atomic32_dec_and_test(atomic_t *v) | 从 v 减 i,如果结果为 0 就返回真,否则返回假 |
int atomi32_inc_and_test(atomic_t *v) | 从 v 加 1,如果结果为 0 就返回真,否则返回假 |
3、原子示例
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define GPIOLED_CNT 1 /* 设备号个数 */
#define GPIOLED_NAME "gpioled" /* 名字 */
#define LEDOFF 0 /* 关灯 */
#define LEDON 1 /* 开灯 */
/* gpioled设备结构体 */
struct gpioled_dev
{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
struct device_node *nd; /* 设备节点 */
int led_gpio; /* led所使用的GPIO编号 */
atomic_t lock; /* 原子变量 */
};
struct gpioled_dev gpioled; /* led设备 */
/*
* @description : 打开设备
* @param - inode : 传递给驱动的inode
* @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
* 一般在open的时候将private_data指向设备结构体。
* @return : 0 成功;其他 失败
*/
static int led_open(struct inode *inode, struct file *filp)
{
/* 通过判断原子变量的值来检查LED有没有被别的应用使用 */
if (!atomic_dec_and_test(&gpioled.lock))
{
atomic_inc(&gpioled.lock); /* 小于0的话就加1,使其原子变量等于0 */
return -EBUSY; /* LED被使用,返回忙 */
}
filp->private_data = &gpioled; /* 设置私有数据 */
return 0;
}
/*
* @description : 从设备读取数据
* @param - filp : 要打开的设备文件(文件描述符)
* @param - buf : 返回给用户空间的数据缓冲区
* @param - cnt : 要读取的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 读取的字节数,如果为负值,表示读取失败
*/
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
/*
* @description : 向设备写数据
* @param - filp : 设备文件,表示打开的文件描述符
* @param - buf : 要写给设备写入的数据
* @param - cnt : 要写入的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 写入的字节数,如果为负值,表示写入失败
*/
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue;
unsigned char databuf[1];
unsigned char ledstat;
struct gpioled_dev *dev = filp->private_data;
retvalue = copy_from_user(databuf, buf, cnt);
if (retvalue < 0)
{
printk("kernel write failed!\r\n");
return -EFAULT;
}
ledstat = databuf[0]; /* 获取状态值 */
if (ledstat == LEDON)
{
gpio_set_value(dev->led_gpio, 0); /* 打开LED灯 */
}
else if (ledstat == LEDOFF)
{
gpio_set_value(dev->led_gpio, 1); /* 关闭LED灯 */
}
return 0;
}
/*
* @description : 关闭/释放设备
* @param - filp : 要关闭的设备文件(文件描述符)
* @return : 0 成功;其他 失败
*/
static int led_release(struct inode *inode, struct file *filp)
{
struct gpioled_dev *dev = filp->private_data;
/* 关闭驱动文件的时候释放原子变量 */
atomic_inc(&dev->lock);
return 0;
}
/* 设备操作函数 */
static struct file_operations gpioled_fops = {
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_release,
};
/*
* @description : 驱动出口函数
* @param : 无
* @return : 无
*/
static int __init led_init(void)
{
int ret = 0;
/* 初始化原子变量 */
atomic_set(&gpioled.lock, 1); /* 原子变量初始值为1 */
/* 设置LED所使用的GPIO */
/* 1、获取设备节点:gpioled */
gpioled.nd = of_find_node_by_path("/gpioled");
if (gpioled.nd == NULL)
{
printk("gpioled node not find!\r\n");
return -EINVAL;
}
else
{
printk("gpioled node find!\r\n");
}
/* 2、 获取设备树中的gpio属性,得到LED所使用的LED编号 */
gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
if (gpioled.led_gpio < 0)
{
printk("can't get led-gpio");
return -EINVAL;
}
printk("led-gpio num = %d\r\n", gpioled.led_gpio);
/* 3、设置GPIO1_IO03为输出,并且输出高电平,默认关闭LED灯 */
ret = gpio_direction_output(gpioled.led_gpio, 1);
if (ret < 0)
{
printk("can't set gpio!\r\n");
}
/* 注册字符设备驱动 */
/* 1、创建设备号 */
if (gpioled.major)
{ /* 定义了设备号 */
gpioled.devid = MKDEV(gpioled.major, 0);
register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);
}
else
{ /* 没有定义设备号 */
alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME); /* 申请设备号 */
gpioled.major = MAJOR(gpioled.devid); /* 获取分配号的主设备号 */
gpioled.minor = MINOR(gpioled.devid); /* 获取分配号的次设备号 */
}
printk("gpioled major=%d,minor=%d\r\n", gpioled.major, gpioled.minor);
/* 2、初始化cdev */
gpioled.cdev.owner = THIS_MODULE;
cdev_init(&gpioled.cdev, &gpioled_fops);
/* 3、添加一个cdev */
cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);
/* 4、创建类 */
gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
if (IS_ERR(gpioled.class))
{
return PTR_ERR(gpioled.class);
}
/* 5、创建设备 */
gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
if (IS_ERR(gpioled.device))
{
return PTR_ERR(gpioled.device);
}
return 0;
}
/*
* @description : 驱动出口函数
* @param : 无
* @return : 无
*/
static void __exit led_exit(void)
{
/* 注销字符设备驱动 */
cdev_del(&gpioled.cdev); /* 删除cdev */
unregister_chrdev_region(gpioled.devid, GPIOLED_CNT); /* 注销设备号 */
device_destroy(gpioled.class, gpioled.devid);
class_destroy(gpioled.class);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("tyustli");
三、信号量
信号量的特点:
①、因为信号量可以使等待资源线程进入休眠状态,因此适用于那些占用资源比较久的场合。
②、因此信号量不能用于中断中,因为信号量会引起休眠,中断不能休眠。
③、如果共享资源的持有时间比较短,那就不适合使用信号量了,因为频繁的休眠、切换线程引起的开销要远大于信号量带来的那点优势。
实验步骤:
①在驱动的入口函数中初始化信号量为1。
②应用通过open函数打开驱动的时候获取信号量,信号量值减1。
③应用通过release函数关闭驱动的时候释放信号量,信号量值加1。
1、API
函数 | 描述 |
---|---|
DEFINE_SEAMPHORE(name) | 定义一个信号量,并且设置信号量的值为 1 |
void sema_init(struct semaphore *sem, int val) | 初始化信号量 sem,设置信号量值为 val |
void down(struct semaphore *sem) | 获取信号量,因为会导致休眠,因此不能在中断中使用。 |
int down_trylock(struct semaphore *sem) | 尝试获取信号量,如果能获取到信号量就获取,并且返回 0。如果不能就返回非 0,并且不会进入休眠 |
int down_interruptible(struct semaphore *sem) | 获取信号量,和 down 类似,只是使用 down 进入休眠状态的线程不能被信号打断。而使用此函数进入休眠以后是可以被信号打断的 |
void up(struct semaphore *sem) | 释放信号量 |
2、示例
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/atomic.h>
#include <linux/semaphore.h>
#define GPIOLED_COUNT 1
#define GPIOLED_NAME "semaphore"
#define LEDOFF 0
#define LEDON 1
/*gpio设备结构体*/
struct gpioled_dev{
dev_t devid; /*设备号*/
int major; /*主设备号*/
int minor; /*次设备号*/
struct cdev cdev;
struct class *class; /*创建类*/
struct device *device; /*创建设备*/
struct device_node *node; /*设备节点*/
int led_gpio;
struct semaphore sem; /* 定义自旋锁 */
};
struct gpioled_dev gpioled;
static int gpioled_open(struct inode *inode, struct file *filp)
{
filp->private_data = &gpioled; /* 设置私有数据 */
//应用打开驱动的时候获取信号量
if(down_interruptible(&gpioled.sem)){
printk("device has been used!\r\n");
return -EINVAL;
}
return 0;
}
static ssize_t gpioled_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt)
{
int ret = 0;
unsigned char databuf[1];
ret = copy_from_user(databuf, buf, cnt);
if(ret < 0){
return -EINVAL;
}
if(databuf[0] == LEDON){
gpio_set_value(gpioled.led_gpio, 0);
}
else if(databuf[0] == LEDOFF){
gpio_set_value(gpioled.led_gpio, 1);
}
return 0;
}
static int gpioled_release(struct inode *inode, struct file *filp){
struct gpioled_dev *dev = filp->private_data;
//应用关闭驱动的时候释放信号量
up(&dev->sem);
return 0;
}
/*定义字符操作集*/
static const struct file_operations gpioled_fops = {
.owner = THIS_MODULE,
.write = gpioled_write,
.open = gpioled_open,
.release = gpioled_release,
};
/*驱动入口函数*/
static int __init led_init(void){
int ret = 0;
/* 初始化信号量 */
sema_init(&gpioled.sem, 1); //初始化信号量为1
gpioled.major = 0; /*linux内核自动申请设备号*/
/*注册字符设备驱动*/
if(gpioled.major){ /*给定主设备号*/
gpioled.devid = MKDEV(gpioled.major, 0);
ret = register_chrdev_region(gpioled.devid, GPIOLED_COUNT, GPIOLED_NAME);
}
else{ /*没给定设备号*/
ret = alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_COUNT, GPIOLED_NAME);
gpioled.major = MAJOR(gpioled.devid); /*保存主设备号*/
gpioled.minor = MINOR(gpioled.devid); /*保存次设备号*/
}
if(ret < 0){
goto failed_devid;
}
printk("gpioled major = %d ,minor = %d \r\n",gpioled.major,gpioled.minor);
/*初始化cdev*/
gpioled.cdev.owner = THIS_MODULE;
cdev_init(&gpioled.cdev, &gpioled_fops);
/*添加cdev*/
ret = cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_COUNT);
if(ret < 0){
goto failed_cdev;
}
/*自动创建设备节点*/
/*创建类*/
gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
if(IS_ERR(gpioled.class)){ /*判断是否创建类成功*/
ret = PTR_ERR(gpioled.class);
goto failed_class;
}
/*创建设备*/
gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
if(IS_ERR(gpioled.device)){ /*判断是否创建类成功*/
ret = PTR_ERR(gpioled.device);
goto failed_device;
}
/*获取设备节点*/
gpioled.node = of_find_node_by_path("/gpioled");
if(gpioled.node == NULL){ /*寻找节点失败*/
ret = -EINVAL;
goto failed_findnode;
}
/*获取led所对应的gpio*/
gpioled.led_gpio = of_get_named_gpio(gpioled.node, "led-gpios", 0);
if(gpioled.led_gpio < 0){
printk("can't find led gpio \r\n");
ret = -EINVAL;
goto failed_findnode;
}
printk("led gpio num = %d \r\n",gpioled.led_gpio);
/*申请gpio*/
ret = gpio_request(gpioled.led_gpio, "led-gpios");
if(ret){
printk("Failed to request gpio \r\n");
ret = -EINVAL;
goto failed_findnode;
}
/*使用IO,申请为输出*/
ret = gpio_direction_output(gpioled.led_gpio, 1); /*设置为输出,高电平不点亮*/
if(ret < 0){
goto failed_setoutput;
}
/*输出低电平,点亮gpio*/
gpio_set_value(gpioled.led_gpio, 0);
return 0;
failed_setoutput:
gpio_free(gpioled.led_gpio);
failed_findnode:
failed_device:
class_destroy(gpioled.class);
failed_class:
cdev_del(&gpioled.cdev);
failed_cdev:
unregister_chrdev_region(gpioled.devid, GPIOLED_COUNT);
failed_devid:
return ret;
}
/*驱动出口函数*/
static void __exit led_exit(void){
/*关灯*/
/*输出高电平,关闭gpio*/
gpio_set_value(gpioled.led_gpio, 1);
/*注销字符设备驱动*/
cdev_del(&gpioled.cdev);
unregister_chrdev_region(gpioled.devid, GPIOLED_COUNT);
device_destroy(gpioled.class, gpioled.devid);
class_destroy(gpioled.class);
/*释放IO*/
gpio_free(gpioled.led_gpio);
}
/*模块入口和出口*/
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZYC");
四、互斥锁
1、定义
自旋锁是一种互斥锁的实现方式而已,相比一般的互斥锁会在等待期间放弃cpu,自旋锁(spinlock)则是不断循环并测试锁的状态,这样就一直占着cpu。
临界区:每个进程中访问临界资源的那段程序称为临界区,每次只允许一个进程进入临界区,进入后不允许其他进程进入。
自旋锁:与互斥量类似,它不是通过休眠使进程阻塞,而是在获取锁之前一直处于忙等(自旋)阻塞状态。用在以下情况:锁持有的时间短,而且线程并不希望在重新调度上花太多的成本。“原地打转”。
2、注意事项
- 互斥锁会导致休眠, 所以在这中断里边不能用互斥锁
- 同一时刻只能有一个线程持有互斥锁,并且只有持有者可以解锁
- 不允许递归上锁和解锁
3、API
函数 | 描述 |
---|---|
DEFINE_MUTEX(name) | 定义并初始化一个互斥锁 |
void mutex_init(mutex *lock) | 初始化互斥锁 |
void mutex_lock(stuct mutex *lock) | 上锁,如果不可以用睡眠 |
void mutex_unlock(stuct mutex *lock) | 解锁 |
int mutex_is_locked(stuct mutex *lock) | 如果锁已经被使用则返回1 , 否则返回0 |
4、例子
struct mutex mutex;
int flage = 1; // 1: available 0:busy
static int hello_open (struct inode *inode, struct file *filep)
{
printk("hello_open()\n");
mutex_lock(&mutex); //加锁
if(flage != 1) //分析一下感觉这个flage可以不要
//这个锁可以用来互斥使用一个全局变量,也可以用来互斥使用一个设备
//不加flage就相当于是保护这个大设备,加flage为互斥使用一个全局变量
{
mutex_unlock(&mutex);
return -EBUSY;
}
//occupy device
flage = 0;
//
return 0;
}
static int hello_release (struct inode *inode, struct file *filep)
{
printk("hello_release()\n");
//release device
flage = 1;
mutex_unlock(&mutex);//解锁
return 0;
}