嵌入式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/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/148560.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

2023年中国体育赛事行业现状及趋势分析:体育与科技逐步融合,推动产业高质量发展[图]

体育赛事运营是指组织体育赛事或获取赛事版权,并进行赛事推广营销、运营管理等一系列商业运作的运营活动。体育赛事运营相关业务主要包括赛事运营与营销、赛事版权运营两个部分。 体育赛事运营行业分类 资料来源:共研产业咨询(共研网&#x…

MySQL面试题合集

MySQL面经知识整理 文章目录 MySQL面经知识整理一、查询相关1.什么是MySQL的连接查询,左连接,右连接,内外连接2.SQL慢查询优化的方法3.大表查询如何优化 二、索引相关1.在MySQL中,可以通过哪些命令来查看查询是否使用了索引2.MySQL的最左匹配…

实验三十四、串联型稳压电路参数的选择

一、题目 电路如图1所示。已知输入电压为 50 Hz 50\,\textrm{Hz} 50Hz 的正弦交流电,来源于电源变压器副边;输出电压调节范围为 5 ∼ 20 V 5\sim20\,\textrm V 5∼20V,满载为 0.5 A 0.5\,\textrm A 0.5A; C 3 C_3 C3​ 为消振…

在排序数组中查找元素的第一个和最后一个位置

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target,返回 [-1, -1]。 你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。 示例 1&#xff1a…

结构和基本尺寸

声明 本文是学习GB-T 586-2015 船用法兰铸钢止回阀. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了法兰连接尺寸和密封面按 CB/T 4196、GB/T 2501 的船用法兰铸钢止回阀(以下简 称止回阀)的分类和标记、要求、试验方法、检验规…

使用Java操作Redis

要在Java程序中操作Redis可以使用Jedis开源工具。 一、jedis的下载 如果使用Maven项目&#xff0c;可以把以下内容添加到pom中 <!-- https://mvnrepository.com/artifact/redis.clients/jedis --> <dependency> <groupId>redis.clients</groupId>…

【LeetCode热题100】--114.二叉树展开为链表

114.二叉树展开为链表 方法一&#xff1a;对二叉树进行先序遍历&#xff0c;得到各个节点被访问到的顺序&#xff0c;利用数组存储下来&#xff0c;然后在先序遍历之后更新每个节点的左右节点的信息&#xff0c;将二叉树展开为链表 /*** Definition for a binary tree node.* …

Vue+ElementUI实现动态树和表格数据的分页模糊查询

目录 前言 一、动态树的实现 1.数据表 2.编写后端controller层 3.定义前端发送请求路径 4.前端左侧动态树的编写 4.1.发送请求获取数据 4.2.遍历左侧菜单 5.实现左侧菜单点击展示右边内容 5.1.定义组件 5.2.定义组件与路由的对应关系 5.3.渲染组件内容 5.4.通过动态…

FFmpeg 命令:从入门到精通 | ffmpeg filter(过滤器 / 滤镜)

FFmpeg 命令&#xff1a;从入门到精通 | ffmpeg filter&#xff08;过滤器 / 滤镜&#xff09; FFmpeg 命令&#xff1a;从入门到精通 | ffmpeg filter&#xff08;过滤器 / 滤镜&#xff09;ffmpeg fliter 基本内置变量视频裁剪文字水印图片水印画中画视频多宫格处理 FFmpeg 命…

希尔排序(C++实现)

文章目录 前言1. 基础概念2. 动图演示3. 代码实现4. 排序过程5. 效率分析6. 总结 前言 上篇文章讲了直接插入排序算法。 首先&#xff0c;在待排序的数组中&#xff0c;元素本身就是有序的情况下&#xff0c;就不需要移动任何元素&#xff0c;所以直接插入排序最好情况时间复…

进程调度的时机,切换与过程以及方式

1.进程调度的时机 进程调度&#xff08;低级调度〉&#xff0c;就是按照某种算法从就绪队列中选择一个进程为其分配处理机。 1.需要进行进程调度与切换的情况 1.当前运行的进程主动放弃处理机 进程正常终止运行过程中发生异常而终止进程主动请求阻塞&#xff08;如等待l/O)…

数据结构与算法-顺序表

数据结构与算法 &#x1f388;1.线性表&#x1f50e;1.1基本操作&#x1f50e;1.2线性表的存储结构 &#x1f388;2.线性表的顺序表示和实现&#x1f50e;2.1线性表的顺序存储表示&#x1f52d;2.1.1静态顺序表&#x1f52d;2.1.2动态顺序表 &#x1f50e;2.2顺序表基本操作的实…

MYSQL8解压版 windows 主从部署步骤及配置(包含配置文件,教程文件,免积分下载)

MYSQL8解压版 windows 主从部署步骤及配置 一.安装MSYQL 这里只讲大概,详细步骤、my.ini文件、安装包等会在页尾文件中(正常情况按首个mysql安装,只是名字有区别) 1.主库my.ini配置 [mysqld] #典型的值是5-6GB(8GB内存)&#xff0c;8-11GB(16GB内存), 20-25GB(32GB内存)&…

阿里云对象存储OSS SDK的使用

官方文档 https://help.aliyun.com/zh/oss/developer-reference/java 准备工作 windows安装好JDK&#xff0c;这里使用JDK1.8为例 windows安装好IDEA&#xff0c;这里使用IDEA2022 登录阿里云控制台&#xff0c;通过免费试用OSS或开通OSS 步骤 配置访问凭证 有临时和长期…

【Go】go-es统计接口被刷数和ip访问来源

go-es模块统计日志中接口被刷数和ip访问来源 以下是使用go的web框架gin作为后端&#xff0c;展示的统计页面 背景 上面的数据来自elk日志统计。因为elk通过kibana进行展示&#xff0c;但是kibana有一定学习成本且不太能满足定制化的需求&#xff0c;所以考虑用编程的方式…

mysql-binlog

1. 常用的binlog日志操作命令 1. 查看bin-log是否开启 show variables like log_%;2. 查看所有binlog日志列表 show master logs;3.查看master状态 show master status;4. 重置&#xff08;清空&#xff09;所有binlog日志 reset master;2. 查看binlog日志内容 1、使用mysqlb…

竞赛 机器视觉人体跌倒检测系统 - opencv python

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 机器视觉人体跌倒检测系统 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&…

腾讯云服务器完整建站过程(新手搭建网站教程)

使用腾讯云服务器搭建网站全流程&#xff0c;包括轻量应用服务器和云服务器CVM建站教程&#xff0c;轻量可以使用应用镜像一键建站&#xff0c;云服务器CVM可以通过安装宝塔面板的方式来搭建网站&#xff0c;腾讯云服务器网分享使用腾讯云服务器建站教程&#xff0c;新手站长搭…

接口测试复习

一。基本概念 接口概念&#xff1a;系统与系统之间 数据交互的通道。 接⼝测试概念&#xff1a;校验 预期结果 与 实际结果 是否⼀致。 特征&#xff1a; 测试⻚⾯测试发现不了的问题。&#xff08;因为&#xff1a;接⼝测试 绕过前端界⾯。 &#xff09; 符合质量控制前移理…

ChainForge:衡量Prompt性能和模型稳健性的GUI工具包

ChainForge是一个用于构建评估逻辑来衡量模型选择&#xff0c;提示模板和执行生成过程的GUI工具包。ChainForge可以安装在本地&#xff0c;也可以从chrome浏览器运行。 ChainForge可以通过聊天节点对多个对话可以使用不同的llm并行运行。可以对聊天消息进行模板化&#xff0c;并…