新的内存管理模块 (#301)

  实现了具有优秀架构设计的新的内存管理模块,对内核空间和用户空间的内存映射、分配、释放、管理等操作进行了封装,使得内核开发者可以更加方便地进行内存管理。

  内存管理模块主要由以下类型的组件组成:

- **硬件抽象层(MemoryManagementArch)** - 提供对具体处理器架构的抽象,使得内存管理模块可以在不同的处理器架构上运行
- **页面映射器(PageMapper)**- 提供对虚拟地址和物理地址的映射,以及页表的创建、填写、销毁、权限管理等操作。分为两种类型:内核页表映射器(KernelMapper)和用户页表映射器(位于具体的用户地址空间结构中)
- **页面刷新器(PageFlusher)** - 提供对页表的刷新操作(整表刷新、单页刷新、跨核心刷新)
- **页帧分配器(FrameAllocator)** - 提供对页帧的分配、释放、管理等操作。具体来说,包括BumpAllocator、BuddyAllocator
- **小对象分配器** - 提供对小内存对象的分配、释放、管理等操作。指的是内核里面的SlabAllocator (SlabAllocator的实现目前还没有完成)
- **MMIO空间管理器** - 提供对MMIO地址空间的分配、管理操作。(目前这个模块待进一步重构)
- **用户地址空间管理机制** - 提供对用户地址空间的管理。
    - VMA机制 - 提供对用户地址空间的管理,包括VMA的创建、销毁、权限管理等操作
    - 用户映射管理 - 与VMA机制共同作用,管理用户地址空间的映射
- **系统调用层** - 提供对用户空间的内存管理系统调用,包括mmap、munmap、mprotect、mremap等
- **C接口兼容层** - 提供对原有的C代码的接口,是的C代码能够正常运行。


除上面的新增内容以外,其它的更改内容:
- 新增二进制加载器,以及elf的解析器
- 解决由于local_irq_save、local_irq_restore函数的汇编不规范导致影响栈行为的bug。
- 解决local_irq_save未关中断的错误。
- 修复sys_gettimeofday对timezone参数的处理的bug
This commit is contained in:
LoGin
2023-07-22 16:22:17 +08:00
committed by GitHub
parent 0663027b11
commit d8ad0a5e77
124 changed files with 8277 additions and 5150 deletions

288
kernel/src/process/exec.rs Normal file
View File

@ -0,0 +1,288 @@
use core::{fmt::Debug, ptr::null};
use alloc::{collections::BTreeMap, string::String, sync::Arc, vec::Vec};
use crate::{
filesystem::vfs::{
file::{File, FileMode},
ROOT_INODE,
},
io::SeekFrom,
libs::elf::ELF_LOADER,
mm::{
ucontext::{AddressSpace, UserStack},
VirtAddr,
},
syscall::SystemError,
};
/// 系统支持的所有二进制文件加载器的列表
const BINARY_LOADERS: [&'static dyn BinaryLoader; 1] = [&ELF_LOADER];
pub trait BinaryLoader: 'static + Debug {
/// 检查二进制文件是否为当前加载器支持的格式
fn probe(self: &'static Self, param: &ExecParam, buf: &[u8]) -> Result<(), ExecError>;
fn load(
self: &'static Self,
param: &mut ExecParam,
head_buf: &[u8],
) -> Result<BinaryLoaderResult, ExecError>;
}
/// 二进制文件加载结果
#[derive(Debug)]
pub struct BinaryLoaderResult {
/// 程序入口地址
entry_point: VirtAddr,
}
impl BinaryLoaderResult {
pub fn new(entry_point: VirtAddr) -> Self {
Self { entry_point }
}
pub fn entry_point(&self) -> VirtAddr {
self.entry_point
}
}
#[allow(dead_code)]
#[derive(Debug)]
pub enum ExecError {
/// 二进制文件不可执行
NotExecutable,
/// 二进制文件不是当前架构的
WrongArchitecture,
/// 访问权限不足
PermissionDenied,
/// 不支持的操作
NotSupported,
/// 解析文件本身的时候出现错误(比如一些字段本身不合法)
ParseError,
/// 内存不足
OutOfMemory,
/// 参数错误
InvalidParemeter,
/// 无效的地址
BadAddress(Option<VirtAddr>),
Other(String),
}
impl Into<SystemError> for ExecError {
fn into(self) -> SystemError {
match self {
ExecError::NotExecutable => SystemError::ENOEXEC,
ExecError::WrongArchitecture => SystemError::EOPNOTSUPP_OR_ENOTSUP,
ExecError::PermissionDenied => SystemError::EACCES,
ExecError::NotSupported => SystemError::EOPNOTSUPP_OR_ENOTSUP,
ExecError::ParseError => SystemError::ENOEXEC,
ExecError::OutOfMemory => SystemError::ENOMEM,
ExecError::InvalidParemeter => SystemError::EINVAL,
ExecError::BadAddress(_addr) => SystemError::EFAULT,
ExecError::Other(_msg) => SystemError::ENOEXEC,
}
}
}
bitflags! {
pub struct ExecParamFlags: u32 {
// 是否以可执行文件的形式加载
const EXEC = 1 << 0;
}
}
#[derive(Debug)]
pub struct ExecParam<'a> {
file_path: &'a str,
file: Option<File>,
vm: Arc<AddressSpace>,
/// 一些标志位
flags: ExecParamFlags,
/// 用来初始化进程的一些信息。这些信息由二进制加载器和exec机制来共同填充
init_info: ProcInitInfo,
}
#[derive(Debug, Eq, PartialEq)]
pub enum ExecLoadMode {
/// 以可执行文件的形式加载
Exec,
/// 以动态链接库的形式加载
DSO,
}
#[allow(dead_code)]
impl<'a> ExecParam<'a> {
pub fn new(file_path: &'a str, vm: Arc<AddressSpace>, flags: ExecParamFlags) -> Self {
Self {
file_path,
file: None,
vm,
flags,
init_info: ProcInitInfo::new(),
}
}
pub fn file_path(&self) -> &'a str {
self.file_path
}
pub fn vm(&self) -> &Arc<AddressSpace> {
&self.vm
}
pub fn flags(&self) -> &ExecParamFlags {
&self.flags
}
pub fn init_info(&self) -> &ProcInitInfo {
&self.init_info
}
pub fn init_info_mut(&mut self) -> &mut ProcInitInfo {
&mut self.init_info
}
/// 获取加载模式
pub fn load_mode(&self) -> ExecLoadMode {
if self.flags.contains(ExecParamFlags::EXEC) {
ExecLoadMode::Exec
} else {
ExecLoadMode::DSO
}
}
pub fn file_mut(&mut self) -> &mut File {
self.file.as_mut().unwrap()
}
}
/// ## 加载二进制文件
pub fn load_binary_file(param: &mut ExecParam) -> Result<BinaryLoaderResult, SystemError> {
let inode = ROOT_INODE().lookup(param.file_path)?;
// 读取文件头部,用于判断文件类型
let file = File::new(inode, FileMode::O_RDONLY)?;
param.file = Some(file);
let mut head_buf = [0u8; 512];
param.file_mut().lseek(SeekFrom::SeekSet(0))?;
let _bytes = param.file_mut().read(512, &mut head_buf)?;
// kdebug!("load_binary_file: read {} bytes", _bytes);
let mut loader = None;
for bl in BINARY_LOADERS.iter() {
let probe_result = bl.probe(param, &head_buf);
if probe_result.is_ok() {
loader = Some(bl);
break;
}
}
// kdebug!("load_binary_file: loader: {:?}", loader);
if loader.is_none() {
return Err(SystemError::ENOEXEC);
}
let loader: &&dyn BinaryLoader = loader.unwrap();
assert!(param.vm().is_current());
// kdebug!("load_binary_file: to load with param: {:?}", param);
let result: BinaryLoaderResult = loader
.load(param, &head_buf)
.unwrap_or_else(|e| panic!("load_binary_file failed: error: {e:?}, param: {param:?}"));
// kdebug!("load_binary_file: load success");
return Ok(result);
}
/// 程序初始化信息,这些信息会被压入用户栈中
#[derive(Debug)]
pub struct ProcInitInfo {
pub args: Vec<String>,
pub envs: Vec<String>,
pub auxv: BTreeMap<u8, usize>,
}
impl ProcInitInfo {
pub fn new() -> Self {
Self {
args: Vec::new(),
envs: Vec::new(),
auxv: BTreeMap::new(),
}
}
/// 把程序初始化信息压入用户栈中
/// 这个函数会把参数、环境变量、auxv等信息压入用户栈中
///
/// ## 返回值
///
/// 返回值是一个元组第一个元素是最终的用户栈顶地址第二个元素是环境变量pointer数组的起始地址
pub unsafe fn push_at(
&self,
ustack: &mut UserStack,
) -> Result<(VirtAddr, VirtAddr), SystemError> {
// 先把程序的名称压入栈中
self.push_str(ustack, self.args[0].as_str())?;
// 然后把环境变量压入栈中
let envps = self
.envs
.iter()
.map(|s| {
self.push_str(ustack, s.as_str()).expect("push_str failed");
ustack.sp()
})
.collect::<Vec<_>>();
// 然后把参数压入栈中
let argps = self
.args
.iter()
.map(|s| {
self.push_str(ustack, s.as_str()).expect("push_str failed");
ustack.sp()
})
.collect::<Vec<_>>();
// 压入auxv
self.push_slice(ustack, &[null::<u8>(), null::<u8>()])?;
for (&k, &v) in self.auxv.iter() {
self.push_slice(ustack, &[k as usize, v])?;
}
// 把环境变量指针压入栈中
self.push_slice(ustack, &[null::<u8>()])?;
self.push_slice(ustack, envps.as_slice())?;
// 把参数指针压入栈中
self.push_slice(ustack, &[null::<u8>()])?;
self.push_slice(ustack, argps.as_slice())?;
let argv_ptr = ustack.sp();
// 把argc压入栈中
self.push_slice(ustack, &[self.args.len()])?;
return Ok((ustack.sp(), argv_ptr));
}
fn push_slice<T: Copy>(&self, ustack: &mut UserStack, slice: &[T]) -> Result<(), SystemError> {
let mut sp = ustack.sp();
sp -= slice.len() * core::mem::size_of::<T>();
sp -= sp.data() % core::mem::align_of::<T>();
unsafe { core::slice::from_raw_parts_mut(sp.data() as *mut T, slice.len()) }
.copy_from_slice(slice);
unsafe {
ustack.set_sp(sp);
}
return Ok(());
}
fn push_str(&self, ustack: &mut UserStack, s: &str) -> Result<(), SystemError> {
self.push_slice(ustack, &[b'\0'])?;
self.push_slice(ustack, s.as_bytes())?;
return Ok(());
}
}