Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >用Rust语言开发Linux内核,得先过内存模型这关

用Rust语言开发Linux内核,得先过内存模型这关

原创
作者头像
beyondma
修改于 2021-10-18 02:29:02
修改于 2021-10-18 02:29:02
75500
代码可运行
举报
文章被收录于专栏:Rust学习专栏Rust学习专栏
运行总次数:0
代码可运行

最近Rust For Linux的项目,随着Rust的火爆也开始逐渐升温,但是谷歌的强烈支持以及rCore OS、Redox等各种Rust操作系统项目的经验积累,Rust想进入到Linux的真正核心,也还是有很长的路要走,之前笔者已经撰文对于Rust在汇编支持、panic和alloc等系统操作等方面的问题进行过简要说明了。这里再对于Rust进入到Linux内核的最大拦路虎-也就是内存模型方面的问题,做一下介绍。

内存模型对于操作系统为何如此重要

我们这里所说的内存模型并不是操作系统管理和分配内存的机制,而是对于程序指令执行顺序及可打断性的执行策略,内存模型在单核单线程的时代几乎没有意义,直到2004年,Java率先引入了适用于多线程环境的内存模型JSR-133,,自此多核时代下操作系统中内存模型的正式登场。

简单的讲当下最新的编译器、操作系统及处理器等等底层技术栈,都会进行某种程度上对于代码进行重排,以获取执行效率的提升,比如以下代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
x=getStatus()

if (x>0)

    y = x;

else

y = 0;

就可能被编译器优化为以下的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
y=0

x=getStatus()

if (x>0)

    y = x;

当然这样的执行顺序重排都有一项重要的原则,就是不会影响单线程环境下程序的执行结果,但是在多线程并发的情况下,y在x之前先被赋值,这对于程序逻辑是否会有潜在影响,这就是内存模型要面对的问题。

简单来讲,可以认为内存模型是一种程序性能与程序复杂性之间的平衡策略。一般来讲内存模型主要包含了下面三个部分:

原子操作:原子类操作一旦执行就不会被打断,是一种不存在中间状态的操作,它要么是执行完成,要么执行失败,外界无法观测到执行过程中的状态。

指令的执行顺序:定义哪些指令执行的顺序不能被打乱。

操作的可见性:定义哪些操作是需要被其它线程所看到。

内存模型与内存屏障指令对应,无论是写屏障(writebarrier)、读屏障(readbarrier)、还是通用屏障(genericbarrier)其实都是对于这几方面的行为进行明确定义的操作指令。

当然这里并不是要详细介绍内存模型,只是要说明当Rust只进行应用程序的开发时,这门语言大可以不用在意内存模型,因为编译器只负责生成可执行的字节码,至于如何执行那是底层的操作系统和CPU的问题,但是当Rust编写“无限接近计算机底层”的操作内核时,内存模型就会变得很重要。内存模型是多线程环境能够可靠工作的基础,因为内存模型需要对多线程环境的运作细节进行完备的定义。

效率和锁的矛盾

加锁实际上就是限制了多线程计算机体系的运行效率,因为在同一时刻即使你有多个CPU也只能有一个CPU进程在被锁保护的区域工作,因此尽量少用锁甚至不用锁才是最终的目标,但无锁编程是一巨大的挑战。它的难度不仅仅是因为无锁编程本身的复杂度,更在于多线程体系下无锁系统的设计,可能很难被非技术出身的领导所理解,这其中的复杂度积累是非线性的,这里先推荐一下an-introduction-to-lock-free-programming(http://preshing.com/20120612/an-introduction-to-lock-free-programming。)

以最经典的无锁队列为例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void LockFreeQueue::push(Node* newHead) 

{ 

    for (;;) 

    { 

        //复制共享变量(m_Head)到oldHead 

        Node* oldHead = m_Head; 

       //做一些不能被其他线程感知的工作

        newHead->next = oldHead; 

        // 然后尝试将改动发送到共享变量中 

        // 如果共享内存没有改变,则CAS成功,返回 

       if (_InterlockedCompareExchange(&m_Head, newHead, oldHead) == oldHead) 

            return; 

    } 

} 

这里InterlockedCompareExchange的实现简要说明如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int compare_and_swap (int* reg, int newval, int oldval)

 {

 int old_reg_val = *reg;

  if (old_reg_val == oldval) {

    *reg = newval;

 }

  return old_reg_val;

}

可以看到这里无锁的概念其实就是在测试与共享变量reg是否有变化,如果没有变化则操作成功,如果有变化则无需要再操作,因为肯定有其它线程修改了队列。那么这其中最关键的一点就是要对于内存模型中的可见性进行定义了。内存模型必须要保证对于reg的操作如:*reg = newval;对于其它线程是可见的,否则所谓的无锁队列也就不成立了。

Rust中的与众不同的锁

上月底谷歌发布了一个RUST版本GPIO驱动,详见:https://github.com/wedsonaf,其中令人印象最深刻的是RUST和C语言在锁方面的不同

C语言中锁的典型用法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
raw_spin_lock_irqsave(&pl061->lock, flags);

gpiodir = readb(pl061->base + GPIODIR);

gpiodir &= ~(BIT(offset));

writeb(gpiodir, pl061->base + GPIODIR);

raw_spin_unlock_irqrestore(&pl061->lock, flags);

而Rust中锁的用法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let _guard = data.lock();

        let pl061 = data.resources().ok_or(Error::ENXIO)?;

可以看到Rust中的lock锁是与具体要保护的数据是有强绑定关系的,开发者要调用data.lock()将锁进行锁定,只有这样才能受锁保护的数据才能被访问,因此程序员在使用锁时犯错误,不可能出现锁的张冠李戴,但这也会造成其它的问题,由于Rust的变量都是有严格的生命周期及借用机制的,因此锁也很可能要在内存中移动,内存中对象的移动、所有权借用等等除了造成移动锁之外还会有移动构造函数等等问题。

但是移动锁、还移动构造函数这些概念在之前的Linux中几乎是闻所未闻的,还是那句话,这样的问题在Rust只开发上层应用时都不是问题,但一旦深入到操作系统内核,这些就都成了问题,所以说Rust想真正深入到Linux的内核当中还有很多的路要走。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
深入理解无锁编程「建议收藏」
无锁编程是一个挑战,不仅因为任务本身的复杂性,还因为从一开始就很难深入了解这个主题,因为该主题和底层技术(编译器,CPU,内存)息息相关,需要深厚底层功底。
全栈程序员站长
2022/06/28
1K1
深入理解无锁编程「建议收藏」
无锁队列的实现
关于无锁队列的实现,网上有很多文章,虽然本文可能和那些文章有所重复,但是我还是想以我自己的方式把这些文章中的重要的知识点串起来和大家讲一讲这个技术。下面开始正文。
Linux阅码场
2020/09/27
3.8K1
无锁队列的实现
lockfree 的队列的实现
(1)队列的大小(m_lMaxQueueSize)应该足够的大,避免处理不过来时,找半天找不到空位置。
全栈程序员站长
2022/09/09
4460
lockfree 的队列的实现
无锁编程基础[通俗易懂]
活锁、死锁本质上是一样的,原因是在获取临界区资源时,并发多个进程/线程声明资源占用(加锁)的顺序不一致,死锁是加不上就死等,活锁是加不上就放开已获得的资源重试,其实单机场景活锁不太常见。举个例子资源A和B,进程P1和P2,
全栈程序员站长
2022/06/28
1.1K0
无锁编程基础[通俗易懂]
深入解析Java内存模型:并发编程的关键基石
在现代并发编程中,内存模型是一个至关重要的概念,它直接影响程序的正确性和性能。Java内存模型(Java Memory Model,简称JMM)为Java程序员提供了一套关于多线程如何交互的规则。理解JMM对于编写高效且正确的多线程应用程序至关重要。
AI码师
2024/06/19
1260
深入解析Java内存模型:并发编程的关键基石
CAS和ABA问题
无锁编程是指在不使用锁的情况下,在多线程环境下实现多变量的同步。即在没有线程阻塞的情况下实现同步。这样可以避免竞态、死锁等问题。
用户1212940
2022/04/13
4580
lock free(无锁并发)是什么
无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。
全栈程序员站长
2022/09/09
1.7K0
无锁编程技术及实现「建议收藏」
多线程编程是多CPU系统在中应用最广泛的一种编程方式,在传统的多线程编程中,多线程之间一般用各种锁的机制来保证正确的对共享资源(share resources)进行访问和操作。
全栈程序员站长
2022/06/28
1.2K0
2.什么是JAVA内存模型?
小陈:老王,看了上一篇的《CPU多级缓存模型》,有个疑问为什么还要有JAVA内存模型啊?
终有救赎
2023/10/16
2860
2.什么是JAVA内存模型?
boost lockfree queue-boost无锁队列「建议收藏」
LockFree的基础知识涉及:Atomic(原子操作),CAS(Compay and swap),内存预分配;
全栈程序员站长
2022/09/09
1.3K0
深入理解并发编程艺术之内存模型
随着硬件技术的飞速发展,多核处理器已经成为计算设备的标配,这使得开发人员需要掌握并发编程的知识和技巧,以充分发挥多核处理器的潜力。然而并发编程并非易事,它涉及到许多复杂的概念和原理。为了更好地理解并发编程的内在机制,需要深入研究内存模型及其在并发编程中的应用。本文将主要以 Java 内存模型来探讨并发编程中 BUG 的源头和处理这些问题的底层实现原理,助你更好地把握并发编程的内在机制。
腾讯技术工程官方号
2023/07/26
3290
深入理解并发编程艺术之内存模型
Java多线程内存模型(JMM)
CPU在摩尔定律的指导下以每18个月起一番的速度在发展,然而内存和硬盘的发展速度远远不及CPU。这就造成了高性能能的内存和硬盘价格及其昂贵。然而CPU的高度运算需要高速的数据。
chenchenchen
2022/01/05
4010
Java多线程内存模型(JMM)
一文详解JMM(Java 内存模型)
要想要理解透彻JMM(Java内存模型),首先我们要从CPU缓存模型和指令重排序讲起!
会呼吸的Coder
2022/12/02
1.1K0
一文详解JMM(Java 内存模型)
Java 内存模型详解
Java的内存模型(Java Memory Model )简称JMM。首先应该明白,Java内存模型是一个规范,主要规定了以下两点:
乱敲代码
2019/07/31
6850
彻底理解Java并发:Java内存模型
进程是静态的概念,进程是资源(CPU、内存等)分配和调度的基本单位,它拥有自己的资源空间,每启动一个进程,系统就会为它分配地址空间;
栗筝i
2022/12/01
3700
彻底理解Java并发:Java内存模型
synchronized
以下内容来自马士兵老师的B站教学视频:https://www.bilibili.com/video/BV1tz411q7c2
hhss
2021/02/12
5290
synchronized
无锁编程实例
有的CPU指令都支持CAS的原子操作,X86下对应的是 CMPXCHG 汇编指令。 大家应该还记得操作系统里面关于“原子操作”的概念,一个操作是原子的(atomic),如果这个操作所处的层(layer)的更高层不能发现其内部实现与结构。原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序是不可以被打乱,或者切割掉只执行部分。有了这个原子操作这个保证我们就可以实现无锁了。 CAS原子操作在维基百科中的代码描述如下: 1: int compare_and_swap(int* reg, int oldval, int newval) 2: { 3: ATOMIC(); 4: int old_reg_val = *reg; 5: if (old_reg_val == oldval) 6: *reg = newval; 7: END_ATOMIC(); 8: return old_reg_val; 9: }
全栈程序员站长
2022/09/06
8720
无锁编程实例
【Java】【并发编程】详解Java内存模型
Java内存模型即 Java Menory Model,简称JMM。JMM定义了Java虚拟机(JVM)在计算机内存(RAM)中的工作方法。JVM是整个计算机虚拟模型,所以JMM隶属于JVM的。
玖柒的小窝
2021/09/24
2.1K0
【Java】【并发编程】详解Java内存模型
JAVA系列之内存模型(JMM)
Java内存模型是在硬件内存模型上的更高层的抽象,它屏蔽了各种硬件和操作系统访问的差异性,保证了Java程序在各种平台下对内存的访问都能达到一致的效果。 Java内存模型是不可见的,它并不是一个真实的东西,它只是一个概念、一个规范。
夕阳也是醉了
2023/10/16
2310
JAVA系列之内存模型(JMM)
面试官:什么是Java内存模型?
当问到 Java 内存模型的时候,一定要注意,Java 内存模型(Java Memory Model,JMM)它和 JVM 内存布局(JVM 运行时数据区域)是不一样的,它们是两个完全不同的概念。
磊哥
2024/02/22
5650
相关推荐
深入理解无锁编程「建议收藏」
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验