博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
linux kernel同步之mutex
阅读量:4056 次
发布时间:2019-05-25

本文共 2557 字,大约阅读时间需要 8 分钟。

互斥锁,顾名思义,就是互斥的,独占的,只能有一个进程占有锁,其他进程都得等待锁占有者释放互斥锁,才有可能占有锁。

以下的内容是基于linux-3.11.1(arm)内核代码来讲解内核中mutex的实现。

mutex的定义:

linux-3.11.1/include/linux/mutex.h

其中, count表示空闲资源的数目,初始值为1,表示只有一个资源可用,即只能一个进程可以用。

count: 1 表示可以lock

count: 0 表示锁被某个进程lock了,且没有其他进程来lock

count: -1表示锁被某个进程lock了,且至少有一个进程来lock。

初始化API:mutex_init()

linux-3.11.1/include/linux/mutex.h

linux-3.11.1/kernel/mutex.c

__mutex_init()初始化lock->count为1,初始化lock->ower为NULL。以及初始化等待spin_lock和等待队列wait_list.

上锁API:mutex_lock()

linux-3.11.1/kernel/mutex.c

我们不考虑CONFIG_PREEMPT_VOLUNTARY=y,那么might_sleep()为空。

linux-3.11.1/include/asm-generic/mutex-xchg.h

atomic_xchg(a,b)是将原子变量a设置为b,并将设置之前a的旧值返回。

我们看以下arm下这个的实现:

linux-3.11.1/arch/arm/include/asm/atomic.h

linux-3.11.1/arch/arm/include/asm/cmpxchg.h

好了,我们回到__mutex_fastpath_lock()。

28行,如果本次lock之前没有其他进程占用锁,则,atomic_xchg()返回1,count被设置为0,表示锁被占用(第一个进程成功lock);否则,atomic_xchg()返回0或负数。28行如果失败,说明之前有其他进程占用锁了,那个占用锁的进程会将count设置为0,然后34行被调用,将count设置为-1,表明有其他进程等待这个mutex锁释放。

注意,在28行到34行之间,有可能那个占有锁的进程释放了锁,则34行有可能锁成功,即atomic_xchg()在设置count为-1的时候,返回1(锁占用成功)。

如果34行返回值不是1,则,count被设置为-1了,然后调用fail_fn(),即调用__mutex_lock_slowpath()

linux-3.11.1/kernel/mutex.c

__mutex_lock_slowpath()调用了__mutex_lock_common()。

linux-3.11.1/kernel/mutex.c

418行: 禁内核抢占

514行: spin lock锁上(多核在这个地方是互斥的,并且排队)

520-521行: 将当前进程加到锁的等待队列lock->wait_list,这个入等待队列是排队的

528-550行: 循环检查锁计数是否为1,为1表明锁可占用,跳出循环,否则将当前进程设置为uninterrupt状态(只能被锁可用唤醒,不能被信号唤醒,不能被Kill),然后释放spin lock,调用schedule_preemt_disabled()去使能内核抢占,并主动调用schedule()让出cpu。当锁占有者释放锁的时候,会唤醒等待队列lock->wait_list中的第一个task. 这个task被唤醒后,从schedule_preemt_disable() -> schedule()返回,然后禁内核抢占。

549行,spin lock锁上。

当task占有锁从循环中break出来后,检查等待队列是否还有等待的task,如果没有,则将锁的值设置为0,然后释放spin lock,使能内核抢占。这样就完成了锁的占有操作。

 

锁的释放API: mutex_unlock()

linux-3.11.1/kernel/mutex.c

linux-3.11.1/arch/arm/include/asm/cmp-xchg.h

mutex_unlock() -> __mutex_fastpath_unlock()将count设置为1,然后判断设置之前count是否是0,不是0,说明至少有一个task在等待锁。然后调用fail_fn(),即调用__mutex_unlock_slowpath()。

这个时候(70行之后),可能有其他task会来上锁,因为70行将count设置为1了。

我们继续看__mutex_unlock_slowpath()。

linux-3.11.1/kernel/mutex.c

__mutex_unlock_slowpath() -> __mutex_unlock_common_slowpath在723行spin lock锁上。

725-734行:判断等待队列是否有task在等待该锁,有则唤醒第一个task。这里只是唤醒,被唤醒的task还没run,这样它就需要和这个点之后来的task竞争锁了。

736行: spin lock释放。

所以,函数并没有处理锁计数刚恢复1时被其他刚来的task占用的”插队问题”,使用mutex_lock尤其要注意这一点,即mutex()不保证lock的先后次序。

 

由于mutex_lock()获取锁失败后,进入uninterrupt状态,只能被锁可用唤醒,不能被信号唤醒,也不能被kill,故还存在下面两个api:

1. mutex_lock_interruptible()  //获取锁失败,进入interrupt状态,可以被锁可用唤醒,也可以被信号唤醒。

2. mutex_lock_killable() //获取锁失败,进入wakekill | uninterrupt状态,只能被kill信号或锁可用唤醒。

最后,由于mutex()获取锁失败时,会调用schedule()让出cpu,故mutex不能用于中断程序中。

转载地址:http://gtlci.baihongyu.com/

你可能感兴趣的文章
电阻屏较准
查看>>
imx6 内核停止启动
查看>>
RTL8188EUS Anaroid M Porting
查看>>
omap 的framebuffer驱动程序
查看>>
android2.3 dvsdk
查看>>
QT Creater的安装配置
查看>>
QT5学习总结
查看>>
ubuntu 安装使用dbus
查看>>
QT QDbus
查看>>
android init launch
查看>>
nand booting
查看>>
android boot animation
查看>>
Python环境搭建
查看>>
Python3 基础
查看>>
Python3 基础(itcast学习笔记)
查看>>
ubuntu_fastboot
查看>>
ubuntu18.04 Android 7.1.2 编译配置
查看>>
s6e3ha3 amoled屏
查看>>
fts touchscreen
查看>>
gpio pinctrl
查看>>