在vDSO内部panic时打印错误信息
make utest
可选的环境变量:
ARCH
riscv64
x86_64
aarch64
LOG
error
trace
debug
info
warn
目前的编译流程在so文件发生变化时(例如vdso内部代码修改或切换ARCH),第一次编译依然引用旧版的so文件,导致可能出现运行错误。在第二次编译时即可正常运行。
vDSO(Virtual Dynamic Shared Object,虚拟动态共享对象)是 Linux 内核提供的一种机制,用来高效地将某些内核服务暴露给用户空间,而无需进行用户态到内核态的系统调用(syscall)切换。
其原理为:在内核空间中加载一个二进制共享库文件(vDSO),以及这个共享库文件访问的数据区(vVAR)。之后,将vDSO和vVAR均映射到用户空间,使用户程序可直接访问vDSO中提供的接口,并间接访问vVAR的数据。通过该方式,即可实现代码和数据在用户态和内核态间、在不同用户程序间的共享。
vDSO
vVAR
为何需要单独的vVAR数据区,而不是使用vDSO中的数据段?
关于vDSO的更详细介绍参考RISC-V Syscall 系列 4:vDSO 实现原理分析。
前文已提及,vDSO技术可用于用户和内核之间、用户进程之间的代码与数据共享。其可应用于一些需要如上的代码数据共享的领域,例如共享任务调度、异步系统调用和进程间通信。本项目即使用vDSO机制实现上述的功能。
本仓库中的两个crate,vdso_helper和build_vdso,用于支持使用Rust语言方便地开发和构建vDSO共享库:
vdso_helper
build_vdso
通过它们实现的,vDSO开发和构建流程如图所示:
基于本项目开展了如下的工作:
vsched
vqueue
vipc
目前本项目的工作在AsyncOS上运行,未适配Linux。
no_std
lib
api
api.rs
vvar_data!
get_vvar_data!
mut_cfg!
use_mut_cfg!
#[unsafe(no_mangle)] pub extern "C" fn 函数名(参数) -> 返回值 { 函数体 }
注意:不是所有对外提供的函数都需要放入api.rs(例如对外提供某些类型关联的方法)。但是,如果该对外提供函数(直接或间接地)访问了共享数据,则必须放入api.rs中。
build.rs
BuildConfig
VvarData
config::PAGES_SIZE_4K
外部代码通过api库访问vDSO功能代码,而从vDSO功能代码到api库有两条路径:
从路径1提供的接口存在如下限制:
async
impl Future
alloc
从路径2提供的接口与路径1相比,可以提供类型和方法,可以包含泛型。但仍存在如下限制:
在vDSO的interface.rs中使用trait_interface宏定义一个trait,可以使外部代码实现这些trait,而让vDSO调用外部代码的实现。
interface.rs
trait_interface
trait
其实现原理为:vDSO会为这个trait在私有数据区创建一个虚函数表(放在私有数据区而非共享数据区,是为了考虑实现代码在不同的地址空间中被映射到了不同位置的情况),并在进程内初始化时初始化虚函数表。之后,就可通过虚函数表调用trait的函数。同时,外部代码可以以指针形式传递实现了这个trait的结构体类型,vDSO内部获得指针后,会将其转化为一个“虚拟实现”类型的引用,从而同样通过虚函数表调用trait的函数。
该机制可以使vDSO实现两种功能:
self
crate_interface
vDSO共享库构建工具
版权所有:中国计算机学会技术支持:开源发展技术委员会 京ICP备13000930号-9 京公网安备 11010802047560号
vDSO共享库构建工具
运行测试程序
可选的环境变量:
ARCH:默认riscv64,可选x86_64、aarch64、riscv64LOG:默认error,可选trace、debug、info、warn、error目前的编译流程在so文件发生变化时(例如vdso内部代码修改或切换
ARCH),第一次编译依然引用旧版的so文件,导致可能出现运行错误。在第二次编译时即可正常运行。vDSO简介
vDSO(Virtual Dynamic Shared Object,虚拟动态共享对象)是 Linux 内核提供的一种机制,用来高效地将某些内核服务暴露给用户空间,而无需进行用户态到内核态的系统调用(syscall)切换。
其原理为:在内核空间中加载一个二进制共享库文件(
vDSO),以及这个共享库文件访问的数据区(vVAR)。之后,将vDSO和vVAR均映射到用户空间,使用户程序可直接访问vDSO中提供的接口,并间接访问vVAR的数据。通过该方式,即可实现代码和数据在用户态和内核态间、在不同用户程序间的共享。为何需要单独的
vVAR数据区,而不是使用vDSO中的数据段?vDSO被整体映射到一个只读区域。因此vDSO的数据段只能存储只读数据,而可变数据需要存储在vVAR中。vDSO功能的实现中,分别加载vDSO的代码段和数据段,并赋予相应的读写权限。但有两种可变数据需要区分:(1)共享的可变数据,在一个地址空间中的修改可以反映到其它地址空间中;(2)私有的可变数据,每个地址空间持有不同的拷贝,修改互不影响。因此我们使用vVAR存储共享的可变数据,而使用vDSO中的数据段存储私有的可变数据。关于vDSO的更详细介绍参考RISC-V Syscall 系列 4:vDSO 实现原理分析。
本项目内容
前文已提及,vDSO技术可用于用户和内核之间、用户进程之间的代码与数据共享。其可应用于一些需要如上的代码数据共享的领域,例如共享任务调度、异步系统调用和进程间通信。本项目即使用vDSO机制实现上述的功能。
本仓库中的两个crate,
vdso_helper和build_vdso,用于支持使用Rust语言方便地开发和构建vDSO共享库:vdso_helper:封装了vDSO代码所需的操作,包括(1)访问vVAR数据和(2)定义可由环境变量修改的常量。build_vdso:将vDSO代码的构建流程整合到vDSO外部代码的构建流程中,并构建可被vDSO外部代码依赖的API库。通过它们实现的,
vDSO开发和构建流程如图所示:基于本项目开展了如下的工作:
vsched:基于vDSO机制的调度器,后续将开发为用户态、内核态的统一调度器。vqueue:基于vDSO机制的IPC队列,由vipc使用。目前本项目的工作在AsyncOS上运行,未适配Linux。
vDSO共享库开发和使用流程
开发
vDSO库no_std、lib类型的Rust crate,并创建一个模块api和源文件api.rs。vdso_helper,使用其中的vvar_data!和get_vvar_data!定义和访问共享数据。vdso_helper中的mut_cfg!和use_mut_cfg!定义在编译期由环境变量指定的常量。api.rs中,且使用以下的函数定义:注意:不是所有对外提供的函数都需要放入
api.rs(例如对外提供某些类型关联的方法)。但是,如果该对外提供函数(直接或间接地)访问了共享数据,则必须放入api.rs中。构建和使用
vDSO库vDSO外部代码的build.rs中使用build_vdso,配置BuildConfig构建参数,并传入build_vdso函数以构建vDSO库。vDSO和vVAR:在外部代码所在的地址空间中映射一块区域,并如此设置:首先保留一块VvarData大小的区域,设置为可读可写。在其之后的下一页加载第2步中的so文件,并为各个段设置合适的可读/可写/可执行权限。vVAR区域与vDSO区域的基址都需要对齐到config::PAGES_SIZE_4K。vDSO的加载基址。vDSO的API。vDSO和vVAR映射到其地址空间,并向用户进程传递vDSO的基址。用户进程即可通过第4、5步的方式使用vDSO。vDSO接口的说明与限制
外部代码通过api库访问vDSO功能代码,而从vDSO功能代码到api库有两条路径:
api.rs中声明的接口函数使用了该路径。api.rs之外的其它位置中声明的接口使用了该路径。从路径1提供的接口存在如下限制:
async声明的函数,因为它实际上是返回impl Future的泛型函数的语法糖。alloccrate中提供的类型或方法。因为其需要被单独编译为so文件,所以无法从它的调用者中获得堆分配器。从路径2提供的接口与路径1相比,可以提供类型和方法,可以包含泛型。但仍存在如下限制:
使vDSO依赖外部代码
在vDSO的
interface.rs中使用trait_interface宏定义一个trait,可以使外部代码实现这些trait,而让vDSO调用外部代码的实现。其实现原理为:vDSO会为这个
trait在私有数据区创建一个虚函数表(放在私有数据区而非共享数据区,是为了考虑实现代码在不同的地址空间中被映射到了不同位置的情况),并在进程内初始化时初始化虚函数表。之后,就可通过虚函数表调用trait的函数。同时,外部代码可以以指针形式传递实现了这个trait的结构体类型,vDSO内部获得指针后,会将其转化为一个“虚拟实现”类型的引用,从而同样通过虚函数表调用trait的函数。该机制可以使vDSO实现两种功能:
trait中定义的方法都不包含self的引用,则这样的依赖接口类似于crate_interface库提供的功能,使上游(vDSO内部)可以依赖下游(vDSO外部)的函数。trait中定义的方法包含self的引用,则其在某种程度上为vDSO提供了处理泛型的功能,使一些在vDSO内部操作的类型可以在vDSO外部定义与实现,也增加了vDSO的可移植性(例如,一个基于vDSO的任务调度器就可以兼容不同系统中定义的任务类型)。不过,这样的泛型支持也是有限制的:每个地址空间中只能提供一种实现。改进方向