深度解析Linux kernel同步机制(上篇)

news2024/12/26 22:39:48

在现代操作系统里,同一时间可能有多个内核执行流在执行,因此内核其实像多进程多线程编程一样也需要一些同步机制来同步各执行单元对共享数据的访问,尤其是在多处理器系统上,更需要一些同步机制来同步不同处理器上的执行单元对共享的数据的访问。在主流的Linux内核中包含了如下这些同步机制包括:

  • 原子操作
  • 信号量(semaphore)
  • 读写信号量(rw_semaphore)
  • Spinlock
  • Mutex
  • BKL(Big Kernel Lock,只包含在2.4内核中,不讲)
  • Rwlock
  • brlock(只包含在2.4内核中,不讲)
  • RCU(只包含在2.6内核及以后的版本中)
  • seqlock(只包含在2.6内核及以后的版本中)

本文章分为两部分,这一章我们主要讨论原子操作,自旋锁,信号量和互斥锁。

一、原子操作

原子操作的概念来源于物理概念中的原子定义,指执行结束前不可分割(即不可打断)的操作,是最小的执行单位。

原子操作与硬件架构强相关,其API具体的定义均位于对应arch目录下的include/asm/atomic.h文件中,通过汇编语言实现,内核源码根目录下的include/asm-generic/atomic.h则抽象封装了API,该API最后分派的实现来自于arch目录下对应的代码。

Structure Definition

typedefstruct{intcounter;}atomic_t;  

原子操作主要用于实现资源计数, 许多引用计数(refcnt)就是通过原子操作实现,例如TCP/IP协议栈的IP碎片中,struct ipq中的refcnt字段,类型即为atomic_t。

atomic_add

原子操作的实现比较简单,以下为例。

原子操作的原子性依赖于ldrex与strex实现,ldrex读取数据时会进行独占标记,防止其他内核路径访问,直至调用strex完成写入后清除标记。自然strex也不能写入被别的内核路径独占的内存,若是写入失败则循环至成功写入。

API

原子操作的API包括如下, 以arm平台为例:

二 、自旋锁(spinlock)

自旋锁是这样一种同步机制:若自旋锁已被别的执行者保持,调用者就会原地循环等待并检查该锁的持有者是否已经释放锁(即进入自旋状态),若释放则调用者开始持有该锁。自旋锁持有期间不可被抢占。

Structure Definition

从定义出发, spinlock根本的实现依赖于具体架构实现中slock这个变量,由于spin_lock是大多locking机制的基础,我们看一看它的实现。

Lock & Unlock

核心unlock函数,使owner自增,保持数据同步。

核心lock函数,使slock +2^16, 当next==owner时,释放锁,否则进入循环等待。Prefetchw用于cache预加载数据。

由于slock与tickets共享同一块内存(union),slock 占32位4字节,tickets内部变量next与owner各16位2字节。以大端序为例,slock 高2字节与next共享,低2字节与owner共享,因此arch_spin_lock实际上是将tickets.next+1。假设初始时next与owner皆为0,此时next与owner不等,通过wfe指令进入一小段时间等待状态,而后读取新的owner值检查与next是否相等,不等则继续等待,相等则结束等待。

而owner的值由arch_spin_unlock控制,即unlock控制何时结束等待。

Spin_lock basic API

Spin_lock API & irq

性能上,spin_lock > spin_lock_bh > spin_lock_irq > spin_lock_irqsave。

安全上,spin_lock_irqsave > spin_lock_irq > spin_lock_bh >spin_lock。

Spin_lock 不同版本的使用

spin_lock用于阻止在不同CPU上的执行单元对共享资源的同时访问以及不同进程上下文互相抢占导致的对共享资源的非同步访问,而中断失效(spin_lock_irq)和软中断失效(spin_lock_bh)却是为了阻止在同一CPU上软中断或中断对共享资源的非同步访问。

  • 如果被保护的共享资源只在进程上下文访问和软中断上下文访问,那么当在进程上下文访问共享资源时,可能被软中断打断,从而可能进入软中断上下文来对被保护的共享资源访问,因此对于这种情况,对共享资源的访问最好使用spin_lock_bh和spin_unlock_bh来保护。
  • 如果被保护的共享资源只在进程上下文和tasklet或timer上下文访问,那么应该使用与上面情况相同,因为tasklet和timer是用软中断实现的。
  • 如果被保护的共享资源只在两个或多个tasklet或timer上下文访问,那么对共享资源的访问仅需要用spin_lock和spin_unlock来保护,不必使用_bh版本,因为当tasklet或timer运行时,不可能有其他tasklet或timer在当前CPU上运行。 如果被保护的共享资源只在一个软中断(tasklet和timer除外)上下文访问,那么这个共享资源需要用spin_lock和spin_unlock来保护,因为同样的软中断可以同时在不同的CPU上运行。
  • 如果被保护的共享资源在软中断(包括tasklet和timer)或进程上下文和硬中断上下文访问,那么在软中断或进程上下文访问期间,可能被硬中断打断,从而进入硬中断上下文对共享资源进行访问,因此,在进程或软中断上下文需要使用spin_lock_irq和spin_unlock_irq来保护对共享资源的访问。
  • 在使用spin_lock_irq和spin_unlock_irq的情况下,完全可以用spin_lock_irqsave和spin_unlock_irqrestore取代,那具体应该使用哪一个也需要依情况而定,如果可以确信在对共享资源访问前中断是使能的,那么使用spin_lock_irq更好一些,因为它比spin_lock_irqsave要快一些。

 资料直通车:Linux内核源码技术学习路线+视频教程内核源码

学习直通车:Linux内核源码内存调优文件系统进程管理设备驱动/网络协议栈

三、信号量(Semaphore)

Linux内核的信号量在概念和原理上与用户态的System V的IPC机制信号量是一样的,但是它不可能在内核之外使用,因此它与System V的IPC机制信号量完全不同。

信号量是这样一种同步机制:信号量在创建时设置一个初始值count,用于表示当前可用的资源数。一个任务要想访问共享资源,首先必须得到信号量,获取信号量的操作为count-1,若当前count为负数,表明无法获得信号量,该任务必须挂起在该信号量的等待队列等待;若当前count为非负数,表示可获得信号量,因而可立刻访问被该信号量保护的共享资源。当任务访问完被信号量保护的共享资源后,必须释放信号量,释放信号量通过把count+1实现,如果count为非正数,表明有任务等待,它也唤醒所有等待该信号量的任务。

Structure Definition

可以发现,信号量是基于spinlock实现的,对其封装以满足高级的功能,例如全局共享资源的配置,并通过等待队列较为灵活的调度。信号量与接下来要讲的mutex都建立在自旋锁实现的执行同步上。

了解了信号量的结构与定义,我们来看看最核心的两个实现down ,up。

down & up

down用于调用者获得信号量,若count大于0,说明资源可用,将其减一即可。

若count<0,将task加入等待队列,并进入等待队列,并进入调度循环等待,直至其被__up唤醒,或者因超时以被移除等待队列。

up用于调用者释放信号量,若waitlist为空,说明无等待任务,count+1,该信号量可用。

若waitlist非空,将task从等待队列移除,并唤醒该task,对应__down条件。

Semaphore API

四、互斥锁(Mutex)

Linux 内核互斥锁是非常常用的同步机制,互斥锁是这样一种同步机制:在互斥锁中同时只能有一个任务可以访问该锁保护的共享资源,且释放锁和获得锁的调用方必须一致。因此在互斥锁中,除了对锁本身进行同步,对调用方(或称持有者)必须也进行同步。当互斥锁无法获得时,task会加入等待队列,直至可获得锁为止。

Structure Definition

互斥锁从结构上看与信号量十分类似,但将原本的int类型的count计数,改成了atomic_long_t的owner以便同步,保证释放者与持有者一致。

mutex_lock & mutex_unlock

上图简单的表现了mutex_lock与mutex_unlock实现的对称性,___mutex_trylock_fast用于owner为0的特殊状态,用于快速加锁,实现核心在slowpath版本上。

*might_sleep指在之后的代码执行中可能会sleep。

由于mutex实现的具体步骤相当复杂,这里选讲比较核心简单的两块。Mutex有关等待队列的处理比较复杂,有兴趣阅读相关内核书籍。

当且仅当lock当前的owner没有变化时(没有其他mutex抢先拥有该锁),此时获得锁,返回NULL, owner 为 curr | flags,owner本身对应task指针。若该锁已被占用,owner和当前task不匹配,返回owner对应指针。

当unlock时,不考虑等待队列的影响,则与上述类似,当且仅当之前持有锁的owner可以解锁,解锁时本来应将lock的owner置为初始0,但是这里保留了mutex的flag以便后续操作。

*这里的owner实际上是task_struct的指针,也就是地址,由于task_struct的地址是L1_cache对齐的,因此实际上指针地址后三位为0,因此linux内核利用这三个比特位用于设置mutex的标志位,不影响指针地址的表示也更高效利用了冗余的比特位。

Mutex 的改进

最初的互斥锁仅支持睡眠等待,然而经过漫长时间的改进,如今的互斥锁已经可以支持自旋等待,通过MCS锁机制实现。在内核中可以选择配置以支持,CONFIG_MUTEX_SPIN_ON_OWNER。

如上是4.9内核中mutex中常用有效的字段,目前最常用的算法是OSQ算法。自旋等待机制的核心原理是当发现持有者正在临界区执行并且没有其他优先级高的进程要被调度(need_resched)时,那么mutex当前所在进程认为该持有者很快会离开临界区并释放锁,此时mutex选择自旋等待,短时间的自旋等待显然比睡眠-唤醒开销小一些。

在实现上MCS保证了同一时间只有一个进程自旋等待持有者释放锁。MCS 的实现较为复杂,具体可参考一些内核书籍。MCS保证了不会存在多个cpu争用锁的情况,从而避免了多个CPU的cacheline颠簸从而降低系统性能的问题。

经过改进后,mutex的性能有了相当大的提高,相对信号量的实现要高效得多。因此我们尽量选用mutex。

Mutex 的使用条件

Mutex虽然高效,灵活,但存在若干限制条件,需要牢记:

  • 同一时刻只有一条内核路径可以持有锁
  • 只有锁持有者可以解锁
  • 不允许递归加锁解锁
  • 进程持有mutex时不可退出
  • Mutex 可能导致睡眠阻塞,不可用于中断处理与下半部使用

Mutex API

 

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

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

相关文章

开源之夏 2023 | 欢迎报名openEuler sig-eBPF开发任务

开源之夏是中国科学院软件研究所联合openEuler发起的开源软件供应链点亮计划系列暑期活动&#xff0c;旨在鼓励在校学生积极参与开源软件的开发维护&#xff0c;促进优秀开源软件社区的蓬勃发展。活动联合各大开源社区&#xff0c;针对重要开源软件的开发与维护提供项目&#x…

Windows版Sketch软件也太好用了吧!

目前为止&#xff0c;Sketch 软件仅支持 macOS 系统&#xff0c;没有推出 Windows 版本&#xff0c;而且在短期内也不太可能推出。这是因为 Sketch 软件是使用独有的 OS X 技术和框架开发的&#xff0c;它依赖于许多 OS X 的核心功能&#xff0c;这些功能不容易轻松移植到其他操…

[MYAQL / Mariadb] 数据库学习-管理表记录2:匹配条件

管理表记录-匹配条件 匹配条件基本条件查询逻辑匹配&#xff08;多个条件判断&#xff09; 高级条件范围匹配模糊查询正则表达式&#xff1a; regexp四则运算 操作查询结果&#xff08;对查找到的数据再做处理&#xff09;排序分组&#xff08;一样的显示一次&#xff09;&…

FreeRTOS_系统配置

目录 1. FreeRTOSConfig.h 文件 2. "INCLUDE_" 开始的宏 2.1 INCLUDE_xSemaphoreGetMutexHolder 2.2 INCLUDE_xTaskAbortDelay 2.3 INCLUDE_vTaskDelay 2.4 INCLUDE_vTaskDelayUntil 2.5 INCLUDE_vTaskDelete 2.6 INCLUDE_xTaskGetCurrentTaskHandle 2.7 IN…

数据结构与算法(二)

一、数组 什么是数组&#xff1f; 数组&#xff1a;在内存中用一串连续的区域来存放一些值。数组是相同类型数据元素的有序集合 数组是由相同类型的元素的集合组成的数据结构 连续内存&#xff1a;JS的数组元素可以是任意类型&#xff0c;JS中的内存地址是不连续的 数组的…

工业互联网的新应用:高端装备的故障预测和健康管理

高端装备制造行业作为我国战略性新兴产业的重要组成部分&#xff0c;面临着产品复杂和生命周期长的挑战。为了提高装备的可靠性、可用性和维护效率&#xff0c;工业互联网技术在高端装备的故障预测和健康管理方面发挥了重要作用。 本文将探讨工业互联网在高端装备中故障预测和…

如何应用金字塔模型提高结构化表达能力

看一下结构化表达的定义&#xff1a; 结构化表达&#xff1a;是基于结构化思维&#xff0c;理清事物整理与部分之间关系、换位思考后&#xff0c;进行简洁、清晰和有信服力的表达&#xff0c;是一种让受众听得明白、记得清楚、产生认同的精益沟通方式。 结构化表达的基本原则是…

度娘教我influxdb,先搞一波在补理论

InfluxDb中的数据查询语法where子句 公司用的时序性数据库是influxdb&#xff0c;但是确实接触有点少。 心里话&#xff1a; 就是没接触过&#xff0c;度娘教教我。。。。 我咋回呢&#xff0c;现学吧&#xff0c;都是数据库&#xff0c;天下乌鸦一般黑然后就哟了以下&#xf…

Mac 更换.gradle文件目录

用mac开发最大滑铁卢居然是存储空间不足......于是加了一个外置的存储器。于是需要把android开发中最大的一个缓存目录移到外置存储器... 一开始对android studio的进行了如下设置 于是乎...Mac用户下的.gradle全删了...这个时候用android studio打包运行都没问题了。 如果你…

AI在视频教学方面会有哪些有意思应用?

阿酷TONY / 原创 / 2023-5-17 / 长沙 来&#xff0c;我们一起来探索未来式的学习体验&#xff0c;AI视频&#xff0c;AI与视频的应用将为你带来前所未有的教学体验。AI在视频教学方面的应用&#xff0c;主要会体现在如下几个方面&#xff1a; 1. AI 课程知识点梳理 2. AI 学…

前段时间公司招人,面了一个要23K的,一问自动化只会点皮毛···

前段时间公司要招2个自动化测试&#xff0c;同事面了几十个候选人&#xff0c;发现了一个很奇怪的现象&#xff0c;面试的时候&#xff0c;如果问的是框架api、脚本编写这些问题&#xff0c;基本上个个都能对答如流&#xff0c;等问到实际项目的时候&#xff0c;类似“怎么从0开…

OPPO关停哲库业务,工程师造芯何去何从?

5月12日&#xff08;上周五&#xff09;&#xff0c;新浪科技从OPPO处了解到&#xff0c;OPPO将终止ZEKU业务。3000多人团队突然原地解散&#xff0c;网上唏嘘声一片&#xff01; ZEKU最初成立于2019年&#xff0c;是OPPO的全资子公司&#xff0c;欧加集团百分之百注资成立。总…

提高合规性与安全性:ADAudit Plus助力企业数据审计

简介&#xff1a;在现代数字化时代&#xff0c;企业面临着日益增长的数据安全威胁和法规合规要求。为了应对这些挑战&#xff0c;企业需要强大的数据审计解决方案来监控和保护其敏感数据。ADAudit Plus是一款功能强大的工具&#xff0c;旨在帮助企业提高合规性&#xff0c;并提…

dll注入技术

一、dll注入的概念 当一个进程运行时&#xff0c;它会加载并使用一些动态链接库&#xff08;DLL&#xff09;来提供额外的功能和资源。这些DLL可以被多个进程共享&#xff0c;使得代码重用和资源共享变得更加高效。DLL注入技术利用了这种共享机制。它允许向正在运行的进程中注…

厦大凭什么和清北复一起入选首批国家集成电路产教融合创新平台?对台区位优势吗

最近&#xff0c;有网友提出这样一个问题&#xff0c;众所周知&#xff0c;中国集成电路研究主要集中在北京和上海&#xff0c;清北可以和北京合作&#xff0c;复旦可以和上海合作&#xff0c;那么厦大和谁合作&#xff0c;厦大又凭什么和清北复一起入选了首批国家集成电路产教…

RecyclerView 分层级展示(抽屉) TreeView

先看效果 难点: 数据层级的划分;理清楚层级关系, 剩下的就简单明了了; 1. 第一步 new Adapter, and setAdapter; (不需要setLayoutManager, Adapter里有set); mAdapter new TreeViewAdapter(getContext(),mRecyclerView); mRecyclerView.setAdapter(mAdapter); 2. 第二步, …

素雅的登录界面,简单而优雅

先上效果图&#xff1a; 再上代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><style>*, *::after, *::before {margin: 0;padding: 0;box-sizing: bord…

本地服务器与云服务器哪个好?

本地服务器和云服务器是企业可以使用的两种不同的服务器设置。主要区别在于本地服务器托管&#xff0c;第三方提供商托管云服务器。那么&#xff0c;本地服务器和云服务器哪个更好呢&#xff1f; 接下来&#xff0c;将带大家讨论本地服务器和云服务器的优缺点&#xff0c;并帮…

Golang笔记:使用embed包将静态资源嵌入到程序中

文章目录 目的使用演示//go:embed 指令在WebServer中应用总结 目的 Golang编译程序默认是静态编译&#xff0c;会把相关的库都打包到一起&#xff0c;这在分发部署使用时非常方便。不过如果项目中用到的外部的静态资源文件&#xff0c;通常就需要将这些资源和程序一起拷贝分发…

软件测试面试大全(涵盖了软件测试的全部核心技术点),应对技术面妥妥的

软件测试面试题&#xff1a;项目 1、简单介绍下最近做过的项目 根据自己的项目整理完成&#xff0c;要点&#xff1a; 1&#xff09;项目背景、业务、需求、核心业务的流程 2&#xff09;项目架构&#xff0c;B/S 还是 C/5&#xff0c;数据库用的什么? 中间件用的什么&…