mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-23 16:23:24 +00:00
新的内存管理模块 (#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:
288
kernel/src/process/exec.rs
Normal file
288
kernel/src/process/exec.rs
Normal 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(());
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user