《Linux 内核设计与实现》10. 内核同步方法

news2024/12/22 10:01:10

文章目录

    • 原子操作
      • 原子整数操作
      • 64 位原子操作
      • 原子位操作
    • 自旋锁
    • 读写自旋锁
    • 信号量
      • 计数信号量和二值信号量
      • 信号量方法列表
    • 读写信号量
    • 互斥体
      • 信号量和互斥体
      • 自旋锁和互斥体
    • 完成变量
    • BLK:大内核锁
    • 顺序锁
    • 禁止抢占
    • 顺序和屏障

原子操作

原子操作:可以保证指令以原子的方式执行,即执行过程不被打断。

原子整数操作

整数的原子操作只针对 atomic_t 类型。因为:

  • 让原子函数只接收 atomic_t 类型的操作数,可以确保原子操作只与这种特殊类型数据一起使用。同时这也保证了该类型的数据不会被传递给任何非原子函数。
  • 使用 atomic_t 类型确保编译器不对相应的值进行访问初始化 —— 这点使得原子操作最终接收到正确的内存地址,而不只是一个别名。

atomic_t 类型定义在 include/linux/types.h:

typedef struct {
	volatile int counter;
} atomic_t;

在 Linux 上 atomic_t 整数类型都是 32 位,其中数据位为高 24 位,低 8 位嵌入了一个锁,因为 SPARC 体系结构对原子操作缺乏指令级支持,所以只能利用该锁来避免对原子类型数据的并发访问。因此在 SPARC 机器上只能用 24 位,在其它机器上可以用 32 位。

(貌似已经在 SPARC 机器上只能使用 24 位的问题已经被解决了)

image-20230423200606276

原子操作在 arch/alpha/include/asm/atomic.h 中:

image-20230423200819012

原子操作通常是内联函数,往往是通过内嵌汇编指令来实现的。若某个函数本就具备原子性,那么通常会被定义为一个宏。

在编写代码时,尽量使用原子操作,而不是使用复杂的加锁机制。相对于锁机制,原子操作可以给系统带来较小的开销,对高速缓存行的影响也小。

64 位原子操作

因为移植性的问题,atomic_t 变量无法在体系结构之间改变。因此 atmoic_t 类型即便在 64 位下也是 32 位的,要使用 64 位的,则要使用 atomic64_t 类型,其功能与 32 位无异。

#ifdef CONFIG_64BIT // 判断系统是不是 64 位的
typedef struct {
	volatile long counter;
} atomic64_t;
#endif

image-20230423201709471

开发者应该使用 32 位的 atomic_t 类型,因为为了可以在各体系结构之间可以移植代码。

原子位操作

路径:arch/alpha/include/asm/bitops.h

image-20230423202108919

非原子位函数在其原子位函数名字前多了两条下划线,例如 test_bit() 的非原子位函数为 __test_bit()。

自旋锁

  • Linux 内中最常见的锁。

  • 最多只能被一个线程持有。

  • 若一个线程试图得到一个已被持有的锁,那么该线程就会一直循环等待该锁被释放为止。

  • 在任意时间,自旋锁都可以防止多于一个线程同时进入临界区。

  • 自旋锁因为会循环等待锁,因此特别浪费处理器时间,所有自旋锁不该被长时间占有。

    也可以让请求线程休眠,直到锁重新可用为止。(这会带来一定的开销,如两次上下文切换)

  • 自旋锁的设计初衷:在短期内进行轻量级加锁。

路径:arch/alpha/include/asm/spinlock.h

也可能是这个:include/linux/spinlock.h

自旋锁不可递归:你试图得到一个你持有的锁,必须自旋等待,可是你处于自旋等待中,因此无法释放锁,导致给自己锁死了。
根据此情况,可知在中断使用自旋锁时,一定要先关闭中断。

CONFIG_DEBUG_SPINLOCK 配置选择为使用自旋锁的代码加入了许多调试检测的手段。

image-20230423205728596

读写自旋锁

  • 一个或多个读任务可以并发地持有读者锁(读者锁有多个)。
  • 用于写的锁最多只能被一个写任务持有(写者锁只有一个),且此时不能有并发的读操作。
  • 在中断在使用该锁时:
    • 读任务:无需关闭中断
    • 写任务:必须关闭有写操作的中断

image-20230423210435686

信号量

  • Linux 中的信号量是一种睡眠锁。
  • 一个任务试图得到一个已被占用的信号量时,信号量会将其推进一个等待队列,然后让其睡眠。直到那个信号量被释放,处于等待队列中的那个任务将被唤醒,并获得该信号量。
  • 相比于自旋锁,信号量拥有更好的处理器利用率,当却比自旋锁有更大的开销。

从信号量的睡眠特性得出的一些结论:

image-20230423211158183

计数信号量和二值信号量

  • 使用者数量(usage count)或数量(count):信号量同时允许持有的数量可以在声明信号量中指定。
  • 同一时刻,当只允许一个锁持有者时,信号量(count)为 1,这种情况下,被称为二值信号量或互斥信号量。
  • 数量设置为大于 1 时,即同一时刻最多允许 count 个锁持有者,这被称为计数信号量。

信号量支持的两个原子操作:

  • P() / down():对信号量减一来得到一个信号量。
  • V() / up():对信号量加一释放信号量。

信号量方法列表

路径:include/linux/semaphore.h

image-20230423212058017

读写信号量

位于:include/linux/rwsem.h

down_read_trylock() 和 down_write_trylock() 方法,若成功获得了信号量锁,返回非0,若信号量锁被争用,则返回0,这与普通信号量情况相反,要小心。

互斥体

  • 信号量是内核中唯一允许睡眠的锁。

    信号量适用于较复杂、未明情况下的互斥访问。简单的锁定,使用信号量并不方便,且信号量调试也不方便。

  • 互斥体是比信号量更简单允许休眠的锁。

  • 互斥体指的是任何可以睡眠的强制互斥锁,如计数是 1 的信号量。

  • 互斥体是一种互斥信号。

  • mutex 是简化版的信号量,因为不再需要管理任何使用计数。

  • 开启 mutex 的调试功能:CONFIG_DEBUG_MUTEXES

路径:include\linux\mutex.h

信号量和互斥体

互斥体和信号量很相似,应该优先使用 mutex,不到万不得已就别用信号量,一般只有很底层的代码才需要信号量。因此首选 mutex,若发现不能满足其约束条件,且没有别的选择时,再考虑选择信号量。

自旋锁和互斥体

image-20230424094419651

完成变量

在内核中一个任务需要发出信号通知另一个任务发生了某个特定事件,利用完成变量是使两个任务得以同步的简单方法。当一个任务要执行一些工作时,另一个任务就会在完成变量上等待。当这个任务完成工作后,会使用完成变量去唤醒在等待的任务。

完成变量仅仅只是代替信号量的一个简单方案。例如,当子进程执行或退出时,vfork() 系统调用使用完成变量唤醒父进程。

路径:include\linux\completion.h

image-20230424094448140

BLK:大内核锁

  • 是一个全局自旋锁。
  • 最初的目的是为了方便实现从 SMP 过度到细粒度加锁机制,这个过程当然是有效的,但是现在已经成了内核可扩展性的障碍。
  • 在内核中不鼓励使用 BKL。
  • 一个执行线程递归的请求锁,那么释放锁也必须以同样的次序进行。
  • BKL 更像是保护代码的,而不是保护数据。
  • 难以发现所有 BKL 的用户之间的关系。

BKL 的特性:

image-20230424094730072

顺序锁

  • 简单的锁机制,用于读写共享数据。
  • 该锁的依靠一个序列计数器实现。
  • 当数据被写入时,会得到一个锁,并且序列值增加。在读取数据前后,序列号都被读取。如果读取的序列号值相同,说明在读操作进行的过程中没有被写操作打断过(因为锁的初值是 0,所以写锁会使值成奇数,释放时成偶数)。
  • 如果读取的值是偶数,表面写操作没有发生。
  • seq 锁对写者更有利,只要没有其它写者,写锁总能被成功获得。
  • 读者不会影响写锁。
  • 挂起的写者会不断地使得读操作循环,直到不再有任何写者持有锁为止。

路径:include/linux/seqlock.h

image-20230424101307883

seq 锁使用场景:

image-20230424101241909

禁止抢占

  • 由于内核是抢占的,意味着一个任务与抢占任务可能会在同一个临界区内。
  • 为了避免这种情况,内核抢占代码使用自旋锁作为非抢占区域的标记。
  • 如果一个自旋锁被持有,内核便不能进行抢占。
  • 在多处理器中,若自旋锁未被持有,内核又是抢占式的,那么新调度任务就可能访问同一个变量。
  • 即便是单处理器,变量也可能被伪并发访问。

image-20230424110856301

顺序和屏障

  • 在处理多处理器之间或硬件之间的同步问题时,需要在程序中以指定的顺序发出读指令和写指令。
  • 但编译器和处理器为了提高效率,可能对读和写重新排序。(x86 处理器不会对写重新排序)
  • 所有可能重新排序的处理器都提供了机器指令来确保顺序执行。同样也确保编译器不会对给定点周围的指令序列重新排序。

重新排序的例子:

a = 1;
b = 2;

重新排序导致可能会在 a 中存放新的值之前就在 b 中存放新值。

编译器和处理器都看不出两者之间的关系。编译器会在编译时按这种顺序编译,这种顺序会是静态的,编译的目标代码就只把 a 放在 b 之前。但是处理器会重新动态排序,因为处理器在执行指令期间,会在取指令和分配时,把表面看似无关的指令按自认为最好的顺序排列。大多数情况下,这样的排序是最佳的。

处理器和编译器绝不会重新排序的情况:

a = 1;
b = a;

书中后面这小段我没懂,此处略…

image-20230424133132796

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

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

相关文章

人大金仓KFS全新升级,从容应对“名场面”

系统迁移升级过程中, 迁移停机时间长? 异构数据库迁移成本高? 数据一致性无法保证? 发生故障后缺乏回滚手段? 这些“名场面”您遇到过吗? KFS全新解决方案正式发布 针对用户不同应用场景出现的普遍痛点&…

C++基础之类、对象一(类的定义,作用域、this指针)

目录 面向对象的编程 类的引入 简介 类的定义 简介 访问限定符 命名规则 封装 简介 类的作用域 类的大小及存储模型 this指针 简介 面向对象的编程 C与C语言不同,C是面向对象的编程,那么什么是面向对象的编程呢? C语言编程,规定…

C++之多态与虚函数

文章目录 初识多态运行时多态的原理静态联编和动态联编 初识多态 多态性是面向对象程序设计的关键技术之一。若程序不支持多态,不能称为面向对象的语言编译时多态:通过函数重载实现,早期绑定运行时多态:在程序执行过成中&#xf…

神策微报告丨10 页速览「生成式 AI」能力边界与商业化!

以 ChatGPT 为代表的生成式 AI 投入规模化应用后,一场人工智能的军备竞赛正在上演,生成式 AI 成为科技领域关注的焦点。 基于此背景,神策数据正式发布微报告《关于生成式 AI,这 10 页 PPT 就够了!》,从突破…

DIDCTF平台练习-2022暑假取证学习

文章目录 前言123456789101112131415161718 前言 挺适合新手的,平台地址https://forensics.didctf.com/challenges 1 直接看 WIN-49I0SNRJAMF 2 计算即可 4547A61A11064DF47B272A4803788597F9A5E9AC0F11A93ABE58C8B8588956CB 3 NoxPlayer,夜神…

记一次azkaban调度异常处理

一、背景 预发布环境使用的数据库性能比较低,根据业务测试的需求,需要将数据库更换成 稳定高性能的数据库。更换业务数据库后azkaban定时任务失败 二、数据库服务信息 说明:该部分使用代号来代替,非真实信息 该数据库存储了azka…

docker 搭建 Elasticsearch和Kibana 8.x版本

参考: docker入门:单机elasticsearch安装记录,保证无坑_8月日更_小鲍侃java_InfoQ写作社区 新建文件夹 同上文所述相同,需要在宿主机上挂载配置文件与数据文件。 mkdir -p /Users/louye/data/learn-data/elastic/config mkdir -p /Users/lo…

学系统集成项目管理工程师(中项)系列19a_成本管理(上)

1. 要确保在批准的预算内完成项目 2. 必须考虑项目决策对项目产品、服务或成果的使用成本、维护成本和支持成本的影响 3. 对成本的影响力在项目早期最大 4. 失控原因 4.1. 对工程项目认识不足 4.1.1. 对信息系统工程成本控制的特点认识不足,对难度估计不足 4.…

大数据|实验三:PageRank算法实现

文章目录 📚PageRank概述🐇什么是PageRank🐇PageRank的简化模型🐇PageRank的随机浏览模型 📚实验目的📚实验平台📚实验内容🐇在本地编写程序和调试🐇在集群上提交作业并执…

【Linux脚本篇】流程控制语句-if

目录 🍁流程控制语句if 🍂单分支语句 🍂双分支语句 🍂多分支语句 🍁流程控制语句:文件比较 🍁流程控制语句:整数比对 🍁流程控制语句:字符对比 🍁…

校园企业车辆维修报修管理系统设计与开发

本研究课题重点主要包括了下面几大模块:在本基于.net平台的车辆系统中分为管理员和用户2个模块,主要功能包括管理员信息管理,车辆信息管理,驾驶员信息管理,事故信息管理,维修信息管理,维修点管理…

【Leetcode -463.岛屿的周长 - 476.数字的补码】

Leetcode Leetcode -463.岛屿的周长Leetcode - 476.数字的补码 Leetcode -463.岛屿的周长 题目:给定一个 row x col 的二维网格地图 grid ,其中:grid[i][j] 1 表示陆地, grid[i][j] 0 表示水域。 网格中的格子 水平和垂直 方向…

C/C++每日一练(20230509) 分割回文串II、盛水容器、Atoi

目录 1. 分割回文串 II 🌟🌟🌟 2. 盛最多水的容器 🌟🌟 3. 字符串转换整数 (atoi) 🌟🌟 🌟 每日一练刷题专栏 🌟 Golang每日一练 专栏 Python每日一练 专栏 C/…

Springboot +Flowable,三种常见网关的使用(排他、并行、包容网关)(二)

一.简介 Flowable 中常用的网关主要有三种类型,分别是: 排他网关并行网关包容网关 下面来说下这三种的网关的概念和用法。 二.并行网关 并行网关,这种网关一般用在并行任务上,截图如下: 并行网关一般是成对出现的…

Vue学习笔记3 - Vue中 radio/select 如何设定初期值

使用 v-model 如何设定 radio的初期值呢&#xff1f; 使用v-model 绑定 value 属性&#xff0c;然后设置初始 value 属性的值即可。 比如 sexValue设置为 女&#xff0c;那么 女 那项就会被默认选中。 <!DOCTYPE html> <html lang"en"> <head>&…

数字孪生模型构建理论及应用

源自&#xff1a;计算机集成制造系统 作者&#xff1a;陶飞 张贺 戚庆林 徐 俊 孙铮 胡天亮 刘晓军 刘庭煜 关俊涛 陈畅宇 孟凡伟 张辰源 李志远 魏永利 朱铭浩 肖斌 摘 要 数字孪生作为实现数字化转型和促进智能化升级的重要使能途径&#xff0c;一直备受各…

Vue-01---初识Vue

一.搭建Vue开发环境 不建议初学者直接使用vue-cli脚手架 不建议初学者使用开发工具直接创建Vue工程 直接在html中用script引入 浏览器安装Vue Devtools插件 CDN链接引入&#xff08;不建议&#xff09;&#xff1a; <script src"h…

什么是电子负载?

1、简介 电子负载在硬件测试中是使用频率比较高的设备之一&#xff0c;是一种从电源吸收电流并消耗功率的测试仪器&#xff0c;基本都是通过控制内部功率器件&#xff08;Mosfet&#xff09;导通量&#xff0c;依靠功率管的耗散功率消耗电能。很多初入硬件或者硬件测试的小伙伴…

物联网安全工作梳理(0)

物联网相比互联网,设备更多,协议更多,标准不统一,安全更脆弱,因此相当于互联网的安全漏洞增量。物联网安全整改流程相比互联网在增量上工作更多些。本篇将从八个方面阐述物联网安全整改工作总结,每个面都是一项大工程。 物联网与互联网差异 物联网安全可分为8大类来说明…

PostgreSQL 新闻速递 谷歌基于POSTGRESQL 兼容数据库提供更大规模的数据库服务

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…