【Linux驱动】LED 互斥控制 —— 原子操作变量、信号量、互斥锁

Linux是一个多任务系统,肯定存在多个应用程序访问同一个驱动的情况,若这个驱动中存在全局变量或者其他共享资源,此时便会出现访问冲突的问题。

下面以多任务操作 LED 为例,介绍三种互斥访问共享资源的方法。


目录

一、原子操作变量

1、API 介绍

2、原子变量控制LED

 二、信号量

1、API 介绍

2、信号量控制LED

三、互斥锁

1、API 介绍

2、互斥锁控制LED


一、原子操作变量

1、API 介绍

原子操作变量是一种特殊变量,具备原子性,在操作过程中不会被中断或者其他线程干扰,通常用于多线程环境,用于管理共享数据的并发访问。常见的原子操作变量如下:

原子整型用于对整型变量进行原子操作
原子布尔型用于对布尔型变量进行原子操作      
原子引用用于对对象引用进行原子操作
原子数组用于对数组进行原子操作

在linux内核中,原子变量相关类型声明在 linux/atomic.h 文件中。对原子变量的操作需要借助linux内核提供的 API。下面以介绍整型原子变量为例,整型原子变量使用 atomic_t 表示。

/** 修改原子变量的值
 * @param v 要操作的原子变量 
 * @param i 修改值
 */
void atomic_set(atomic_t *v, int i);
/** 获取原子变量的值
 * @param v 要操作的原子变量 
 * @return  返回整型原子变量的值
 */
int atomic_read(atomic_t *v);
/** 原子变量 v 加上一个值 i
 */
void atomic_add(int i, atomic_t *v);
/** 原子变量 v 自增 1
 */
void atomic_inc(atomic_t *v);
/** 原子变量 v 减去一个值 i
 */
void atomic_sub(int i, atomic_t *v);
/** 原子变量 v 自减 1
 */
void atomic_dec(atomic_t *v);

2、原子变量控制LED

如果使用原子变量来控制LED,基本步骤如下:

  1. 在驱动入口函数,初始化原子变量 devNum 为 1(表示可用设备为1)
  2. 在open操作函数中,如果 devNum = 1,那么 devNum 自减;如果 devNum <= 0,说明设备正在被占用,返回 -1
  3. 在release操作函数中,devNum 自增 1(需注意自增后的值不能超过1)
static atomic_t devNum;
/* 驱动入口函数 */
atomic_set(&devNum, 1);

/* open操作函数 */
if (atomic_read(&devNum) <= 0)
{
    printk("open dev failed, device is ocuppied!");
    return -1;
}
else
	atomic_dec(&devNum);

/* release操作函数 */
if (atomic_read(&devNum) == 0)
{
	atomic_inc(&devNum);
}

 二、信号量

信号量相当于一个计数器,统计的是目前临界资源的剩余数量,申请一个信号量时,计数器自减;释放一个信号量时,计数器自增。

1、API 介绍

信号量相关API声明在 linux/semaphore.h 文件中,Linux内核提供了数据类型 struct semaphore 来表示一个信号量。因为信号量相当于一个计数器,所以在使用之前需要先初始化信号量:

/** 信号量初始化
 * @param sem 要初始化的信号量
 * @param val 信号量初值
 */
void sema_init(struct semaphore *sem, int val);

申请信号量:

/** 申请信号量(申请失败会进入休眠)
 * @param sem 要操作的信号量
 * @return 成功返回 0,失败返回其他
 *         1、EINTR/ERESTARTSYS: 等待过程被中断
 *         2、EACCES: 权限不足
 *         3、EAGAIN: 资源暂时不可用
 */
// 进入休眠后,无法被信号打断(即便按下 Ctrl + C 也无法关闭进程)
void down(struct semaphore *sem);
// 进入休眠后,可以被信号打断 (只有在被信号打断的时候才会返回,否则会一直阻塞)
int down_interruptible(struct semaphore *sem);

释放信号量:

/** 释放信号量
 * @param sem 要操作的信号量
 */
void up(struct semaphore *sem);

2、信号量控制LED

如果使用信号量来控制LED,基本步骤如下:

  1. 在驱动入口函数,初始化信号量 dev_sem 为 1(表示可用设备为1)
  2. 在open操作函数中,申请信号量
  3. 在release操作函数中,释放信号量
static struct semaphore dev_sem;
/* 驱动入口函数 */
sema_init(&dev_sem, 1);

/* open操作函数 */
if (down_interruptible(&dev_sem))
{
	printk("open dev failed, device is ocuppied!\n");
	return -1;
}

/* release操作函数 */
up(&dev_sem);

三、互斥锁

互斥锁相当于一块令牌,拿到令牌的人才能访问临界资源,没有拿到令牌的只能挂起等待,只有当令牌处于释放状态时才能被各方争抢。

1、API 介绍

 互斥锁相关的API声明在 linux/mutex.h 文件中,Linux内核提供了数据类型 struct mutex 来表示一个互斥锁。和信号量一样,在使用互斥锁之前需要先初始化互斥锁:

/** 互斥锁初始化
 * @param lock 要初始化的互斥锁
 */
void mutex_init(mutex *lock);

加锁:

/** 加锁(如果没有申请到互斥锁便会进入休眠)
 * @param lock 要操作的互斥锁
 * @return 成功返回 0,失败返回其他
 */
// 休眠无法被信号打断
void mutex_lock(struct mutex *lock);
// 休眠可以被信号打断 (只有在被信号打断的时候才会返回,否则会一直阻塞)
int mutex_lock_interruptible(struct mutex *lock);

解锁:

/** 解锁
 * @param lock 要操作的互斥锁
 */
void mutex_unlock(struct mutex *lock);

2、互斥锁控制LED

如果使用信号量来控制LED,基本步骤如下:

  1. 在驱动入口函数,初始化互斥锁 dev_mtx
  2. 在open操作函数中,申请信号量
  3. 在release操作函数中,释放信号量
static struct mutex dev_mtx;
/* 驱动入口函数 */
mutex_init(&dev_mtx);

/* open操作函数 */
if (mutex_lock_interruptible(&dev_mtx))
{
	printk("open dev failed, device is ocuppied!\n");
	return -1;
}

/* release操作函数 */
mutex_unlock(&dev_mtx);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值