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

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


最近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;

image.gif

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

y=0
x=getStatus()
if (x>0)
    y = x;

image.gif

当然这样的执行顺序重排都有一项重要的原则,就是不会影响单线程环境下程序的执行结果,但是在多线程并发的情况下,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; 
    } 
}

image.gif

这里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;
}

image.gif

可以看到这里无锁的概念其实就是在测试与共享变量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);

image.gif

Rust中锁的用法如下:

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

image.gif

       

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

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

相关文章
|
21天前
|
算法 Linux
深入探索Linux内核的内存管理机制
本文旨在为读者提供对Linux操作系统内核中内存管理机制的深入理解。通过探讨Linux内核如何高效地分配、回收和优化内存资源,我们揭示了这一复杂系统背后的原理及其对系统性能的影响。不同于常规的摘要,本文将直接进入主题,不包含背景信息或研究目的等标准部分,而是专注于技术细节和实际操作。
|
21天前
|
存储 缓存 网络协议
Linux操作系统的内核优化与性能调优####
本文深入探讨了Linux操作系统内核的优化策略与性能调优方法,旨在为系统管理员和高级用户提供一套实用的指南。通过分析内核参数调整、文件系统选择、内存管理及网络配置等关键方面,本文揭示了如何有效提升Linux系统的稳定性和运行效率。不同于常规摘要仅概述内容的做法,本摘要直接指出文章的核心价值——提供具体可行的优化措施,助力读者实现系统性能的飞跃。 ####
|
22天前
|
监控 算法 Linux
Linux内核锁机制深度剖析与实践优化####
本文作为一篇技术性文章,深入探讨了Linux操作系统内核中锁机制的工作原理、类型及其在并发控制中的应用,旨在为开发者提供关于如何有效利用这些工具来提升系统性能和稳定性的见解。不同于常规摘要的概述性质,本文将直接通过具体案例分析,展示在不同场景下选择合适的锁策略对于解决竞争条件、死锁问题的重要性,以及如何根据实际需求调整锁的粒度以达到最佳效果,为读者呈现一份实用性强的实践指南。 ####
|
22天前
|
缓存 监控 网络协议
Linux操作系统的内核优化与实践####
本文旨在探讨Linux操作系统内核的优化策略与实际应用案例,深入分析内核参数调优、编译选项配置及实时性能监控的方法。通过具体实例讲解如何根据不同应用场景调整内核设置,以提升系统性能和稳定性,为系统管理员和技术爱好者提供实用的优化指南。 ####
|
24天前
|
负载均衡 算法 Linux
深入探索Linux内核调度机制:公平与效率的平衡####
本文旨在剖析Linux操作系统内核中的进程调度机制,特别是其如何通过CFS(完全公平调度器)算法实现多任务环境下资源分配的公平性与系统响应速度之间的微妙平衡。不同于传统摘要的概览性质,本文摘要将直接聚焦于CFS的核心原理、设计目标及面临的挑战,为读者揭开Linux高效调度的秘密。 ####
34 3
|
27天前
|
负载均衡 算法 Linux
深入探索Linux内核调度器:公平与效率的平衡####
本文通过剖析Linux内核调度器的工作机制,揭示了其在多任务处理环境中如何实现时间片轮转、优先级调整及完全公平调度算法(CFS),以达到既公平又高效地分配CPU资源的目标。通过对比FIFO和RR等传统调度策略,本文展示了Linux调度器如何在复杂的计算场景下优化性能,为系统设计师和开发者提供了宝贵的设计思路。 ####
40 6
|
26天前
|
消息中间件 安全 Linux
深入探索Linux操作系统的内核机制
本文旨在为读者提供一个关于Linux操作系统内核机制的全面解析。通过探讨Linux内核的设计哲学、核心组件、以及其如何高效地管理硬件资源和系统操作,本文揭示了Linux之所以成为众多开发者和组织首选操作系统的原因。不同于常规摘要,此处我们不涉及具体代码或技术细节,而是从宏观的角度审视Linux内核的架构和功能,为对Linux感兴趣的读者提供一个高层次的理解框架。
|
28天前
|
缓存 并行计算 Linux
深入解析Linux操作系统的内核优化策略
本文旨在探讨Linux操作系统内核的优化策略,包括内核参数调整、内存管理、CPU调度以及文件系统性能提升等方面。通过对这些关键领域的分析,我们可以理解如何有效地提高Linux系统的性能和稳定性,从而为用户提供更加流畅和高效的计算体验。
30 2
|
28天前
|
缓存 网络协议 Linux
深入探索Linux操作系统的内核优化策略####
本文旨在探讨Linux操作系统内核的优化方法,通过分析当前主流的几种内核优化技术,结合具体案例,阐述如何有效提升系统性能与稳定性。文章首先概述了Linux内核的基本结构,随后详细解析了内核优化的必要性及常用手段,包括编译优化、内核参数调整、内存管理优化等,最后通过实例展示了这些优化技巧在实际场景中的应用效果,为读者提供了一套实用的Linux内核优化指南。 ####
45 1
|
29天前
|
缓存 算法 Linux
Linux内核的心脏:深入理解进程调度器
本文探讨了Linux操作系统中至关重要的组成部分——进程调度器。通过分析其工作原理、调度算法以及在不同场景下的表现,揭示它是如何高效管理CPU资源,确保系统响应性和公平性的。本文旨在为读者提供一个清晰的视图,了解在多任务环境下,Linux是如何智能地分配处理器时间给各个进程的。

热门文章

最新文章