嵌入式Linux应用开发-驱动大全-同步与互斥③
嵌入式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/
相关文章:

嵌入式Linux应用开发-驱动大全-同步与互斥③
嵌入式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…...

力扣-383.赎金信
Idea 使用一个hashmap 或者一个int数组存储第二次字符串中每一个字符及其出现的次数 遍历第一个字符串,讲出现的重复字符减1,若该字符次数已经为0,则返回false AC Code class Solution { public:bool canConstruct(string ransomNote, strin…...

计算机网络 第二章物理层
计算机网络第二章知识点速刷 其中重要的是信源和信宿,以及调制解调器在通信模型当中起到的作用。...
uniapp:动态修改页面标题
我们经常遇到这种情况,点击新增按钮,进入一个空白表单页面,点击修改按钮,其实也是进入这个表单页面,只是表单内容已经被数据库的记录反显了,为了区别页面,我们还需要动态设置页面的标题…...

java学生管理系统
一、项目概述 本学生管理系统旨在提供一个方便的界面,用于学校或机构管理学生信息,包括学生基本信息、课程成绩等。 二、系统架构 系统采用经典的三层架构,包括前端使用JavaSwing,后端采用Java Servlet,数据库使用M…...
Docker和容器化:简介和使用案例
Docker和容器化:简介和使用案例 引言 容器化技术在近年来变得越来越流行,为开发人员和运维团队提供了更加灵活、高效的软件部署和管理方式。其中,Docker是最为知名和广泛使用的容器化平台之一。本篇博客文章将介绍Docker和容器化的基本概念…...

(高阶) Redis 7 第18讲 RedLock 分布式锁
🌹 以下分享 RedLock 分布式锁,如有问题请指教。🌹🌹 如你对技术也感兴趣,欢迎交流。🌹🌹🌹 如有对阁下帮助,请👍点赞💖收藏🐱🏍分享😀 问题 分布式锁问题从(高阶) Redis 7 第17讲 分布式锁 实战篇_PJ码匠人的博客-CSDN博客 这篇文章来看,…...

嵌入式软件架构基础设施设计方法
大家好,今天分享一篇嵌入式软件架构设计相关的文章。 软件架构这东西,众说纷纭,各有观点。在我看来,软件架构是软件系统的基本结构,包含其组件、组件之间的关系、组件设计与演进的规则,以及体现这些规则的基…...

MySQL进阶_3.性能分析工具的使用
文章目录 第一节、数据库服务器的优化步骤第二节、查看系统性能参数第三节、 慢查询日志第四节、 查看 SQL 执行成本第五节、 分析查询语句:EXPLAIN5.1 基本语法5.2 EXPLAIN各列作用 第一节、数据库服务器的优化步骤 当我们遇到数据库调优问题的时候,可…...

Scala第十三章节
Scala第十三章节 1. 高阶函数介绍 2. 作为值的函数 3. 匿名函数 4. 柯里化 5. 闭包 6. 控制抽象 7. 案例: 计算器 scala总目录 文档资料下载...

Nginx高级 第一部分:扩容
Nginx高级 第一部分:扩容 通过扩容提升整体吞吐量 1.单机垂直扩容:硬件资源增加 云服务资源增加 整机:IBM、浪潮、DELL、HP等 CPU/主板:更新到主流 网卡:10G/40G网卡 磁盘:SAS(SCSI) HDD(机械…...
vue项目上线后去除控制台所有console.log打印-配置说明
方式一 npm i babel-plugin-transform-remove-console --save-dev babel.config.js文件中添加 // 然后在babel.config.js中添加判断 const prodPlugin []if (process.env.NODE_ENV production) { // 如果是生产环境,则自动清理掉打印的日志,但保留…...

《XSS-Labs》02. Level 11~20
XSS-Labs 索引Level-11题解 Level-12题解 Level-13题解 Level-14题解 Level-15题解 Level-16题解 Level-17题解 Level-18~20题解 靶场部署在 VMware - Win7。 靶场地址:https://github.com/do0dl3/xss-labs 只要手动注入恶意 JavaScript 脚本成功,就可以…...
Java中处理千万级数据的最佳实践:性能优化指南
在今天的数字化时代,处理大规模数据已经成为许多Java应用程序的核心任务。无论您是构建数据分析工具、实现实时监控系统,还是处理大规模日志文件,性能优化都是确保应用程序能够高效运行的关键因素。本指南将介绍一系列最佳实践,帮…...
LCR 069.山峰数组的峰顶索引
题目来源: leetcode题目,网址:LCR 069. 山脉数组的峰顶索引 - 力扣(LeetCode) 解题思路: 二分查找即可。 解题代码: class Solution {public int peakIndexInMountainArray(int[] arr) {…...
AtCoder Beginner Contest 233 (A-Ex)
A.根据题意模拟即可 B.根据题意模拟即可 C.直接用map 进行dp即可 D.用前缀和进行模拟,用map统计前缀和,每次计算当前前缀和-k的个数就是以当前点为右端点答案。 E - Σ[k0..10^100]floor(X/10^k) (atcoder.jp) (1)…...
解决caffe中的python环境安装的问题
由于caffe(GitHub - BVLC/caffe: Caffe: a fast open framework for deep learning.)使用的python版本是2.7,而非python3,所以安装的时候使用命令:sudo apt install python2.7进行安装。 而在安装python的各种包时&am…...

专业图像处理软件DxO PhotoLab 7 mac中文特点和功能
DxO PhotoLab 7 mac是一款专业的图像处理软件,它为摄影师和摄影爱好者提供了强大而全面的照片处理和编辑功能。 DxO PhotoLab 7 mac软件特点和功能 强大的RAW和JPEG格式处理能力:DxO PhotoLab 7可以处理来自各种相机的RAW格式图像,包括佳能、…...

面试题:Kafka 为什么会丢消息?
文章目录 1、如何知道有消息丢失?2、哪些环节可能丢消息?3、如何确保消息不丢失? 引入 MQ 消息中间件最直接的目的:系统解耦以及流量控制(削峰填谷) 系统解耦: 上下游系统之间的通信相互依赖&a…...

WSL安装异常:WslRegisterDistribution failed with error: 0xc03a001a
简介:如果文件夹右上角是否都有两个相对的蓝色箭头,在进行安装wsl时,设置就会抛出 Installing WslRegisterDistribution failed with error: 0xc03a001a的异常 历史攻略: 卸载WSL WSL:运行Linux文件 WSL࿱…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...

佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...
人工智能 - 在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型
在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型。这些平台各有侧重,适用场景差异显著。下面我将从核心功能定位、典型应用场景、真实体验痛点、选型决策关键点进行拆解,并提供具体场景下的推荐方案。 一、核心功能定位速览 平台核心定位技术栈亮…...

CSS3相关知识点
CSS3相关知识点 CSS3私有前缀私有前缀私有前缀存在的意义常见浏览器的私有前缀 CSS3基本语法CSS3 新增长度单位CSS3 新增颜色设置方式CSS3 新增选择器CSS3 新增盒模型相关属性box-sizing 怪异盒模型resize调整盒子大小box-shadow 盒子阴影opacity 不透明度 CSS3 新增背景属性ba…...

基于stm32F10x 系列微控制器的智能电子琴(附完整项目源码、详细接线及讲解视频)
注:文章末尾网盘链接中自取成品使用演示视频、项目源码、项目文档 所用硬件:STM32F103C8T6、无源蜂鸣器、44矩阵键盘、flash存储模块、OLED显示屏、RGB三色灯、面包板、杜邦线、usb转ttl串口 stm32f103c8t6 面包板 …...

未授权访问事件频发,我们应当如何应对?
在当下,数据已成为企业和组织的核心资产,是推动业务发展、决策制定以及创新的关键驱动力。然而,未授权访问这一隐匿的安全威胁,正如同高悬的达摩克利斯之剑,时刻威胁着数据的安全,一旦触发,便可…...