用rust语言开发linux内核,得先过内存模型这关(代码片段)

beyondma beyondma     2023-01-15     657

关键词:

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

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

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

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

x=getStatus()

if (x>0)

    y = x;

else

y = 0;

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

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。)

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

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的实现简要说明如下:

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语言中锁的典型用法如下:

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中锁的用法如下:

let _guard = data.lock();

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

        

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

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

rust学习内存安全探秘:变量的所有权引用与借用

作者:京东零售周凯一.前言Rust语言由Mozilla开发,最早发布于2014年9月,是一种高效、可靠的通用高级语言。其高效不仅限于开发效率,它的执行效率也是令人称赞的,是一种少有的兼顾开发效率和执行效率的语言。Rust语言具备... 查看详情

新增3.2万行代码,linux内核有望在2022年正式支持rust

...c;Linux的确无处不在地运行着。在Linux操作系统之上,C语言一直是Linux的主导语言,然而俗话说的好,十年河东,十年河西,在编程语言战场,Rust正在悄然兴起,并深受一线大厂们的青睐。那Rust为何会... 查看详情

用rust开发linux,可行吗?(代码片段)

...原生领域已经稳定占据了C位,那么让Rust更进一步去开发操作系统的内核,就成为很多Rust粉丝心中的终极梦想,而Rust官方也一直有想法使Rust语 查看详情

rust所有权语义模型

...释Rust所有权的概念,以便帮助降低Rust的学习曲线。编程语言的内存管理,大概可以分为自动和手动两种。自动管理就是用GC(垃圾回收)来自动管理内存,像Java、Ruby、Golang、Elixir等语言都依赖于GC。而C/C++却是依赖于手工管理... 查看详情

flink内核原理学习内存模型(代码片段)

... 目前,大数据计算引擎主要用Java或是基于JVM的编程语言实现的, 查看详情

geek新鲜事-初版的rust支持已合并到linux内核

...生,但就在本周末,仍有一些Linux用户对Rust编程语言支持Linux内核的想法持不同意见。不过现在它已经落地了。在合并之前,来自谷歌的Linu 查看详情

geek新鲜事-初版的rust支持已合并到linux内核

...生,但就在本周末,仍有一些Linux用户对Rust编程语言支持Linux内核的想法持不同意见。不过现在它已经落地了。在合并之前,来自谷歌的Linu 查看详情

geek新鲜事-初版的rust支持已合并到linux内核

...生,但就在本周末,仍有一些Linux用户对Rust编程语言支持Linux内核的想法持不同意见。不过现在它已经落地了。在合并之前,来自谷歌的Linux内核工程师KeesCook在PR中写道,希望Linus能在Linux6.1-rc1中合并对Rust的初始... 查看详情

在linux新版内核中的rust初探,原来是这样的!

...,下一个版本的Linux内核主线,可能就会合并Rust语言提交的PR分支。然而,在五天前有开发者询问Linus是否在 Lin 查看详情

rust入坑指南:坑主驾到(代码片段)

...下面我先跳了,各位请随意。Rust简介众所周知,在编程语言中,更易读的高级语言和控制底层资源的低级语言是一对矛盾体。Rust想要挑战这一现状,它尝试为开发者提供更好的体验的同时给予开发者控制底层细节的权限(比如... 查看详情

什么是linux下的c语言驱动开发?

...供驱动,也需要给LINUX/苹果MacOS提供驱动,那么就需要用C语言写驱动程序了。linux内核重要组成部分之一,就是硬件驱动。 查看详情

linux内核开发与linux驱动开发有啥关系?

...如下图所示:多说一点,要进行linux驱动开发,必须学好C语言、能够看懂电路图(因为驱动开发需要根据相应的引脚来编写驱动)需要模电和数电知识,linux操作系统知识,linux应用编程知识(多进程、多线程、文件io操作)因为... 查看详情

如何参与linux内核开发

...将试图解释内核社区为何这样运作。Linux内核大部分是由C语言写成的,一些体系结构相关的代码用到了汇编语言。要参与内核开发,你必须精通C语言。除非你想为某个架构开发底层代码,否则你并不需要了解(任何体系结构的... 查看详情

rust能否替代c语言,主宰linux的世界?

现有的编程语言非常多,我们都习惯了要在性能、表达力和内存安全之间取舍,直到Rust横空出世。 对于Rust这个新语言,很多人可能都听过,但是没用过。实际上,早从16年起,Rust已经连续六年霸榜,... 查看详情

rust能否替代c语言,主宰linux的世界?

现有的编程语言非常多,我们都习惯了要在性能、表达力和内存安全之间取舍,直到Rust横空出世。 对于Rust这个新语言,很多人可能都听过,但是没用过。实际上,早从16年起,Rust已经连续六年霸榜,... 查看详情

linux内核加速支持rust开发;中科院计划每半年升级一次risc-v芯片;python3.10.1发布|开源日报

...开发2021年中旬,我们发现Linux内核开始引入对Rust编程语言的支持,“RustforLinux”这一项目也应运而生,MiguelOjeda便是参与该项目的首席开发者之一。12月6日,MiguelOjeda 查看详情

内核解读之内存管理内存模型(代码片段)

文章目录1、基本术语2、FLATMEM(平坦内存模型)3、SPARSEMEM稀疏内存模型1、基本术语在介绍内存模型之前需要了解一些基本的知识。1、什么是pageframe?在linux操作系统中,物理内存被分成一页页的pageframe来管理ÿ... 查看详情

内核解读之内存管理内存模型(代码片段)

文章目录基本的术语CONFIG_FLATMEM(平坦内存模型)稀疏的内存模型基本的术语在介绍内存模型之前需要了解一些基本的知识。1、什么是pageframe?在linux操作系统中,物理内存被分成一页页的pageframe来管理,具体... 查看详情