百度做网站多少钱能做,wordpress人机验证,如何做企业网站内容策划,国内优秀网页鉴赏嵌入式Linux应用开发-驱动大全-同步与互斥③ 第一章 同步与互斥③1.4 Linux锁的介绍与使用1.4.1 锁的类型1.4.1.1 自旋锁1.4.1.2 睡眠锁 1.4.2 锁的内核函数1.4.2.1 自旋锁1.4.2.2 信号量1.4.2.3 互斥量1.4.2.4 semaphore和 mutex的区别 1.4.3 何时用何种锁1.4.4 内核抢占(pree… 嵌入式Linux应用开发-驱动大全-同步与互斥③ 第一章 同步与互斥③1.4 Linux锁的介绍与使用1.4.1 锁的类型1.4.1.1 自旋锁1.4.1.2 睡眠锁 1.4.2 锁的内核函数1.4.2.1 自旋锁1.4.2.2 信号量1.4.2.3 互斥量1.4.2.4 semaphore和 mutex的区别 1.4.3 何时用何种锁1.4.4 内核抢占(preempt)等额外的概念1.4.5 使用场景1.4.5.1 只在用户上下文加锁1.4.5.2 在用户上下文与 Softirqs之间加锁1.4.5.3 在用户上下文与 Tasklet之间加锁1.4.5.4 在用户上下文与 Timer之间加锁1.4.5.5 在 Tasklet与 Timer之间加锁1.4.5.6 在 Softirq之间加锁1.4.5.7 硬中断上下文 第一章 同步与互斥③ 1.4 Linux锁的介绍与使用
本节参考 https://www.kernel.org/doc/html/latest/locking/index.html https://mirrors.edge.kernel.org/pub/linux/kernel/people/rusty/kernel-locking/
1.4.1 锁的类型
Linux内核提供了很多类型的锁它们可以分为两类 ① 自旋锁(spinning lock) ② 睡眠锁(sleeping lock)。
1.4.1.1 自旋锁
简单地说就是无法获得锁时不会休眠会一直循环等待。有这些自旋锁 自旋锁的加锁、解锁函数是spin_lock、spin_unlock还可以加上各种后缀这表示在加锁或解锁的同时还会做额外的事情
1.4.1.2 睡眠锁
简单地说就是无法获得锁时当前线程就会休眠。有这些休眠锁
1.4.2 锁的内核函数
1.4.2.1 自旋锁
spinlock函数在内核文件 include\linux\spinlock.h中声明如下表
自旋锁的加锁、解锁函数是spin_lock、spin_unlock还可以加上各种后缀这表示在加锁或解锁的同时还会做额外的事情
1.4.2.2 信号量
semaphore semaphore函数在内核文件 include\linux\semaphore.h中声明如下表
1.4.2.3 互斥量
mutex mutex函数在内核文件 include\linux\mutex.h中声明如下表
1.4.2.4 semaphore和 mutex的区别
semaphore中可以指定 count为任意值比如有 10个厕所所以 10个人都可以使用厕所。 而 mutex的值只能设置为 1或 0只有一个厕所。 是不是把 semaphore的值设置为 1后它就跟 mutex一样了呢不是的。 看一下 mutex的结构体定义如下
它里面有一项成员“struct task_struct *owner”指向某个进程。一个 mutex只能在进程上下文中使用谁给 mutex加锁就只能由谁来解锁。 而 semaphore并没有这些限制它可以用来解决“读者-写者”问题程序 A在等待数据──想获得锁程序 B产生数据后释放锁这会唤醒 A来读取数据。semaphore的锁定与释放并不限定为同一个进程。 主要区别列表如下
1.4.3 何时用何种锁
本节参考https://wenku.baidu.com/view/26adb3f5f61fb7360b4c656e.html 英文原文https://mirrors.edge.kernel.org/pub/linux/kernel/people/rusty/kernel-locking/ 你可能看不懂下面这个表格请学习完后面的章节再回过头来看这个表格。
举例简单介绍一下上表中第一行“IRQ Handler A”和第一列“Softirq A”的交叉点是“spin_lock_irq()”意思就是说如果“IRQ Handler A”和“Softirq A”要竞争临界资源那么需要使用 “spin_lock_irq()”函数。为什么不能用 spin_lock而要用 spin_lock_irq也就是为什么要把中断给关掉假设在 Softirq A中获得了临界资源这时发生了 IRQ A中断IRQ Handler A去尝试获得自旋锁这就会导致死锁所以需要关中断。
1.4.4 内核抢占(preempt)等额外的概念
早期的的 Linux内核是“不可抢占”的假设有 A、B两个程序在运行当前是程序 A在运行什么时候轮到程序 B运行呢 ① 程序 A主动放弃 CPU 比如它调用某个系统调用、调用某个驱动进入内核态后执行了 schedule()主动启动一次调度。 ② 程序 A调用系统函数进入内核态从内核态返回用户态的前夕 这时内核会判断是否应该切换程序。 ③ 程序 A正在用户态运行发生了中断 内核处理完中断继续执行程序 A的用户态指令的前夕它会判断是否应该切换程序。
从这个过程可知对于“不可抢占”的内核当程序 A运行内核态代码时进程是无法切换的(除非程序A主动放弃)比如执行某个系统调用、执行某个驱动时进程无法切换。 这会导致 2个问题 ① 优先级反转 一个低优先级的程序因为它正在内核态执行某些很耗时的操作在这一段时间内更高优先级的程序也无法运行。 ② 在内核态发生的中断不会导致进程切换
为了让系统的实时性更佳Linux内核引入了“抢占”(preempt)的功能进程运行于内核态时进程调度也是可以发生的。 回到上面的例子程序 A调用某个驱动执行耗时的操作在这一段时间内系统是可以切换去执行更高优先级的程序。 对于可抢占的内核编写驱动程序时要时刻注意你的驱动程序随时可能被打断、随时是可以被另一个进程来重新执行。对于可抢占的内核在驱动程序中要考虑对临界资源加锁。
1.4.5 使用场景
本节参考https://wenku.baidu.com/view/26adb3f5f61fb7360b4c656e.html 英文原文https://mirrors.edge.kernel.org/pub/linux/kernel/people/rusty/kernel-locking/
1.4.5.1 只在用户上下文加锁
假设只有程序 A、程序 B会抢占资源这 2个程序都是可以休眠的所以可以使用信号量代码如下
static DEFINE_SPINLOCK(clock_lock); // 或 struct semaphore sem; sema_init(sem, 1);
if (down_interruptible(sem)) // if (down_trylock(sem))
{ /* 获得了信号量 */
} /* 释放信号量 */
up(sem);
对于 down_interruptible函数如果信号量暂时无法获得此函数会令程序进入休眠别的程序调用up()函数释放信号量时会唤醒它。 在 down_interruptible函数休眠过程中如果进程收到了信号则会从 down_interruptible中返回对应的有另一个函数 down在它休眠过程中会忽略任何信号。 注意“信号量”(semaphore)不是“信号”(signal)。 也可以使用 mutex代码如下
static DEFINE_MUTEX(mutex); //或 static struct mutex mutex; mutex_init(mutex);
mutex_lock(mutex);
/* 临界区 */
mutex_unlock(mutex); 注意一般来说在同一个函数里调用 mutex_lock或 mutex_unlock不会长期持有它。这只是惯例如果你使用 mutex来实现驱动程序只能由一个进程打开在 drv_open中调用 mutex_lock在 drv_close中调用 mutex_unlock这也完全没问题。
1.4.5.2 在用户上下文与 Softirqs之间加锁
假设这么一种情况程序 A运行到内核态时正在访问一个临界资源这时发生了某个硬件中断在硬件中断处理完后会处理 Softirq而某个 Softirq也会访问这个临界资源。 怎么办 在程序 A访问临界资源之前干脆禁止 Softirq好了 可以使用 spin_lock_bh函数它会先禁止本地 CPU的中断下半部即 Softirq这样本地 Softirq就不会跟它竞争了假设别的 CPU也想获得这个资源它也会调用 spin_lock_bh禁止它自己的 Softirq。这 2个 CPU都禁止自己的 Softirq然后竞争 spinlock谁抢到谁就先执行。可见在执行临界资源的过程中本地 CPU的 Softirq、别的 CPU的 Softirq都无法来抢占当前程序的临界资源。 释放锁的函数是 spin_unlock_bh。 spin_lock_bh/spin_unlock_bh的后缀是“_bh”表示“Bottom Halves”中断下半部这是软件中断的老名字。这些函数改名为 spin_lock_softirq也许更恰当请记住spin_lock_bh会禁止 Softirq而不仅仅是禁止“中断下半部”(timer、tasklet里等都是 Softirq中断下半部只是 Softirq的一种)。 示例代码如下
static DEFINE_SPINLOCK(lock); // static spinlock_t lock; spin_lock_init(lock);
spin_lock_bh(lock);
/* 临界区 */
spin_unlock_bh(lock); 1.4.5.3 在用户上下文与 Tasklet之间加锁
Tasklet也是 Softirq的一种所以跟前面是“在用户上下文与 Softirqs之间加锁”完全一样。
1.4.5.4 在用户上下文与 Timer之间加锁
Timer也是 Softirq的一种所以跟前面是“在用户上下文与 Softirqs之间加锁”完全一样。
1.4.5.5 在 Tasklet与 Timer之间加锁
假设在 Tasklet中访问临界资源另一个 CPU会不会同时运行这个 Tasklet不会的所以如果只是在某个 Tasklet中访问临界资源无需上锁。 假设在 Timer中访问临界资源另一个 CPU会不会同时运行这个 timer不会的所以如果只是在某个Timer中访问临界资源无需上锁。 如果在有 2个不同的 Tasklet或 Timer都会用到一个临界资源那么可以使用 spin_lock()、spin_unlock()来保护临界资源。不需要用 spin_lock_bh()因为一旦当前 CPU已经处于 Tasklet或 Timer中同一个 CPU不会同时再执行其他 Tasklet或 Timer。
1.4.5.6 在 Softirq之间加锁
这里讲的 softirq不含 tasklet、timer。 同一个 Softirq是有可能在不同 CPU上同时运行的所以可以使用 spin_lock()、spin_unlock()来访问临界区。如果追求更高的性能可以使用“per-CPU array”本章不涉及。 不同的 Softirq之间可以使用 spin_lock()、spin_unlock()来访问临界区。
总结起来在 Softirq之间(含 timer、tasklet、相同的 Softirq、不同的 Softirq)都可以使用spin_lock()、spin_unlock()来访问临界区。 示例代码如下
static DEFINE_SPINLOCK(lock); // static spinlock_t lock; spin_lock_init(lock); spin_lock(lock);
/* 临界区 */
spin_unlock(lock); 1.4.5.7 硬中断上下文
假设一个硬件中断服务例程与一个 Softirq共享数据需要考虑 2点 ① Softirq执行的过程中可能会被硬件中断打断 ② 临界区可能会被另一个 CPU上的硬件中断进入。 怎么办 在 Softirq获得锁之前禁止当前 CPU的中断。 在硬件中断服务例程中不需要使用 spin_lock_irq()因为当它在执行的时间 Softirq是不可能执行的它可以使用 spin_lock()用来防止别的 CPU抢占。 如果硬件中断 A、硬件中断 B都要访问临界资源怎么办这篇文章里说要使用 spin_lock_irq() https://mirrors.edge.kernel.org/pub/linux/kernel/people/rusty/kernel-locking/ 但是我认为使用 spin_lock()就足够了。因为 Linux不支持中断嵌套即当前 CPU正在处理中断 A时中断 B不可能在当前 CPU上被处理不需要再次去禁止中断当前 CPU正在处理中断 A时假如有另一个CPU正在处理中断 B它们使用 spin_lock()实现互斥访问临界资源就可以了。 spin_lock_irq()/spin_unlock_irq()会禁止 /使能中断另一套函数是spin_lock_irqsave()/spin_unlock_irqrestore()spin_lock_irqsave()会先保存当前中断状态(使能还是禁止)再禁止中断spin_unlock_irqrestore()会恢复之前的中断状态(不一定是使能中断而是恢复成之前的状态)。 示例代码如下
static DEFINE_SPINLOCK(lock); // static spinlock_t lock; spin_lock_init(lock); spin_lock_irq(lock);
/* 临界区 */
spin_unlock_irq(lock);
示例代码如下
unsigned long flags;
static DEFINE_SPINLOCK(lock); // static spinlock_t lock; spin_lock_init(lock); spin_lock_irqsave(lock, flags);
/* 临界区 */
spin_unlock_irqrestore(lock, flags); 写在最后这个链接是一篇很好的文档以后我们会完全翻译出来现在讲的知识暂时够用了。 https://mirrors.edge.kernel.org/pub/linux/kernel/people/rusty/kernel-locking/