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

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

相关文章

2023蓝帽杯半决赛电子取证+CTF部分题解

文章目录 电子取证123456789101112131415 CTFWeb | MyLinuxBotWeb | AirticleShareCrypto | ezrsaPwn | AdminPwn | uafmisc|排排坐吃吃果果 电子取证 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 CTF Web | MyLinuxBot Web | AirticleShare import requests import times reques…

imgui开发笔记<1>、ubuntu环境下快速应用

去这个链接下载imgui源码(在此之前需要安装opengl glfw3等等): sudo apt-get install libglfw3-dev https://github.com/ocornut/imgui 我这里源码下载到/home/temp/imgui目录下,咱们不需要编译源码成库,而是直接将下…

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

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

免杀对抗-DLL劫持免杀

C&Py-DLL劫持-语言-调用加载 1.使用visual studio创建项目 2.将文件名重命名为.c后缀 3.将如下加载器代码生成dll文件 加载器代码&#xff1a; #include "pch.h" #include <Windows.h> #include <stdio.h> #include <string.h>#pragma comment…

ARM-day2

1、1到100累加 .text .global _start_start:MOV r0, #1ADD r1,r0, #1fun:ADD r0,r0,r1ADD r1,r1, #1cmp r1, #0x65moveq PC,LRbl funstop:b stop.end2、思维导图

ili9431液晶 tft_espi图形库演示 时钟、天气、滚动、气象图标

米思齐tft_spi模块库演示程序。心知天气、阿里云时钟、WiFi信号强度检测、1分钟滚屏、更新天气时间为15分钟、加入天气图标。更新天气次数。断网检测 。此程序为tft_eSPI图形库演示、如感觉好可以自行优化。 ili9431tft_espi库是用于ESP32和ESP8266芯片的TFT LCD驱动程序库&am…

前端——html面试题(一)

文章目录 前言一、说说你对 html 语义化的理解。二、html的标签有哪些三、标签类型1、问题&#xff1a;请说说你对块级元素、行内元素、空元素的理解&#xff0c;它们分别都有哪些常见的标签&#xff1f; 总结 前言 说说对html语义化的理解html的标签标签类型 一、说说你对 ht…

国庆day5---QT实现TCP服务器客户端搭建的代码,现象

ser.h #ifndef SER_H #define SER_H#include <QWidget> #include<QTcpServer> //服务器头文件 #include<QTcpSocket> //客户端头文件 #include<QMessageBox> //消息对话框 #include<QList> //链表头文件QT_BEGIN_NAMESPACE nam…

孔雀东南飞:经济高质量发展与人才流动(数据复现)

数据简介&#xff1a;人才是经济高质量发展的动力源泉。中国ZF一直高度重视人才培养&#xff0c;积极发挥人才作用。“人才是第一资源”,“深入实施……人才强国战略”,“坚持……人才引领驱动”。与此同时&#xff0c;地方ZF大力引进人才&#xff0c;不断推出各类人才优待政策…

二十,镜面IBL--打印BRDF积分贴图

比起以往&#xff0c;这节应该是最轻松的了&#xff0c; 运行结果如下 代码如下&#xff1a; #include <osg/TextureCubeMap> #include <osg/TexGen> #include <osg/TexEnvCombine> #include <osgUtil/ReflectionMapGenerator> #include <osgDB/Re…

C#(CSharp)入门教程

目录 C#的第一个程序 变量 折叠代码 变量类型和声明变量 获取变量类型所占内存空间&#xff08;sizeof&#xff09; 常量 转义字符 隐式转换 显示转换 异常捕获 运算符 算术运算符 布尔逻辑运算符 关系运算符 位运算符 其他运算符 字符串拼接 …

《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。 靶场地址&#xff1a;https://github.com/do0dl3/xss-labs 只要手动注入恶意 JavaScript 脚本成功&#xff0c;就可以…