Linux驱动开发(四)

1、资源与竞争

一、自旋锁

1、自旋锁的定义

和信号量不同的是自旋锁可在不能休眠的代码中使用,如中断处理例程。在正确使用时,自旋锁通常比信号量具有更高的性能。

如果锁可用,则锁定位被设置,代码继续进入临界区;相反则代码进入忙循环并重复检查锁,直到该锁可用。

所有自旋锁在本质上是不可中断的,一旦调用了spin_lock,在获得锁之前一直处于自旋状态。

自旋锁的实现由于linux所支持的架构不同而不同。

1、自旋锁的核心规则是:任何拥有自旋锁的代码必须是原子的。自旋锁不能休眠。

2、自旋锁必须在可能的最短时间内拥有。

2、自旋锁的死锁

在多核cpu或者支持的单核cpu中,被自旋锁保护的临界区不能调用任何能够引起睡眠或者阻塞的函数,否则可能会发生死锁。

使用自旋锁会禁止抢占,比如在单核的cpu下,A进程获取到自旋锁以后暂时关闭了内核抢占,如果A进程此时进入了休眠状态,(放弃了CPU的使用权)B进程此时也想获取到自旋锁,但是此时的自旋锁已经被A进程所持有,而且此时的cpu的抢占也已经被禁止了,因为是单核,进程B无法被调度出去,只能在原地旋转,等在锁被A释放,但是进程A无法运行,锁也就无法释放,死锁也就发生了。

多核CPU不会发生上边的情况,因为其他核也会调度其他的进程,

当进程A获取的自旋锁以后,如果产生了中断,并且在中断里面也要访问共享资源,(中断里边可以使用自旋锁)此时中断里边无法获取到自旋锁,只能原地旋转,产生死锁,为了避免这种情况发生,可以使用spin_lock_irqsaveAPI来禁止中断并获取自旋锁

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

绛洞花主敏明

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值