驱动程序开发:原子操作、自旋锁、信号量、互斥体详细讲解

原子操作、自旋锁、信号量、互斥体详细讲解

一、源来
  并发与并行的区别:
  ①并发:当有多个线程在操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间段,再将时间 段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状。.这种方式我们称之为并发(Concurrent)。
  ②并行:当系统有一个以上CPU时,则线程的操作有可能非并发。当一个CPU执行一个线程时,另一个CPU可以执行另一个线程,两个线程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。
  由以上概念用现实中盖房子的现象,如下图所示。并发情况下,同一时刻只能提供给一个房子建设,若果另一个建设房子建设工人强制拿取建设资源水泥罐车,那么两个房子的建设工人就有可能出现纠纷打架了(这就是竞争)。在并行情况下,同一时刻两个房子建设各自享有水泥罐车,所有不存在纠纷问题。
因此,并发工作可能会带来竞争的影响。
在这里插入图片描述
二、并发带来竞争解决方法
1、原子操作
  原子操作:指不能再进一步分割的操作,一般原子操作用于变量或者位操作。
  由以上概念可以理解为,在并发情况下建房子,设立了一个建房子的包工头作为管理人员(原子操作),他负责调和两个房子建设发生的纠纷,只有这个房子做好了一层的楼体,那么包工头才会将水泥罐车的使用权给另一个房子建设。
  因为原子操作相当于建设房子的包工头管理人员,这个包工头只能解决房子建设的纠纷,不能去搞什么飞机、火箭、轮船等建设里头的纠纷问题。

驱动程序实现步骤:

/* 原子操作,实现同一时刻只能由一个应用程序打开驱动程序 */
/* 1.定义原子变量 */
atomic_t atomic_lock;   /* 原子变量 */
/* 2.初始化原子变量 1:表示可用,0:表示不可用 */
atomic_set(&gpioled.atomic_lock,1);
/* 3.判断原子变量是否为1,可用 */
if(atomic_read(&gpioled.atomic_lock)<=0) {
	/* 已被使用,所以不可用 */
	return -EBUSY;		//返回忙碌
} else {
	/* 原子变量自减1,表示被占用,不可使用状态 */
	atomic_dec(&gpioled.atomic_lock);
}
/* 4.关闭时释放原子锁,也就是原子变量自增1,设置可使用状态 */
atomic_inc(&gpioled.atomic_lock);
/***********************************************************/

2、自旋锁

  自旋锁:专为防止多处理器并发而引入的一种锁,它在内核中大量应用于中断处理等部分(对于单处理器来说,防止中断处理中的并发可简单采用关闭中断的方式,即在标志寄存器中关闭/打开中断标志位,不需要自旋锁)。它弥补了原子操作的缺点,它支持任意的类型处理。
注意: 自旋锁会自动禁止抢占,也就说当线程 A得到锁以后会暂时禁止内核抢占。如果线程 A 在持有锁期间进入了休眠或阻塞状态,那么线程 A 会被挂起,自动放弃 CPU 使用权。线程 B 开始运行,线程 B 也想要获取锁,但是此时锁被 A 线程持有,而且内核抢占还被禁止了!线程 B 无法被调度出去,那么线程 A 就无法运行,锁也就无法释放,好了,死锁发生了!
如下图描述,A人进房子拿了钥匙,因为开G被赶出了房子(阻塞或休眠),随后B人进入了房子,巧的是它也想开房间,但是A人没有遵守要求,出房子前没把钥匙放回房子里,导致B人在房间里找疯狂找要是(就是那种没找到就不出去,不然没脸见人)。因为房子只能进一个人,所有全部人都得外面等,而这个时候A人给系统解除禁制了,想进入房子把钥匙归还,但是因为一个房子一个人,所有进不去。那么整个系统就等死了。
在这里插入图片描述
驱动程序实现步骤:

/* 自旋锁,实现同一时刻只能由一个应用程序打开驱动程序 */
/* 1.初始化自旋锁变量 */
spin_lock_init(&gpioled.spinlock);
gpioled.spinlock_flag = 0;	//0:表示可用,>=1:表示不可用
/* 2.自旋锁上锁 */
spin_lock(&gpioled.spinlock);
/* 3.临界区 */
if(gpioled.spinlock_flag) {
	spin_unlock(&gpioled.spinlock);
	return -EBUSY;
} else {
	gpioled.spinlock_flag++;	//设置被使用状态
}
/* 4.自旋锁解锁 */
spin_unlock(&gpioled.spinlock);
/* 5.自旋锁上锁 */
spin_lock(&dev->spinlock);
/* 6.临界区 */
if(gpioled.spinlock_flag) {
	dev->spinlock_flag--;	//解除被使用状态,转为可使用状态
}
/* 7.自旋锁解锁 */
spin_unlock(&dev->spinlock);
/***********************************************************/

3、信号量

  信号量:略!(LiteOS的信号量那篇有详细描述,它允许多个线程同时访问共享资源)。
  信号量与自旋锁区别:信号量可以使线程进入休眠状态,这也就相当于A人进入房子,而B人也想进入房子,A人和B人说:“你不用排队,你先回去睡会觉,我出来了就打电话给你过来”,那么这样就不会后面出现那么多人在房子外面傻乎乎地等待了。
因此信号量适合占用资源时间比较久的场合,而自旋锁适合占用资源时间比较短的场合。
驱动程序实现步骤:

/* 信号量,实现统一时刻只有一个应用程序打开驱动程序 */
/* 1.初始化信号量 */
sema_init(&gpioled.sem,1);
/* 2.获取信号量 */
down(&gpioled.sem);
/* 3.释放信号量 */
up(&dev->sem);
/***********************************************************/

4、互斥体

  互斥体:实现了“互相排斥”同步的简单形式(所以名为互斥体(mutex))。互斥体禁止多个线程同时进入受保护的代码“临界区”。
  互斥体与信号量区别:是否允许多线程访问同一个资源(临界区)。

驱动程序实现步骤:

/* 互斥体,实现统一时刻只有一个应用程序打开驱动程序 */
/* 1.初始化互斥体 */
mutex_init(&gpioled.mutex_body);
/* 2.获取互斥体 */
mutex_lock(&gpioled.mutex_body);
/* 3.释放互斥体 */
mutex_unlock(&gpioled.mutex_body);
/***********************************************************/

5、一些总结:
自旋锁:
①、因为在等待自旋锁的时候处于“自旋”状态,因此锁的持有时间不能太长,一定要短,否则的话会降低系统性能。如果临界区比较大,运行时间比较长的话要选择其他的并发处
理方式,比如稍后要讲的信号量和互斥体。
②、自旋锁保护的临界区内不能调用任何可能导致线程休眠的 API 函数,否则的话可能
导致死锁。
③、不能递归申请自旋锁,因为一旦通过递归的方式申请一个你正在持有的锁,那么你就
必须“自旋”,等待锁被释放,然而你正处于“自旋”状态,根本没法释放锁。结果就是自己
把自己锁死了!
④、在编写驱动程序的时候我们必须考虑到驱动的可移植性,因此不管你用的是单核的还
是多核的 SOC,都将其当做多核 SOC 来编写驱动程序。


信号量:
①、因为信号量可以使等待资源线程进入休眠状态,因此适用于那些占用资源比较久的场
合。
②、因此信号量不能用于中断中,因为信号量会引起休眠,中断不能休眠。
③、如果共享资源的持有时间比较短,那就不适合使用信号量了,因为频繁的休眠、切换
线程引起的开销要远大于信号量带来的那点优势。


互斥体:
①、 mutex 可以导致休眠,因此不能在中断中使用 mutex,中断中只能使用自旋锁。
②、和信号量一样, mutex 保护的临界区可以调用引起阻塞的 API 函数。
③、因为一次只有一个线程可以持有 mutex,因此,必须由 mutex 的持有者释放 mutex。并且 mutex 不能递归上锁和解锁。

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

邓家文007

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

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

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

打赏作者

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

抵扣说明:

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

余额充值