[字符驱动设备]通过引入读写信号量,实现互斥访问

说在前面的话

Linux是一个多任务操作系统,会存在多个任务共同操作同一段内存或设备的情况,这些任务可能会相互覆盖内存中的数据,进而造成内存数据混乱。造成这种现象的原因很复杂,主要有以下几个主要原因。

1) 多线程并发。

2)抢占式并发访问(2.6版本内核支持抢占,调度程序可以在任意时刻抢占正在运行的线程,从而运行其他线程)。

3)中断程序并发访问。

4)SMP(Symmetrical Multi-Processing)核间并发访问。

以多线程访问为例,当多个控制线程共享相同内存时,需要确保每个线程看到一致性的数据视图。如果每个线程使用的变量都是其他线程不会读取或修改的,那么就不存在一致性问题。同样的如果变量是只读的,多个线程也可以同时读取该变量。但是,当某个线程可以修改变量,而其他线程也可以读取或者修改这个变量的时候,就需要对这些线程进行同步。以确保它们在访问变量的储存内容时不会访问到无效的数值。

临界区概念:

​ 实际上在涉及共享内存、共享文件夹以及共享任何资源的情况都会引发“竞争”而导致错误,避免这种错误的关键是找出某种途径来阻止多个进程同时读写共享的数据。也就是说,当一个进程在使用一个共享变量活文件时,其他进程不能做同样的操作。这个问题也可以抽象成另一种表达方式进行描述。我们将进程粗略的分成两部分内容,一部分时间是做内部计算或者一些不会引发竞争条件的操作。另一部分时间会访问共享内存、共享文件或一些会导致竞争的操作。其中把对共享内存进行访问的程序片段叫做***临界区***。因此只要安排得当,使得同一时间只有一个进程处于临界区,就能够避免竞争。

解决的办法:

目前解决这一问题的方法主要有:原子操作、自旋锁、信号量和互斥体等。

1.原子操作(Atomic)

在化学中,原子代表不可再分的基本微粒,原子操作就表示一个访问是一个步骤,不能再继续进行拆分。

2.自旋锁(Spin lock)

当一个线程要访问某个共享资源的时候首先要先获取相应的锁(假想的共享变量),锁只能被一个线程持有,只要此线程不释放持有的锁,那么其他的线程就不能获取此锁。对于自旋锁而言,如果自旋锁正在被线程 A 持有,线程 B 想要获取自旋锁,那么线程 B 就会处于忙循环-旋转-等待状态,线程 B 不会进入休眠状态或者说去做其他的处理,而是会一直傻傻的在那里“转圈圈”的等待锁可用。一个形象的例子是比如现在有个公用电话亭,一次肯定只能进去一个人打电话,现在电话亭里面有人正在打电话,相当于获得了自旋锁。此时你到了电话亭门口,因为里面有人,所以你不能进去打电话,相当于没有获取自旋锁,这个时候你肯定是站在原地等待,你可能因为无聊的等待而转圈圈消遣时光,反正就是那里也不能去,要一直等到里面的人打完电话出来。终于,里面的人打完电话出来了,相当于释放了自旋锁,这个时候你就可以使用电话亭打电话了,相当于获取到了自旋锁。

3.信号量(Semaphore)

信号量是E.W.Dijkstra在1965年提出的一种方法,它使用一个整形变量来累积唤醒次数。他建议设立两种操作:down和up。对于一信号量执行down操作,则是检查其值是否大于0,若此值大于0,则对其减一(代表用掉一个保存的唤醒信号)。若该值为0,则进程睡眠。相比于自旋锁,信号量可以使线程进入休眠状态。举个例子,比如 A 与 B、C 合租了一套房子,这个房子只有一个厕所,一次只能一个人使用。某一天早上 A 去上厕所了,过了一会 B 也想用厕所,因为 A 在厕所里面,B 只能等到 A 用来了才能进去。B 要么就一直在厕所门口等着,等 A 出来,这个时候就相当于自旋锁。B 也可以告诉 A,让 A 出来以后通知他一下,然后 B 继续回房间睡觉,这个时候相当于信号量。可以看出,使用信号量会提高处理器的使用效率,毕竟不用一直傻乎乎的在那里“自旋”等待。但是,信号量的开销要比自旋锁大,因为信号量使线程进入休眠状态以后会切换线程,切换线程就会有开销。

4.互斥量(Mutex)

如果不需要信号量的计数能力,有时候可以使用信号量的一个简化版本,称为互斥量,也就是将信号量的值设置为1。互斥量仅仅适用于管理共享资源或一小段代码。由于互斥量在实现时既容易又有效,这使得互斥量在实现用户空间线程包时非常有用。互斥量是一个可以处于两态之一的变量:解锁和加锁。只需一个二进制位表示它,实际上,尝尝使用一个整数量进行表示,0表示解锁,而其他的所有值则表示加锁。

除了以上四种比较典型的方法,还有顺序锁、读写自选锁、读写信号量和RCU(Read-Copy-Update)等机制,读写自旋锁为读和写操作提供了不同的锁,一次只能允许一个写操作,也就是只能一个线程持有写锁,而且不能进行读操作。但是当没有写操作的时候允许一个或多个线程持有读锁,可以进行并发的读操作。读写信号量与信号量的关系与读写自旋锁和自旋锁的关系类似,读写信号量可能引起进程阻塞,但它可允许N个读执行单元同时访问共享资源,而最多只能有一个写执行单元。 因此,读写信号量是一种相对放宽条件的粒度稍大于信号量的互斥机制。剩下的机制此处不展开说明,感兴趣可以自行搜索查阅。

例子

此处以字符驱动设备为例,通过引入读写信号量,实现互斥访问(可以多人读,只能一人写).

/****************字符驱动源码helloDev.C************/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/slab.h>

#define BUFFER_MAX (64)
#define OK (0)
#define ERROR (-1)

struct cdev *gDev;
struct file_operations *gFile;
dev_t devNum;
unsigned int subDevNum = 1;
int reg_major  = 232;
int reg_minor = 0;
char *buffer;
struct rw_semaphore sema;
wait_queue_head_t queue; //等待队列
int open_count = 0;
static char kerbuf[BUFFER_MAX];
 
void hello_delay(void)
{
   
	wait_event_timeout(queue,0,5*HZ);
}

int hello_open(struct inode *p, struct file *f)
{
   
	printk(KERN_INFO "hello_open ok");
	return 0;
}
int 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值