Linux kernel同步机制

在现代操作系统里,同一时间可能有多个内核执行流在执行,因此内核其实像多进程多线程编程一样也需要一些同步机制来同步各执行单元对共享数据的访问,尤其是在多处理器系统上,更需要一些同步机制来同步不同处理器上的执行单元对共享的数据的访问。在主流的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要快一些。

三、信号量(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

    推荐阅读:

    专辑|Linux文章汇总

    专辑|程序人生

    专辑|C语言

嵌入式Linux

微信扫描二维码,关注我的公众号 

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

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

相关文章

linux 文件系统 簇 浪费空间,Linux rm -rf删除文件不释放空间的解决办法

前几天发现在Linux系统下有一个很大的无用文件&#xff0c;于是用rm -rf 删除&#xff0c;然后用df -h查看磁盘空间&#xff0c;发现即使文件被删除了&#xff0c;但文件所占用的空间并未释放&#xff0c;十分疑惑&#xff0c;于是在网上找到了解决方案&#xff0c;即使用lsof …

[Diary]6.10

一大早回闵行&#xff0c;继续奋斗论文。我真是个小巫&#xff0c;不停的自言自语。

linux 特定用户ssh,linux - 如何在登录后将SSH用户限制为一组预定义的命令?

你为什么不写自己的login-shell&#xff1f; 为此使用Bash会非常简单&#xff0c;但您可以使用任何语言。Bash中的示例使用您喜欢的编辑器创建文件;(这可以是任何名称或路径&#xff0c;但应该是&和&&)&#xff1a;#!/bin/bashcommands("man" "pwd&…

水晶报表的推模式

在一个多层结构中&#xff0c;水晶报表的使用往往比较繁琐&#xff1a;1、在项目中添加数据集&#xff0c;形成xsd文件2、利用xsd文件&#xff0c;在水晶报表环境中生成报表3、回到.net&#xff0c;添加rpt文件这样做的坏处除了繁琐&#xff0c;就是在UI层直接要访问数据库&…

关于sql和MySQL的语句执行顺序(必看!!!)

ql和mysql执行顺序&#xff0c;发现内部机制是一样的。最大区别是在别名的引用上。 一、sql执行顺序 (1)from (3) join (2) on (4) where (5)group by(开始使用select中的别名&#xff0c;后面的语句中都可以使用)(6) avg,sum.... (7)having (8) select (9) distinct (10) orde…

fedora linux命令,Fedora Linux的一些常用设置和常用命令

1.设置常用路径跳转:alias ubootcd /opt/U-boot-2009.11_tekkaman/U-boot-2009.11_tekkaman/2.samba服务重启命令在更改ip后&#xff0c;或修改samba配置文件后&#xff0c;如果没有重启服务&#xff0c;所作的修改实际上是不会生效的重启网络服务的命令是service network rest…

10大黑客专用的 Linux 操作系统,你了解哪些?

今天列出一些最常用、最受欢迎的Linux发行版来学习黑客和渗透测试。1. Kali LinuxKali Linux是最著名的Linux发行版&#xff0c;用于道德黑客和渗透测试。Kali Linux由Offensive Security开发&#xff0c;之前由BackTrack开发。Kali Linux基于Debian。它带有来自安全和取证各个…

【windows phone】CollectionViewSource的妙用

在windows phone中绑定集合数据的时候&#xff0c;有时候需要分层数据&#xff0c;通常需要以主从试图形式显示。通常的方法是将第二个ListBox&#xff08;主视图&#xff09;的数据源绑定到第一个ListBox &#xff08;从视图&#xff09;的SelectedItem&#xff0c;或者通过第…

ftp linux包,图文详解Ubuntu搭建Ftp服务器的方法(包成功)

一、今天下午由于课程的要求不得已做了Ubuntu搭建Ftp服务器的实验&#xff0c;但是实验指导书还是N年前的技术&#xff0c;网上搜了一大把&#xff0c;都是模模糊糊的&#xff01;在百般困难中终于试验成功&#xff0c;特把经验分给大家 希望大家少走弯路&#xff01;二、详细步…

产品狗,工作三年,转行AI应该怎样规划?

作为AI 初学者来说&#xff0c;最大的问题就是&#xff1a;资料太多&#xff01;&#xff01;&#xff01;看不完&#xff01;&#xff01;&#xff01;不知道如何取舍&#xff01;&#xff01;&#xff01;人的精力有限&#xff01;&#xff01;&#xff01;大部分想转行AI算法…

业务系统里面常见的方法接口设计

在程序中用得最多的还是方法&#xff0c;在处理业务的系统中如何设计方法是一个很重要的问题。很多系统由于缺乏统一的约定而导致维护困难。本文主要介绍的是偶在平常的开发中总结出来的一些方法设计的思路。希望大家不嫌弃。 1. 简单的方法 void DoSth(args) { // …

linux python 信号,Python模块之信号(signal)

在了解了Linux的信号基础之 后&#xff0c;Python标准库中的signal包就很容易学习和理解。signal包负责在Python程序内部处理信号&#xff0c;典型的操作包括预设信号处理函数&#xff0c;暂 停并等待信号&#xff0c;以及定时发出SIGALRM等。要注意&#xff0c;signal包主要是…

你为什么喜欢VIM?

昨天看到的一个讨论&#xff0c;说vim有没有那么必要&#xff1f;所以写了这篇文章&#xff0c;在评论区说出你的观点&#xff0c;当然说出你的观点的时候&#xff0c;你拿不到一百万&#xff0c;也得不到小红花&#xff0c;但也是因为你的评论&#xff0c;我可能会开心一整天。…

新世纪篇章

团队blog开通http://www.cnblogs.com/team/solartimes.html还没弄明白怎么搞。。。一阵研究solartimes成员&#xff0c;快来注册。。。 转载于:https://www.cnblogs.com/konimeter/archive/2005/09/19/239671.html

RTT大牛告诉你,混合微内核是什么?

RT-Thread之前写过好几篇文章介绍微内核&#xff0c;然而不少开发者依然不清楚到底微内核操作系统是什么。为此&#xff0c;我们特别整理了5期快问快答&#xff0c;来为大家答疑解惑&#xff01;RT-Thread Smart称作是混合微内核操作系统&#xff0c;为什么叫混合微内核操作系统…

linux操作指令及根目录介绍

1. 查看当做操作目录位置 > pwd 2. 查看(当前)目录里边的文件内容 > ls //list > ls -l 或ll //显示文件的详细信息 > ls -al //all显示文件的详细信息(包括隐藏文件) > ls -a //显示目录全部文件名字(包括隐藏文件) > ls [-al] 目录 //查看指定目录的文件信…

计算机linux运维日记,计算机的运维系统情况

其实&#xff0c;对于计算机的主要部件来说&#xff0c;计算机是有硬件和软件组成的&#xff0c;并且能够通过硬件和软件的协同工作完成了的&#xff0c;而对于一些计算机的硬件&#xff0c;则是由一些不同的部件进行完成的&#xff0c;那么其中主要的补间就是内存&#xff0c;…

Linux ALSA 图解

最近在解决一个音频的问题&#xff0c;所以正好借这个机会来把音频的东西重新梳理一下&#xff0c;总结是一个很好的习惯&#xff0c;能方便自以后遇到问题快速排查问题。平台「MT8167」内核版本「kernel 4.4」音频读数据函数流程tinyalsa调用读取函数IOCTL调用流程读取数据到应…

OpenGL学习笔记-坐标系统

转换关系&#xff1a; 局部坐标系 &#xff08;模型矩阵&#xff09;》 世界坐标系&#xff08;观察矩阵&#xff09;》观察坐标&#xff08;投影矩阵&#xff09;》裁剪坐标 Vclip Mprojection * Mview * Mmodel * Vlocal 裁剪空间&#xff08;透视划分&#xff09;》标准化设…