嵌入式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.rhkb.cn/news/147803.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript系列从入门到精通系列第十四篇:JavaScript中函数的简介以及函数的声明方式以及函数的调用

文章目录 一:函数的简介 1:概念和简介 2:创建一个函数对象 3:调用函数对象 4:函数对象的普通功能 5:使用函数声明来创建一个函数对象 6:使用函数声明创建一个匿名函数 一:函…

嵌入式Linux应用开发-驱动大全-同步与互斥①

嵌入式Linux应用开发-驱动大全-同步与互斥① 第一章 同步与互斥①1.1 内联汇编1.1.1 C语言实现加法1.1.2 使用汇编函数实现加法1.1.3 内联汇编语法1.1.4 编写内联汇编实现加法1.1.5 earlyclobber的例子 1.2 同步与互斥的失败例子1.2.1 失败例子11.2.2 失败例子21.2.3 失败例子3…

留住时光、固化时间、让一瞬成为永恒——全新的真人手办定制模式,就选易模小程序制作。

如何能永远留住一瞬间? 有人说我可以拍摄一张照片,照片的问世确实给生活中许多美好的瞬间留下了可以记录的工具。但是随着时间的流逝,照片终究也会泛黄,似乎有一些记忆的温度,有一些岁月的棱角,也是照片所给…

分享Arduino环境下加速下载 第三方库或芯片包

Content 问题描述问题解决 问题描述 众所周知,由于网络的问题,导致Arduino里面的包下载速度非常慢,甚至下了非常久,最后也还是出现下载失败的情况。 有的人打开了加速器,但是也依旧是速度非常慢,为什么呢…

在微信小程序中跳转到另一个小程序(多种实现方式)

方式一: 配置要跳转的appid和小程序页面路径 wx.navigateToMiniProgram({appId: 目标小程序appid,path: 目标小程序页面路径,//develop开发版;trial体验版;release正式版envVersion: release, success(res) {// 打开成功console.log("跳…

ArcGIS Engine:报错无法嵌入互操作类型“ESRI.ArcGIS.Geometry.EnvelopeClass”。请改用适用的接口。

此错误是由于尝试直接实例化ArcGIS COM组件的某些互操作类引起的。这在.NET Framework 4及更高版本中是不被推荐的。 为了解决此问题,你需要确保在工程的引用中将ArcGIS的互操作类型设置为“不嵌入”。 按照以下步骤操作: 在解决方案资源管理器中找到…

原型、原型链、判断数据类型

目录 作用 原型链 引用类型:__proto__(隐式原型)属性,属性值是对象函数:prototype(原型)属性,属性值是对象 Function:本身也是函数 相关方法 person.prototype.isPrototypeOf(stu) Object.getPrototypeOf(objec…

分享一款开源的QT的串口示波器

分享一款开源的QT的串口示波器,完全开源,支持串口、TCP、波形显示、通信协议。 Sailor Project功能说明 串口调试助手功能 支持传统的串口调试助手的基本收发功能,同时可以刷新大量的数据而不卡顿 支持保存接收的数据 支持最大200条可编辑…

Docker Tutorial

什么是Docker 为每个应用提供完全隔离的运行环境 Dockerfile, Image,Container Image: 相当于虚拟机的快照(snapshot)里面包含了我们需要部署的应用程序以及替它所关联的所有库。通过image,我们可以创建很…

SD卡格式化怎么恢复?

随着智能手机、平板电脑、数码相机和行车记录仪等各种移动电子设备走入千家万户,SD卡作为与这些设备相配套的存储介质,也随之获得了广泛的市场认可。SD卡因其体积小巧、容量庞大、读写速度高及与多种设备兼容的优点,逐渐成为日常工作和生活中…

linux 笔记:远程服务器登录jupyter notebook

1 生成jupyter notebook 配置文件(服务器端) jupyter notebook --generate-config #Writing default config to: /home/shuailiu/.jupyter/jupyter_notebook_config.py2 Ipython中设置密码(服务器端) 3 修改jupyter 配置文件&…

如何使用百度“云一朵”来分析PDF文件

PDF 文件是一种常见的文件格式,用于存储文档、图像和其他内容。在许多情况下,我们需要对 PDF 文件进行分析,以提取其中的信息。百度“云一朵”提供了一个 PDF 分析 API,可以帮助我们轻松地对 PDF 文件进行分析。 在本博客文章中&…

车牌文本检测与识别:License Plate Recognition Based On Multi-Angle View Model

论文作者:Dat Tran-Anh,Khanh Linh Tran,Hoai-Nam Vu 作者单位:Thuyloi University;Posts and Telecommunications Institute of Technology 论文链接:http://arxiv.org/abs/2309.12972v1 内容简介: 1)方向&#x…

基于spring boot的医疗管理系统 /基于java的医疗系统

摘 要 随着信息技术和网络技术的飞速发展,人类已进入全新信息化时代,传统管理技术已无法高效,便捷地管理信息。为了迎合时代需求,优化管理效率,各种各样的管理系统应运而生,各行各业相继进入信息管理时代&a…

概率密度函数,概率分布函数

概率密度函数:描述信号的取值在某个确定的取值点附近的概率的函数;概率分布函数的导数。 以幅值大小为横坐标,以每个幅值间隔内出现的概率为纵坐标进行统计分析。反映了信号落在不同幅值强度区域内的概率情况。 直方图:对每个幅…

解决nvm切换node版本失败的终极办法-秒杀网上99%的水文

nvm是一款强大的node多版本管理器,可以轻易选择你需要的node版本,这对win7平台简直就是超好的福音:可以突破node 14.15以上的安装限制。 但是nvm安装有一个巨大的坑点:nvm use 版本号以后,并没有生效,nvm …

【时区】Flink JDBC 和CDC时间字段时区 测试及时间基准

关联文章: 各种时间类型和timezone关系浅析 一、测试目的和值 1. 测试一般的数据库不含time zone的类型的时区。 mysql timestamp(3) 类型postgres timestamp(3) 类型sqlserver datetime2(3) 类型oracle类型 TIMESTAMP(3) 类型 在以下测试之中均为ts字段 2.测试CDC中元数据…

【深蓝学院】手写VIO第4章--基于滑动窗口算法的 VIO 系统:可观性和 一致性--作业

0. 内容 T1. 参考SLAM14讲P247直接可写,注意 ξ 1 , ξ 2 \xi_1,\xi_2 ξ1​,ξ2​之间有约束(关系)。 套用舒尔补公式: marg掉 ξ 1 \xi_1 ξ1​之后,信息被传递到 L 1 和 L 2 L_1和L_2 L1​和L2​之间了。 T2. …

IDM(Internet Download Manager)2024中文版下载工具软件

IDM(Internet Download Manager):功能强大,下载速度快,支持多线程下载,下载过程中遇到突然断电等情况,可以进行断点续传,很多人因此而首选IDM。 优点: (1&a…

抖音聊天对话模板,制作一条一条冒出来的聊天对话短视频

聊天对话模板是一种非常有趣且实用的工具,它主要用于制作抖音聊天记录一条一条冒出来的视频。有了聊天对话模板,你再也不需要费心去写剧本,只需输入一些简单的文案,就能自动生成搞笑对话视频。 聊天对话工具下载: htt…