Linux 锁

日期:2023-01-15

版本号:V1.0

一、自旋锁

1.1、基础版自旋锁

1.1.1 原理

自旋锁是基于硬件的锁定原语。需要由硬件实现原子操作(例如test_and_set,在非原子实现中,这将导致多种不同的同步问题)。自旋锁本质上就是不允许或不需要休眠的原子操作(上下文)。由于SMP的快速发展,自旋锁更多的应用于多核之间的同步(理解为CPU之间的强制性硬件同步,等待资源的CPU需要一直监听资源锁的释放)。例如下图中CPUA对自旋锁上锁后,CPUB由于无法请求到该锁,所以进行自旋阻塞等待(即不得进行休眠或者进程切换),等到CPUA释放资源后,CPUB也进行上锁并继续执行的操作。自旋锁可使多核之间的通信更加安全

 

1.1.2 接口

DEFINE_SPINLOCK(x)

spin_lock_init()

spin_unlock

spin_lock

1.2、中断自旋锁

1.2.1 原理

但基础版自旋锁有一些限制:虽然自旋锁能够阻止CPU被其他进程抢占(进程上下文),但它并不能阻止该CPU被中断抢占(中断上下文)。例如当进程A创建了一个“自旋锁”以保护给定的资源时,若在上锁期间发生中断了。但这个IRQ处理程序需要获取相同的自旋锁(进程上下文与中断上下文处理程序数据共享)。它将无限旋转,并试图获取一个已经被它抢占的任务锁定的锁。

为了避免这种情况,Linux系统提供了一组关于中断的自旋锁API,在给自旋锁上锁前,系统会首先关闭中断,以防止上文所述的情况,当资源使用完毕后并解锁后,也相应的进行中断打开操作。

1.2.2 接口

spin_unlock_irq

spin_lock_irq

1.3、中断恢复自旋锁

1.3.1 原理

通过开关中断虽然解决了中断上下文与进程上下文之间的同步问题,但也给中断处理这边带来了不必要的麻烦。例如,在代码进入临界区之前,处理器上已经禁用了中断。因此,当调用spin_unlock_irq时,不仅会释放锁,还会启用中断。显然,这与系统预期行为并不一致。

为了避免中断状态的恢复问题,内核提供了一组中断状态保存以及恢复的自旋锁操作API,在上锁时同时进行中断使能状态的保存以及中断关闭,在解锁时则进行对应中断使能状态的恢复。

当然,通过关闭中断能够禁止进程的被动切换,但相关中断的关闭,会使相关的调度器(典型例子是基于时间中断的调度器)将不会被触发。而临界区中又有可能主动切调用调度器。在内核中,使用了preempt_count变量来应对此问题。当自旋锁加锁时,会将preempt_count(初始为0)进行+1;当preempt_count>1时,调用调度器时,调度器只返回正常值,而不执行相关内容(可以等效理解位调度器启动,然后又强制切换回当前进程);当preempt_count=0时,则真实进行相关的调度。

1.3.2 接口

spin_lock_irqsave

spin_unlock_irqrestore

二、互斥锁

2.1、基础版互斥锁

2.1.1 原理

互斥锁同样是一种锁定原语。它的原理和作用与自旋锁基本一致,唯一的区别是等待互斥锁的进程可以休眠。如果TASKA需要等待一个由TASKB占有的互斥锁,那么TASKA将会让自己进入休眠状态,而唤醒他的方式是TASKB释放互斥锁(TASKA主动释放了CPU的执行权,由调度器对CPU资源进行再分配)。由于没有自旋过程,取而代之的是休眠过程,所以内核可以进行任务的切换,所以互斥锁也可以理解为专门针对任务的锁(锁的是任务,而不是像自旋锁一样直接锁CPU硬件资源)。

TASKA创建互斥锁MUTEX后,随之对其进行上锁操作,TASKB此时申请访问该锁失败,则将其自身的进程号(识别号)加入到MUTEX的等待进程中,当TASKA释放锁时,需要检查该锁是否存在等待进程,若有则进行唤醒。

由于互斥锁中不同进程(任务)都会访问MUTEX的等待进程,所以对于互斥锁而言,自身应该是可以任意共享的,但自身的等待进程不能进行互斥操作,所以互斥锁本身还需要依靠了自旋锁对等待进程这个特殊成员进行加锁。

2.1.2 接口

mutex_lock

mutex_unlock

mutex_init

mutex_is_locked

mutex_trylock

2.2、信号唤醒互斥锁

2.2.1 原理

正常情况下,TASKB应该等待TASKA释放互斥锁后,才能进行下一步操作,但是有可能在等待期间出现了更加紧急的事情-信号(你在等待校车接你上学,但学校突然给你发了一个疫情居家隔离的信号),默认情况下TASKB会继续等待校车的到来(TASK_UNINTERRUPTIBLE),但更多的机智的学生选择了(TASK_INTERRUPTIBLE),这时候,系统会首先按照预定的应急处理机制(信号处理函数)对相应信号处理,然后从之前等待位置进行返回,并且返回退出原因是收到了信号(是学校让我居家的,不是我想逃课!)

2.2.2 接口

mutex_lock_interruptible

2.3、kill信号唤醒互斥锁

2.3.1 原理

原理与上节一致,但是只响应kill信号,其他信号并不能唤醒该进程(除非学校被炸,否则你就得接着上学)

2.3.2 接口

mutex_lock_killable

三、休眠/等待机制

3.1 原理

原理同互斥锁,等待的对象从资源扩展某个事件/动作的发生。等待的数量变多,有很多学生在等待校车,且随时增加或不等待,但是每班校车可能提供提供足额、部分或没有位置等不同情况。等待资源数量增加(学校有多辆校车,并不是非得某辆才行)

3.2 接口

wait_for_completion_interruptible

wait_for_completion_interruptible_timeout

wait_for_completion_killable

wait_for_completion_killable_timeout

wait_for_completion_timeout

wait_event

wait_event_interruptible

wait_event_timeout

…………

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值