Linux设备驱动程序 之 信号量和互斥体

摘要:
概念信号量本质上是一个整数值,它与一系列函数一起使用。这对函数通常称为P和V;希望进入关键区域的进程将在相关信号量上调用P;如果信号量值大于零,该值将减少1,进程可以继续执行;相反,如果信号量值为0或更小,则进程必须等待其他人释放信号量;通过调用V来完成信号量的解锁;此函数增加信号量的值,并在必要时唤醒等待进程;当信号量用于互斥时(即,避免多个进程同时在关键区域运行),信号量的值
概念

一个信号量本质是一个整数值,它和一堆函数联合使用,这一对函数通常称为P和V;希望进入临界区的进程将在相关信号量上调用P;如果信号量的值大于零,则该值会减少1,进程可以继续执行;相反,如果信号量的值为0或者更小,则进程必须等待知道其他人释放该信号量;对信号量的解锁通过调用V完成;该函数增加信号量的值,并在必要时唤醒等待的进程;

当信号量用于互斥时(即避免多个进程同时在一个临界区中运行),信号量 的值应该初始化为1;这种信号量在任何给定时刻只能由单个进程或者线程拥有;这种使用模式下,一个信号量有时也成为一个互斥体(mutex),它是互斥的简称;

Linux内核汇总几乎所有的信号量均用于互斥体;

信号量的使用

Linux内核遵守上述语义提供了信号量的实现,要使用信号量,内核代码必须包含<linux/semaphore.h>,相关的类型是struct semaphore;

实际的信号量可以通过集中途径来生命和初始化;

直接创建信号量,其中val参数是赋予一个信号量的初始值;

1 static inline void sema_init(struct semaphore *sem, int val)

Linux中P函数被称为down–或者这个名字的变种;该类函数减小了信号量的值,它也许会将调用者置于休眠状态,然后等待信号量变为可用,之后授予调用者对保护资源的访问;作为通常规则,我们不应该使用非中断的操作;down的几个变种函数都有返回值,需要始终进行检查;

Linux中V函数被称为up,该函数增加了信号量的值,使用该函数后,调用者不再拥有该信号量;任何down操作都需要对应进行up操作,特别注意在错误分支中对已持有信号量的释放;

1 void down(struct semaphore *sem);
2 int __must_check down_interruptible(struct semaphore *sem);
3 int __must_check down_killable(struct semaphore *sem);
4 int __must_check down_trylock(struct semaphore *sem);
5 int __must_check down_timeout(struct semaphore *sem, long jiffies);
6 void up(struct semaphore *sem);
读写信号量的使用

许多任务可以划分成两种不同的工作类型:一些任务只需要读取受保护的数据结构,而其他的则必须做出修改;允许多个并发读取是可能的,只要它们中没有哪个要做修改;这样做可以大大的提高性能,以内容只读任务可以并行的完成它们的工作,而不需要等待其他读取者退出临界区;

内核中为这种情形提供了一种特殊的信号量类型,rwsem,虽然使用比较少,但偶尔也比较有用;

使用rwsem必须包含头文件<linux/rwsem.h>,对应的数据类型是struct rw_semaphore;

在使用之前需要使用init_rwsem宏进行初始化;

1 #define init_rwsem(sem)                        
2 do {                                
3     static struct lock_class_key __key;            
4                                 
5     __init_rwsem((sem), #sem, &__key);            
6 } while (0)

down_read系列函数提供了对保护资源的只读访问,可以和其他读取者并发的访问;down_write则提供了对保护资源的写访问,与其他读写着互斥;一个rwsem允许一个写入者和多个读取者拥有该信号量;up_xxx操作则用于释放已经持有的信号量;

 1 /*
 2  * lock for reading
 3  */
 4 extern void down_read(struct rw_semaphore *sem);
 5 
 6 /*
 7  * trylock for reading -- returns 1 if successful, 0 if contention
 8  */
 9 extern int down_read_trylock(struct rw_semaphore *sem);
10 
11 /*
12  * lock for writing
13  */
14 extern void down_write(struct rw_semaphore *sem);
15 extern int __must_check down_write_killable(struct rw_semaphore *sem);
16 
17 /*
18  * trylock for writing -- returns 1 if successful, 0 if contention
19  */
20 extern int down_write_trylock(struct rw_semaphore *sem);
21 
22 /*
23  * release a read lock
24  */
25 extern void up_read(struct rw_semaphore *sem);
26 
27 /*
28  * release a write lock
29  */
30 extern void up_write(struct rw_semaphore *sem);
31 
32 /*
33  * downgrade write lock to read lock
34  */
35 extern void downgrade_write(struct rw_semaphore *sem);
互斥体的使用

Linux内核互斥体之前是以val为1的信号量存在的,现在已经单独实现;使用互斥体需要包含<linux/mutex.h>头文件;

mutex_init宏完成对互斥量的初始化;

1 #define mutex_init(mutex)                        
2 do {                                    
3     static struct lock_class_key __key;                
4                                     
5     __mutex_init((mutex), #mutex, &__key);                
6 } while (0)

其中包含了一些列的lock与unlock操作,如下所示,其中mutex_lock_xxx表示在进入临界区之前加锁的操作,mutex_unlock操作表示退出临界区的解锁的操作;

 1 extern void mutex_lock(struct mutex *lock);
 2 extern int __must_check mutex_lock_interruptible(struct mutex *lock);
 3 extern int __must_check mutex_lock_killable(struct mutex *lock);
 4 extern void mutex_lock_io(struct mutex *lock);
 5 
 6 # define mutex_lock_nested(lock, subclass) mutex_lock(lock)
 7 # define mutex_lock_interruptible_nested(lock, subclass) mutex_lock_interruptible(lock)
 8 # define mutex_lock_killable_nested(lock, subclass) mutex_lock_killable(lock)
 9 # define mutex_lock_nest_lock(lock, nest_lock) mutex_lock(lock)
10 # define mutex_lock_io_nested(lock, subclass) mutex_lock(lock)
11 #endif
12 
13 /*
14  * NOTE: mutex_trylock() follows the spin_trylock() convention,
15  *       not the down_trylock() convention!
16  *
17  * Returns 1 if the mutex has been acquired successfully, and 0 on contention.
18  */
19 extern int mutex_trylock(struct mutex *lock);
20 extern void mutex_unlock(struct mutex *lock);
21 
22 extern int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock);

免责声明:文章转载自《Linux设备驱动程序 之 信号量和互斥体》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇LXC-Linux Containers介绍wordpress主题升级之后返回到原来版本主题的方法下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

ARM Linux BenchMark【转】

转自:https://blog.csdn.net/sy373466062/article/details/49070025 背景说明 许多公司有很多不同的ARM SoC的研发产品,ARM核心可能有Cortex-A8/A9/A15,核心数可能有单核双核和四核.现在,几乎每出一款手机,网络上马上就有人对其评测.对比和评测不同ARM SoC 芯片以及嵌入式系统是...

linux安装配置jdk、tomcat、开机自启动

1、安装JDK 1.1、官网下载jdk 1.2、源码包解压 jdk源码包上传到/usr/local(软件一般安装到这个目录) 使用解压命令解压 [root@localhost local]# tar -zxvf jdk-8u301-linux-x64.tar.gz 1.3、配置jdk环境变量 /etc/profile文件的改变会涉及到系统的环境,我们要将j...

Linux less grep

第一步,less查看文件 less 日志文件名 1,这时候,使用组合键 shift + g 可以定位到文件末尾。 在文件末尾,使用组合键(从末尾开始根据之后输入的字符串向上检索) shift + ? 然后输入查询条件,可以是时间串,也可以是关键字,比如日志有打印userid,则输入有问题的userid,检测所有有该userid出现过的日志。 使用...

GCC编译器原理(一)------交叉编译器制作和GCC组件及命令

1.1 交叉编译器制作 默认安装的 GCC 编译系统所产生的代码适用于本机,即运行 GCC 的机器,但也可将 GCC 安装成能够生成其他的机器代码。安装一些必须的模块,就可产生多种目标机器代码,而且可通过命令行选择一种希望使用的代码。 1.1.1 目标机 从网站 http://gcc.gnu.org/install/specific.html 可以得到有可...

go build 不同系统下的可执行文件

Golang 支持在一个平台下生成另一个平台可执行程序的交叉编译功能。 1、Mac下编译Linux, Windows平台的64位可执行程序: 1 2 $ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build test.go $ CGO_ENABLED=0 GOOS=windows GOARCH=amd6...

kvm虚拟机磁盘文件读取小结

kvm虚拟机磁盘挂载还真不是一帆风顺的。xen虚拟化默认就raw格式的磁盘,可以直接挂载,kvm如果采用raw也可以直接挂载,与xen磁盘挂载方式一致。 本文出自:http://koumm.blog.51cto.com 1.kvm虚拟化相比xen虚拟化来说,工具与方法众多,本文列举思路如下: (1)raw格式的磁盘可以直接挂载,可以将qcow2磁盘转换成r...