内核的并发和竞态(信号量、completion、自旋锁)

2023-11-01

1、并发和并行

(1)并行:在同一时刻有多个线程一起运行;
(2)并发:在同一时刻只有一个线程在运行,但是在一个时间段内有多个线程运行;
总结:并发是宏观的并行。因为CPU运行特别快,虽然CPU不断在切换运行的线程,但是对于人来说,根本感知不到CPU的切换过程,就好像荧光灯在不停的闪烁,
但是频率只要够快,人们就感觉不到在闪烁。

2、并发产生的原因

(1)正在运行的多个用户进程以无法预知的方式访问驱动程序代码
(2)外部设备的中断异步的发生,导致正在运行的进程或者驱动代码被中断
(3)linux的软件抽象(如timer, tasklet, workqueue)也在异步运行着
(4)现在SMP的处理器架构,导致驱动程序可能会在不同的CPU上运行
(5)可抢占的内核调度算法,可能导致驱动代码随时被抢占

3、内核的竞态

内核的竞态和进程/线程的同步问题是一样的,都是对共享资源的访问不一致导致的。A线程和B线程同时去操作同一共享资源时,由于两个线程互相都感知不到对方在
操作共享资源,当两个线程同时去修改共享资源时,时间上后修改的就会覆盖掉前修改的,这就导致了错误。

4、解决竞态的方法

4.1、乐观锁和悲观锁

悲观锁:认为多线程同时修改共享资源的概率比较高,于是很容易出现冲突,所以访问共享资源前,先要上锁,在同一时间只允许一个线程访问资源,从源头解决竞争;
下面介绍的信号量、comption、自旋锁都是属于悲观锁;
乐观锁:先修改完共享资源,再验证这段时间内有没有发生冲突,如果没有其他线程在修改资源,那么操作完成,如果发现有其他线程已经修改过这个资源,就放弃本次操作。

4.2、信号量

//创建一个信号量,初始值为val
void sema_init(struct semaphore *sem, int val)

//创建名字为name的信号量被初始化为1:初始状态是没有锁定的;
DECLARE_MUTEX(name)

//创建名字为name的信号量被初始化为0:初始状态是锁定的
DECLARE_MUTEX_LOCKED(name)

//P操作,会一直阻塞
void down(struct semaphore *sem);

//P操作,在等待过程中可以被打断,打断后函数返回非零值,调用者不会拥有该信号量
void down_interruptible(struct semaphore *sem);

//P操作,不能拥有信号量就马上返回并返回非零值
void down_trylock(struct semaphore *sem);

//V操作
void up(struct semaphore *sem);

(1)信号量本质就是一个整数值,用来管理资源。比如用信号量来管理打印机资源:设备上总共有三台打印机,于是信号量初始值为3,每一次申请打印机资源后,信号量就减一(P操作)。
当信号量减到0(或者负数)时则代表当前没有可用的打印机,这个时候申请打印机资源的线程就会阻塞,等待之前的线程释放打印机资源将信号量加一(V操作),当信号量大于0时则会去唤醒
最先申请打印机资源的线程。
(2)当信号量初始值为1时,代表只有一个共享资源,这个资源在同一时刻只能被一个线程享有,于是达到了互斥访问的效果;

4.3、读取者/写入者信号量:rw_semaphore

4.3.1、为什么要有读取者/写入者信号量?

(1)上面将信号量初始化为1,其实就是一把互斥锁,保证了在同一时刻只有一个线程能访问共享资源,虽然保证了访问共享资源时不会冲突,但是这样共享资源的利用率又很低;
(2)访问共享资源的线程如果都是去读,那么同时访问是没有冲突的,只有当线程去尝试修改共享资源时才可能冲突;
(3)于是读写信号量的功能:在同一时刻,允许多个线程去读,或者允许一个线程去写;读和写操作是互斥的,但是可以有多个线程同时读,提高共享资源的利用率;

4.3.2、读取者/写入者信号量的优缺点

(1)优点:当共享资源被读取的时候占多数时,因为允许多个线程同时读,所以共享资源利用率高;
(2)缺点:当写操作占用时间长,并且不停有线程去上写锁,很可能造成要读取共享资源的线程“饿死”;但是如果提高写锁的优先级,如果不停有线程去上写锁,也可能
造成读线程“饿死”;所以用读写锁,要注意策略,不要造成线程“饿死”。

4.3.3相关的变量类型和函数

struct rw_semaphore {
	__s32			activity;
	spinlock_t		wait_lock;
	struct list_head	wait_list;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lockdep_map dep_map;
#endif
};

//初始化
void init_rwsem(truct rw_semaphore *sem);

//上读锁,会阻塞
void down_read(struct rw_semaphore * sem);	

//上读锁,不会阻塞,马上返回
int down_read_trylock(struct rw_semaphore *sem);

//读锁的解锁
void up_read(struct rw_semaphore * sem);

//上写锁,会阻塞
void down_write(struct rw_semaphore * sem);	

//上写锁,不会阻塞,马上返回
int down_write_trylock(struct rw_semaphore *sem);

//写锁的解锁
void up_write(struct rw_semaphore * sem);

4.4、completion

4.4.1、completion介绍

(1)在内核中存在A线程的执行依赖B线程的某些操作,也就是线程同步,A线程的某些操作必须等待B线程的某些操作完成才能执行;
(2)comption是一种轻量级的机制,它允许一个线程告诉另一个线程某个工作已经完成;
补充:普通的信号量也可以完成completion的功能,但是completion的开销更小;

4.4.2、comption相关函数

//初始化一个变量叫name的struct completion变量
#define DECLARE_COMPLETION(name) struct completion work = COMPLETION_INITIALIZER(work)

//初始化struct completion变量
void init_completion(struct completion *c)

//等待completion完成
void wait_for_completion(struct completion *c);

//唤醒一个等待completion的线程
extern void complete(struct completion *c);

//唤醒所有等待completion的线程
extern void complete_all(struct completion *c);

4.5、自旋锁

4.5.1、信号量和自旋锁的比较

在这里插入图片描述

(1)共同点:自旋锁和非自旋锁(比如:信号量)在功能上是一样的,都是互斥锁,都是解决内核的竞态,确保共享资源的访问不冲突;
(2)不同点:非自旋锁上锁失败线程则会阻塞,放弃CPU的使用,CPU切换去执行其他的线程;自旋锁上锁失败不会让渡出CPU,会不断的循环尝试获取锁,知道成功获取到锁;
参考博客:什么是自旋锁?自旋的好处和后果是什么呢?;

4.5.2、自旋锁的好处

(1)CPU阻塞和唤醒线程是需要开销的;
(2)自旋锁在获取不到锁时不释放CPU,不断尝试获取锁,也是有开销的,自旋锁的开销和锁住的时间是正相关的;
总结:当共享资源被锁住的时间很短时,自旋锁的开销小,适合用自旋锁;当共享资源锁住的时间长时,适合用非自旋锁;

5、除了锁之外的同步办法

5.1、免锁算法

免锁算法就是从源头解决,内核竞态不少情况是多个线程要去访问同一资源,如果我们增加资源的数量,让线程不必去争抢资源,也就
不会发生竞态,也没有必要使用上述的锁机制;比如:增加循环缓冲区;

5.2、原子变量atomic_t

typedef struct {
	int counter;
} atomic_t;

有时候共享资源就是一个整数值,完整的锁机制对一个简单的整数来讲显得有些浪费,于是内核提供了一种原子的整数类型atomic_t。
一个atomic_t变量在所有内核支持的架构上保存一个int值,但是不能使用完整的整数范围,在atomic_t变量中不能记录大于24位的整数。
补充:操作atomic_t类型变量要使用专门的函数。

5.3、其他方法

(1)位操作:以原子的形式操作单个位。上面的atomic_t是针对整数值的;
(2)seqlock:适合会被频繁读,但是很少写的共享资源;
(3)读取-复制-更新:针对频繁读,但是很少写的共享资源,做了优化处理,驱动很少用;

6、产生死锁的情况

(1)拥有自旋锁的进程A在内核态阻塞了, 内核调度B进程, 碰巧B进程也要获得自旋锁, 此时B只能自旋转。 而此时抢占已经关闭,( 单核) 不会调度A进程了, B永远自旋, 产生死锁;
(2)进程A拥有自旋锁, 中断到来, CPU执行中断函数,中断处理函数, 中断处理函数需要获得自旋锁, 访问共享资源, 此时无法获得锁, 只能自旋, 产生死锁;

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

内核的并发和竞态(信号量、completion、自旋锁) 的相关文章

  • Linux C++ 错误:未定义对“dlopen”的引用

    我在 Linux 上使用 C Eclipse 工作 并且想要使用一个库 Eclipse 向我显示一个错误 undefined reference to dlopen 你知道解决办法吗 这是我的代码 include
  • 找出Linux上一个进程使用了​​多少内存页

    我需要找出进程分配了多少内存页 每个页面是 4096 进程内存使用情况我在查找正确值时遇到一些问题 当我查看 gome system monitor 时 内存映射下有几个值可供选择 Thanks 这样做的目的是将内存使用量除以页数并验证页大
  • 如何告诉 mex 链接到 /usr/lib 中的 libstdc++.so.6 而不是 MATLAB 目录中的 libstdc++.so.6?

    现在 MATLAB 2012a 中的 mex 仅正式支持 gcc 4 4 6 但我想使用 gcc 4 7 风险自负 现在如果我直接用 mex 编译一些东西 它会抱怨 usr lib gcc i686 linux gnu 4 7 cc1plu
  • 何时用引号将 shell 变量括起来?

    我应该或不应该在 shell 脚本中用引号括住变量吗 例如 下列说法正确的是 xdg open URL eq 2 or xdg open URL eq 2 如果是这样 为什么 一般规则 如果它可以为空或包含空格 或实际上任何空格 或特殊字符
  • Qt 嵌入式触摸屏 QMouseEvents 在收到 MouseButtonRelease 之前未收到

    我在带有触摸屏的小型 ARM 嵌入式 Linux 设备上使用 Qt 4 8 3 我的触摸屏配置了 tslib 并对其进行了校准 因此 etc 中有一个 pointcal 文件 我的触摸事件的位置工作得很好 但无论如何我都会在鼠标按下或鼠标释
  • 如何通过 makefile 在 Linux 上安装程序? [复制]

    这个问题在这里已经有答案了 可能的重复 Linux Unix make install 应该包含什么 https stackoverflow com questions 528399 what should linux unix make
  • 任何退出 bash 脚本但不退出终端的方法

    当我使用exitshell 脚本中的命令 该脚本将终止终端 提示符 有什么方法可以终止脚本然后停留在终端中吗 我的剧本run sh预计通过直接获取或从另一个脚本获取来执行 编辑 更具体地说 有两个脚本run2 sh as run sh ec
  • 对于任何真实数据集,数据压缩比的最小可能值是多少

    我在写信ZLIB类似于嵌入式硬件压缩器的 API 它使用 deflate 算法来压缩给定的输入流 在进一步讨论之前 我想解释一下数据压缩率 数据压缩率定义为未压缩大小与压缩大小之间的比率 压缩比通常大于一 这意味着压缩数据通常比未压缩数据小
  • 使用脚本检查 git 分支是否领先于另一个分支

    I have branch1 and branch2我想要某种 git branch1 isahead branch2 这将显示如果branch1已承诺branch2没有 也可能指定这些提交 我无法检查差异原因branch2 is在之前br
  • Vagrant 遇到问题 - “404 - 未找到”

    我正在尝试使用 Vagrant 制作一个 LAMP 盒子 有人告诉我它使用起来非常简单 我对网络和虚拟机完全陌生 对 Linux Ubuntu 的经验也很少 我目前已尝试按照官方文档页面上的教程进行操作 http docs vagrantu
  • 在ubuntu中打开spyder

    我想在ubuntu中打开spyder Python IDE 通常我会在 shell 中编写 spyder 它会打开spyder IDE 现在 当我在shell中编写spyder时 它只是换行 什么也没有发生 类似于按 enter 我如何找回
  • Ruby:在 Ubuntu 上安装 rmagick

    我正在尝试在 Ubuntu 10 04 上安装 RMagick 看起来here https stackoverflow com questions 1482823 is there an easy way to install rmagic
  • Linux shell 从用户输入中获取设备 ID

    我正在为一个程序编写安装脚本 该程序需要在其配置中使用 lsusb 的设备 ID 因此我正在考虑执行以下操作 usblist lsusb put the list into a array for each line use the arr
  • “git add”返回“致命:外部存储库”错误

    我刚刚进入 git 的奇妙世界 我必须提交我对程序所做的一系列更改 位于名为的目录中 var www myapp 我创建了一个新目录 home mylogin gitclone 从这个目录中 我做了一个git clone针对公共回购 我能够
  • /sys/device/ 和 dmidecode 报告的不同 CPU 缓存大小

    我正在尝试获取系统中不同缓存级别的大小 我尝试了两种技术 a 使用 sys device 中的信息 这是输出 cat sys devices system cpu cpu0 cache index1 size 32K cat sys dev
  • Python 3.4.3 subprocess.Popen 在没有管道的情况下获取命令的输出?

    我试图将命令的输出分配给变量 而不让命令认为它正在通过管道传输 原因是 如果正在通过管道传输 则相关命令会给出未格式化的文本作为输出 但如果从终端运行 则会给出颜色格式化的文本 我需要获取这种颜色格式的文本 到目前为止我已经尝试了一些事情
  • 如何在不使用 IDE 的情况下在 Linux 上运行 Java 项目

    我是 Java 新手 基本上 我开发了一个java项目 其中包含Eclipse中的多个Java包 该项目在我安装了 redhat Linux 的桌面上运行正常 然而 我需要在一个更强大的没有安装X11的Linux服务器 redhat ent
  • grep 排除文件的数组参数

    我想从我的文件中排除一些文件grep命令 为此我使用参数 exclude excluded file ext 为了更容易阅读 我想使用包含排除文件的 bash 数组 EXCLUDED FILES excluded file ext 然后将
  • 执行命令而不将其保留在历史记录中[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 在进行软件开发时 经常需要在命令行命令中包含机密信息 典型示例是将项目部署到服务器的凭据设置为环境变量 当我不想将某些命令存储在命令历史记
  • 相当于Linux中的导入库

    在 Windows C 中 当您想要链接 DLL 时 您必须提供导入库 但是在 GNU 构建系统中 当您想要链接 so 文件 相当于 dll 时 您就不需要链接 为什么是这样 是否有等效的 Windows 导入库 注意 我不会谈论在 Win

随机推荐

  • MSN下载2010最新版

    转载的 哈哈 我的笔记 MSN 最新微软官网下载地址 NLP伪原创技术早期并不是很受欢迎 基于主动学习的伪原创句法识别研究 这几年专注于Web开发 Web界面设计的心得在这篇文章中已经与大家分享 做项目时 如何做比较美观大方的数据输入窗体
  • apisix高性能网关-中文开发文档

    2020年6月12日13 48 33 github https github com apache incubator apisix 目前此项目已有成为apache孵化项目 官方中文文档 https github com apache in
  • 拓展人脉,把握机会,摆脱“孔乙己的长衫”

    孔乙己的长衫 学历究竟成为敲门砖还是枷锁 孔乙已是鲁迅笔下人物 穷困流倒还穿着象征读书人的长衫 迁腐 麻木 最近 大家自我调佩是 当代孔乙己 学历成为思想负担 找工作时高不成低不就 我将从以下几个方面来说一说我对这方面的看法 我认为 在当今
  • 1. 两数之和

    目录 给定一个整数数组 nums 和一个整数目标值 target 请你在该数组中找出 和为目标值 target 的那 两个 整数 并返回它们的数组下标 你可以假设每种输入只会对应一个答案 但是 数组中同一个元素在答案里不能重复出现 你可以按
  • 数据结构--计算表达式完整版(涉及乘方)

    思路 两个栈 数据栈与符号栈 在描述优先级时 我们将加减描述为 I级 乘除为 II既 乘方为 III级 1 在之前的只有加减乘除的计算表达式里 就有了初步思路 当我们要计算的时候 局部计算函数 数字栈与符号栈 分别在数字栈顶取出两个操作数
  • i2c_SLAVE

    driver driver通过I2C的接口与DUT相连 负责接收DUT通过接口传来的地址数据等读写信息并作出反应 将sequencer中传来的trans中的数据驱动到总线上并根据设置的ack和nack作出回应 将总线上写来的数据存到tran
  • Visual Studio 2019 C语言程序(VS2019 C语言)

    新的Visual Studio 2019出来已经有一段时间了 博主也是一开始就从vs2017换到了vs2019 发现整体的操作流程还是有一定的改变 因为之前发表过一个vs2017的博客 对Visual Studio IDE和风格确实非常喜欢
  • Qt5 通过 QFtp 实现 Ftp文件下载

    在Qt5环境下使用自编译的QFtp库 参考官方提供的实例 对部分内容进行更改 实现了Ftp下载文件的客户端 详细代码如下 h头文件 ifndef FTPWINDOW H define FTPWINDOW H include
  • linux下查看某服务端口对应的进程ID

    Linux下查看某端口号所对应的进程ID 使用lsof命令 格式为 lsof i 端口号 例如 lsof i 30000 root Web Service CAServer lsof i 30000 COMMAND PID USER FD
  • oracle的io优化--db_writer_processes & dbwr_io_slaves对比

    author skate time 2011 09 29 db writer processes 和 dbwr io slaves对比 在计算机世界里 磁盘的发展速度远低于cpu memory 磁盘io现在已经成为计算机的瓶颈 对于orac
  • 揭密微信跳一跳小游戏那些外挂

    欢迎大家前往云 社区 获取更多腾讯海量技术实践干货哦 作者 Hahn 腾讯高级UI工程师 由 WeTest质量开放平台团队 发布于云 社区 WeTest 导读 张小龙 这个游戏发布以后 其实它的效果有点超出我们的预期 我们自己开玩笑说 这个
  • mysql数据表查询操作

    数据表查询操作 准备工作 导入之前需要先创建一个数据库 数据库资料传送门 使用新创建的数据库 使用 source 文件地址 导入数据 create database db charset utf8 use db source home py
  • win10网上邻居看不到别的共享电脑怎么样办

    https jingyan baidu com article 4853e1e5b714aa1909f72600 html 转载于 https www cnblogs com kuangke p 10901469 html
  • 【Dom获取&属性操作】JavaScript 全栈体系(十)

    Web APIs 第四章 操作元素属性 一 操作元素常用属性 还可以通过 JS 设置 修改标签元素属性 比如通过 src更换 图片 最常见的属性比如 href title src 等 语法 对象 属性 值
  • python+appium自动化测试-pytest+allure测试报告(一)

    来自APP Android端自动化测试初学者的笔记 写的不对的地方大家多多指教哦 一 Allure安装 1 pytest和allure插件安装 pip install allure pytest pip install pytest 2 A
  • python3+robotframework+selenium3 浏览器兼容性测试

    robot framework 测试浏览器兼容性 目前ride已支持一下浏览器 firefox ie chrome safari 本次我们已win 10中的ie为例 来看看如何使用python3 robotframework seleniu
  • 我是如何从通信转到Java软件开发工程师的?

    我的CSDN和公众号的读者里面有绝大部分都是在校学生 有本科的 也有专科的 我在微信里收到很多读者的提问 大部分问题都跟如何学习编程有关 有换专业自学的 有迷茫不知道如何学习的 有报培训班没啥效果的等等 我能感受到他们的诚意和焦虑 所以我觉
  • 哪些情况可以终止线程呢

    目录 哪些情况可以终止线程的进行 题目解析 线程结束的三个原因 线程结束的三种方法 具体分析 1 使用标志位推出线程 2 使用stop方法强制终止线程 3 使用interrupt终止线程 哪些情况可以终止线程的进行 题目解析 答案选C A
  • Kotlin常用的高阶函数(Filter、TakeWhile、Let、Apply、With......)

    一 Filter package net println kotlin chapter5 builtins author wangdong description Kotlin常见的高阶函数 fun main args Array
  • 内核的并发和竞态(信号量、completion、自旋锁)

    1 并发和并行 1 并行 在同一时刻有多个线程一起运行 2 并发 在同一时刻只有一个线程在运行 但是在一个时间段内有多个线程运行 总结 并发是宏观的并行 因为CPU运行特别快 虽然CPU不断在切换运行的线程 但是对于人来说 根本感知不到CP