首页 > 编程知识 正文

互斥锁和信号量,互斥锁原理

时间:2023-05-05 17:06:35 阅读:116928 作者:1280

linux内核有多个内核锁。 内核锁定的作用如下:

在多核处理器中,多个进程可能处于内核状态,但在内核状态下,进程可以访问所有内核数据,因此必须保护或独占处理共享数据。

linux内核锁定机制包括信号量、排他锁定、旋转锁定和原子操作。

一、信号量:

一种用于解决进程/线程之间同步和互斥问题的通信机制,用于防止同时调用两个或多个重要代码。

“信号”(Saphore )由值和指针组成,指针指向等待信号的过程。 信号量值表示相应资源的使用情况。 信号量S=0时,s表示可用资源的数量。 由于执行一次p操作意味着请求资源分配,因此s的值将减去1。对于S0,表示不再有可用资源,s的绝对值表示当前正在等待该资源的进程数。 请求者必须等待其他进程释放此类资源,然后才能继续执行。 执行v操作意味着释放资源,因此对于将s的值加1的S0,由于存在正在等待资源的进程,因此调用等待状态的进程使其运行。

信号选择睡眠方式以停止对共享工作的访问。

也就是说,信号量通过PV操作同步解决了进程/线程临界资源利用的冲突问题

二、互斥锁: (mutex_lock )

独占锁定也是线程之间“同步到进程”和“独占”的另一种机制。

独占锁定经常强调对共享资源的锁定作用,如果一个线程占用当前共享资源并使用独占锁定进行锁定,其他线程将无法访问。 如果不等待unlock,其他线程将无法使用共享资源的内容。

互斥锁选择休眠方式以停止对共享工作的访问。

也就是说,排他锁定通过共享资源的锁定和排他锁定来解决利用资源的冲突问题

三.旋转锁定(spin_lock ) :

为实现共享资源的保护提出锁定机制。 其实,自旋锁与排他锁相似,都是为了解决某些资源的排他使用。 无论是排他锁定还是旋转锁定,在任何时刻最多只能有一个保持者。 也就是说,在任何时刻最多只能获得一个执行单元的锁定。 但是两者的调度机制略有不同。 对于互斥锁,如果资源已经被占用,则资源申请者只能进入休眠状态。 但是,自旋锁不会引起呼叫方的睡眠。 如果旋转锁已经由另一个执行单元保持,则调用程序会一直在那里循环,以查看该旋转锁的所有者是否已释放该锁定。 “自旋”这个词就是为此而命名的。

四.原子操作:

参考:

4.1、Linux原子概念:

原子操作是指“不能中断的一个或一系列操作”。

原子操作是指在更高水平上被中断而不能剥夺优先操作。 既然你提出了这个问题,我就说得深一点。 由于操作系统大部分时间都打开了中断,因此在程序运行过程中,高优先级线程可能会发生中断。 部分操作无法中断。 否则,就会造成无法复原的结果。 在这种情况下,这些操作需要原子操作。 是不能被中断的操作。

硬件级原子操作:在单处理器系统(UniProcessor )中,中断只发生在指令的边缘,因此一个指令可以执行的所有操作都可以视为“原子操作”。 虽然在多处理器架构中不同,但由于系统内多个处理器独立地动作,所以在一个指令上执行的操作也可能受到干扰。 在X86平台上,CPU提供了一种在命令运行时锁定总线的方法。 CPU上的北桥连接有导线#HLOCK pin。 如果在汇编语言程序中在指令前加上前缀“LOCK”,则汇编的机器码会使CPU在执行该指令时降低#HLOCK pin的电位,开放并锁定总线,直到该指令结束,同一总线上的其他CPU会暂时这是有保证的。对于其他平台的CPU,实现有很多种,包括通过关闭中断来实现原子操作的(sparc ),以及通过CMPXCHG类指令来实现原子操作的(IA64 )。 本文主要探讨在X86平台上原子操作的实现。

4.2、Linux内核两组原子操作界面:

1、原子整数操作

原子操作通常对int或bit类型的数据执行,但Linux并不是直接对int执行原子操作,只能通过atomic_t的数据结构执行。

#include中定义

图1.1内核中的整数原子操作函数

2 .内核提供的几个主要位原子操作函数。 内核还提供了一组与上述操作相对应的非原子位操作函数,在名称前添加了两条下划线。 因为原子性得不到保证,所以有可能更快地执行。

#include中定义

图1.2内核中的位原子操作函数

1voidatomic_set(atomic_t*v,int i ); //将原子变量v的值设为I

2atomic_tv=atomic_init(0; //定义原子变量v,初始化为0

3

4atomic_read(atomic_t*v; //返回原子变量v的值;

5

6voidatomic_add(intI,atomic_t* v ); //原子变量v增加

加i;

7 void atomic_sub(int i, atomic_t*v);8

9 void atomic_inc(atomic_t* v); //原子变量增加1;

10 void atomic_dec(atomic_t*v);11

12 int atomic_inc_and_test(atomic_t* v); //先自增1,然后测试其值是否为0,若为0,则返回true,否则返回false;

13 int atomic_dec_and_test(atomic_t*v); //先自减1,然后测试其值是否为0,若为0,则返回true,否则返回false14 int atomic_sub_and_test(int i, atomic_t* v); //先减i,然后测试其值是否为0,若为0,则返回true,否则返回false;

15 //注意:只有自加,没有加操作16

17 int atomic_add_return(int i, atomic_t* v); //v的值加i后返回新的值;

18 int atomic_sub_return(int i, atomic_t*v);19 int atomic_inc_return(atomic_t* v); //v的值自增1后返回新的值;

20 int atomic_dec_return(atomic_t* v);

实例代码:

在scull_open 函数和scull_close函数中:

如果没有进程在使用该驱动 ,原子变量值 为 1 ,将原子变量减 一 为 0 ,函数返回 true ,再!true 为 假 ,if 里面的代码不执行;这样打开了、并使用该驱动, 原子变量变为 0;

如果再有进程来打开驱动程序,0-1 = 负1,返回 false ,if 条件成立,运行里面的代码,将原子变量加一恢复到  0,程序返回;

最后, 在应用程序退出时 close 函数, 自增 恢复原子变量值为 1:

1 static atomic_t scull_available = ATOMIC_INIT(1);      //init atomic

2

3 int scull_open(struct inode *inode, struct file *filp)4 {5     struct scull_dev *dev;         //device information

6

7     dev = container_of(inode->i_cdev, structscull_dev, cdev);8     filp->private_data = dev;         //for other methods

9     if(!atomic_dec_and_test(&scull_available)){10         atomic_inc(&scull_available);11         return -EBUSY;12 }13     return 0;         //success

14 }15

16 int scull_release(struct inode *inode, struct file *filp)17 {18     atomic_inc(&scull_available);19     return 0;20 }

以上总结几点:

互斥锁与信号量的区别:

1、信号量一般以同步的方式对共享资源进行控制,而互斥锁通过互斥的方式对共享资源对其进行控制;

2、信号量可以对进程的共享资源进行控制,而互斥锁不行;

3、信号量的值为非负整数,而互斥锁的值只能为0或1;

4、互斥量的加锁和解锁必须由同一线程分别对应使用,信号量可以由一个线程释放,另一个线程得到;mutex和二值信号量的区别在于mutex必须是同一个进程来释放

自旋锁与互斥锁的区别:

1、因为自旋锁不会引起调用者睡眠,所以效率比较高

2、自旋锁比较适用于锁使用者保持锁时间比较短的情况。

3、自旋锁容易造成死锁,所以需要安全使用它;

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。