英文购物网站模板,珠海网站建设怎么样,备案系统,奢侈品购物网站排名嵌入式Linux应用开发-驱动大全-同步与互斥④ 第一章 同步与互斥④1.5 自旋锁spinlock的实现1.5.1 自旋锁的内核结构体1.5.2 spinlock在UP系统中的实现1.5.3 spinlock在SMP系统中的实现 1.6 信号量semaphore的实现1.6.1 semaphore的内核结构体1.6.2 down函数的实现1.6.3 up函数的… 嵌入式Linux应用开发-驱动大全-同步与互斥④ 第一章 同步与互斥④1.5 自旋锁spinlock的实现1.5.1 自旋锁的内核结构体1.5.2 spinlock在UP系统中的实现1.5.3 spinlock在SMP系统中的实现 1.6 信号量semaphore的实现1.6.1 semaphore的内核结构体1.6.2 down函数的实现1.6.3 up函数的实现 1.7 互斥量mutex的实现1.7.1 mutex的内核结构体1.7.2 mutex_lock函数的实现1.7.2.1 fastpath1.7.2.2 slowpath 1.7.3 mutex_unlock函数的实现1.7.3.1 fastpath1.7.3.2 slowpath 第一章 同步与互斥④ 1.5 自旋锁spinlock的实现
自旋锁顾名思义自己在原地打转等待资源可用一旦可用就上锁霸占它。 问题来了假设别人已经上锁了你原地打转会占住 CPU资源了别的程序怎么运行它没有 CPU怎么解锁 这个问题有 2个答案 ① 原地打转的是 CPU x以后 CPU y会解锁这涉及多个 CPU适用于 SMP系统 ② 对于单 CPU系统自旋锁的“自旋”功能就去掉了只剩下禁止抢占、禁止中断 我先禁止别的线程来打断我(preempt_disable)我慢慢享用临界资源用完再使能系统抢占(preempt_enable)这样别人就可以来抢资源了。 注意SMP就是 Symmetric Multi-Processors对称多处理器UP即 Uni-Processor系统只有一个单核 CPU。 要理解 spinlock要通过 2个情景来分析 ① 一开始怎么争抢资源不能 2个程序都抢到。 这挺好解决使用原子变量就可以实现。 ② 某个程序已经获得资源怎么防止别人来同时使用这个资源。 这是使用 spinlock时要注意的地方对应会有不同的衍生函数(_bh/_irq/_irqsave/_restore)。
1.5.1 自旋锁的内核结构体
spinlock对应的结构体如下定义不同的架构可能有不同的实现
上述__raw_tickets结构体中有 owner、next两个成员这是在 SMP系统中实现 spinlock的关键。
1.5.2 spinlock在UP系统中的实现
对于“自旋锁”它的本意是如果还没获得锁我就原地打转等待。等待谁释放锁 ① 其他 CPU ② 其他进程/线程 对于单 CPU系统没有“其他 CPU”如果内核不支持 preempt当前在内核态执行的线程也不可能被其他线程抢占也就“没有其他进程/线程”。所以对于不支持 preempt的单 CPU系统spin_lock是空函数不需要做其他事情。 如果单 CPU系统的内核支持 preempt即当前线程正在执行内核态函数时它是有可能被别的线程抢占的。这时 spin_lock的实现就是调用“preempt_disable()”你想抢我我干脆禁止你运行。 在 UP系统中spin_lock函数定义如下
从以上代码可知在 UP系统中 spin_lock()就退化为 preempt_disable()如果用的内核不支持 preempt那么 spin_lock()什么事都不用做。 对于 spin_lock_irq()在 UP系统中就退化为 local_irq_disable()和 preempt_disable()如下图所示 假设程序 A要访问临界资源可能会有中断也来访问临界资源可能会有程序 B也来访问临界资源那么使用 spin_lock_irq()来保护临界资源先禁止中断防止中断来抢再禁止 preempt防止其他进程来抢。 对于 spin_lock_bh()在 UP系统中就退化为禁止软件中断和 preempt_disable()如下图所示
对于 spin_lock_irqsave它跟 spin_lock_irq类似只不过它是先保存中断状态再禁止中断如下
对应的 spin_unlock函数就不再讲解。
1.5.3 spinlock在SMP系统中的实现
要让多 CPU中只能有一个获得临界资源使用原子变量就可以实现。但是还要保证公平先到先得。比如有 CPU0、CPU1、CPU2都调用 spin_lock想获得临界资源谁先申请谁先获得。 要想理解 SMP系统中 spinlock的实现得举一个例子。感谢这篇文章 Linux内核同步机制之四spin lock http://www.wowotech.net/kernel_synchronization/spinlock.html wowotech真是一个神奇的网站里面 Linux文章的作者统一标为“linuxer”牛 我借用这篇文章的例子讲解餐厅里只有一个座位去吃饭的人都得先取号、等叫号。注意有 2个动作顾客从取号机取号电子叫号牌叫号。 ① 一开始取号机待取号码为 0 ② 顾客 A从取号机得到号码 0电子叫号牌显示 0顾客 A上座 取号机显示下一个待取号码为 1。 ③ 顾客 B从取号机得到号码 1电子叫号牌还显示为 0顾客 B等待 取号机显示下一个待取号码为 2。 ④ 顾客 C从取号机得到号码 2电子叫号牌还显示为 0顾客 C等待 取号机显示下一个待取号码为 3。 ⑤ 顾客 A吃完离座电子叫号牌显示为 1顾客 B的号码等于 1他上座 ⑥ 顾客 B吃完离座电子叫号牌显示为 2顾客 C的号码等于 2他上座 在这个例子中有 2个号码取号机显示的“下一个号码”顾客取号后它会自动加 1电子叫号牌显示 “当前号码”顾客离座后它会自动加 1。某个客户手上拿到的号码等于电子叫号牌的号码时该客户上座。 在这个过程中即使顾客 B、C同时到店只要保证他们从取号机上得到的号码不同他们就不会打架。 所以关键点在于取号机的号码发放必须互斥保证客户的号码互不相同。而电子叫号牌上号码的变动不需要保护只有顾客离开后它才会变化没人争抢它。 在 ARMv6及以上的 ARM架构中支持 SMP系统。它的 spinlock结构体定义如下
owner就相当于电子叫号牌现在谁在吃饭。next就当于于取号机下一个号码是什么。每一个 CPU从取号机上取到的号码保存在 spin_lock函数中的局部变量里。 spin_lock函数调用关系如下核心是 arch_spin_lock
arch_spin_lock代码如下
图中的注释把原理讲得非常清楚了,即使不同的个体去同时取号也可以保证取到的号码各不相同。
假设第 1个程序取到了号码它访问了临界资源后调用 spin_unlock代码如下
假如有其他程序正在 spin_lock函数中循环等待它就会立刻判断自己手上的 next是否等于lock-tickets.owner如果相等就表示输到它获得了锁。 深入分析_linux_spinlock_实现机制 https://blog.csdn.net/electrombile/article/details/51289813 深入分析 Linux自旋锁 http://blog.chinaunix.net/uid-20543672-id-3252604.html Linux内核同步机制之四spin lock http://www.wowotech.net/kernel_synchronization/spinlock.html
1.6 信号量semaphore的实现
1.6.1 semaphore的内核结构体
注意这是信号量不是信号。在前面学习异步通知时驱动程序给应用程序发信号。现在我们讲的信号量是一种同步、互斥机制。 信号量的定义及操作函数都在 Linux内核文件 include\linux\semaphore.h中定义如下
初始化 semaphore之后就可以使用 down函数或其他衍生版本来获取信号量使用 up函数释放信号量。我们只分析 down、up函数的实现。
1.6.2 down函数的实现
如果 semaphore中的 count大于 0那么 down函数就可以获得信号量否则就休眠。在读取、修改 count时要使用 spinlock来实现互斥。 休眠时要把当前进程放在 semaphore的 wait_list链表中别的进程释放信号量时去 wait_list中把进程取出、唤醒。 代码如下
1.6.3 up函数的实现
如果有其他进程在等待信号量则 count值无需调整直接取出第 1个等待信号量的进程把信号量给它共把它唤醒。 如果没有其他进程在等待信号量则调整 count。 整个过程需要使用 spinlock来保护代码如下
1.7 互斥量mutex的实现
1.7.1 mutex的内核结构体
mutex的定义及操作函数都在 Linux内核文件 include\linux\mutex.h中定义如下
初始化 mutex之后就可以使用 mutex_lock函数或其他衍生版本来获取信号量使用 mutex_unlock函数释放信号量。我们只分析 mutex_lock、mutex_unlock函数的实现。 这里要堪误一下前面的视频里我们说 mutex中的 owner是用来记录获得 mutex的进程以后必须由它来释放 mutex。这是错的 从上面的代码可知owner并不一定存在 owner有 2个用途debug(CONFIG_DEBUG_MUTEXES)或 spin_on_owner(CONFIG_MUTEX_SPIN_ON_OWNER)。 什么叫 spin on owner 我们使用mutex的目的一般是用来保护一小段代码这段代码运行的时间很快。这意味着一个获得mutex的进程可能很快就会释放掉 mutex。 针对这点可以进行优化特别是当前获得 mutex的进程是在别的 CPU上运行、并且“我”是唯一等待这个 mutex的进程。在这种情况下那“我”就原地 spin等待吧懒得去休眠了休眠又唤醒就太慢了。 所以mutex是做了特殊的优化比 semaphore效率更高。但是在代码上并没有要求“谁获得 mutex就必须由谁释放 mutex”只是在使用惯例上是“谁获得 mutex就必须由谁释放 mutex”。
1.7.2 mutex_lock函数的实现
1.7.2.1 fastpath
mutex的设计非常精巧比 semaphore复杂但是更高效。 首先要知道 mutex的操作函数中有 fastpath、slowpath两条路径(快速、慢速)如果 fastpath成功就不必使用 slowpath。 怎么理解 这需要把 metex中的 count值再扩展一下之前说它只有 1、0两个取值1表示 unlocked0表示locked还有一类值“负数”表示“locked并且可能有其他程序在等待”。 代码如下
先看看 fastpath的函数__mutex_fastpath_lock这个函数在下面 2个文件中都有定义
include/asm-generic/mutex-xchg.h
include/asm-generic/mutex-dec.h 使用哪一个文件呢看看 arch/arm/include/asm/mutex.h内容如下
#if __LINUX_ARM_ARCH__ 6
#include asm-generic/mutex-xchg.h
#else
#include asm-generic/mutex-dec.h
#endif 所以对于 ARMv6以下的架构使用 include/asm-generic/mutex-xchg.h中的__mutex_fastpath_lock函数对于 ARMv6及以上的架构使用 include/asm-generic/mutex-dec.h中的__mutex_fastpath_lock函数。这 2个文件中的__mutex_fastpath_lock函数是类似的mutex-dec.h中的代码如下
大部分情况下mutex当前值都是 1所以通过 fastpath函数可以非常快速地获得 mutex。
1.7.2.2 slowpath
如果 mutex当前值是 0或负数则需要调用__mutex_lock_slowpath慢慢处理可能会休眠等待。
__mutex_lock_common函数也是在内核文件 kernel/locking/mutex.c中实现的下面分段讲解。 ① 分析第一段代码
② 分析第二段代码
③ 分析第三段代码
这个 wait_list是 FIFO(Firt In Firs Out)谁先排队谁就可以先得到 mutex。
④ 分析第四段代码for循环这是重点
⑤ 分析第五段代码收尾工作
1.7.3 mutex_unlock函数的实现
mutex_unlock函数中也有 fastpath、slowpath两条路径(快速、慢速)如果 fastpath成功就不必使用 slowpath。 代码如下
1.7.3.1 fastpath
先看看 fastpath的函数__mutex_fastpath_lock这个函数在下面 2个文件中都有定义
include/asm-generic/mutex-xchg.h
include/asm-generic/mutex-dec.h 使用哪一个文件呢看看 arch/arm/include/asm/mutex.h内容如下
#if __LINUX_ARM_ARCH__ 6
#include asm-generic/mutex-xchg.h
#else
#include asm-generic/mutex-dec.h
#endif 所以对于 ARMv6以下的架构使用 include/asm-generic/mutex-xchg.h中的__mutex_fastpath_lock函数对于 ARMv6及以上的架构使用 include/asm-generic/mutex-dec.h中的__mutex_fastpath_lock函数。这 2个文件中的__mutex_fastpath_lock函数是类似的mutex-dec.h中的代码如下
大部分情况下加 1后 mutex的值都是 1表示无人等待 mutex所以通过 fastpath函数直接增加 mutex的 count值为 1就可以了。 如果 mutex的值加 1后还 是小于等于 0就表示有人在等待 mutex需要去 wait_list把它取出唤醒这需要用到 slowpath的函数__mutex_unlock_slowpath。
1.7.3.2 slowpath
如果 mutex当前值是 0或负数则需要调用__mutex_unlock_slowpath慢慢处理需要唤醒其他进程。
__mutex_unlock_common_slowpath函数代码如下主要工作就是从 wait_list中取出并唤醒第 1个进程