mirror of
https://github.com/rcore-os/rCore.git
synced 2024-11-23 08:26:17 +04:00
Fork (not finished). Not depend on boot_info after remap_kernel.
This commit is contained in:
parent
678d1d2b54
commit
4d6925a562
@ -24,6 +24,7 @@ once = "0.3.3"
|
|||||||
linked_list_allocator = "0.5.0"
|
linked_list_allocator = "0.5.0"
|
||||||
redox_syscall = "0.1.37"
|
redox_syscall = "0.1.37"
|
||||||
xmas-elf = "0.6"
|
xmas-elf = "0.6"
|
||||||
|
arrayvec = { version = "0.4.7", default-features = false }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cc = "1.0"
|
cc = "1.0"
|
||||||
|
@ -26,7 +26,7 @@ pub const MAX_CPU_NUM: usize = 8;
|
|||||||
pub const KERNEL_HEAP_OFFSET: usize = KERNEL_OFFSET - PML4_SIZE;
|
pub const KERNEL_HEAP_OFFSET: usize = KERNEL_OFFSET - PML4_SIZE;
|
||||||
pub const KERNEL_HEAP_PML4: usize = (KERNEL_HEAP_OFFSET & PML4_MASK)/PML4_SIZE;
|
pub const KERNEL_HEAP_PML4: usize = (KERNEL_HEAP_OFFSET & PML4_MASK)/PML4_SIZE;
|
||||||
/// Size of kernel heap
|
/// Size of kernel heap
|
||||||
pub const KERNEL_HEAP_SIZE: usize = 1 * 1024 * 1024; // 1 MB
|
pub const KERNEL_HEAP_SIZE: usize = 2 * 1024 * 1024; // 1 MB
|
||||||
|
|
||||||
/// Offset to kernel percpu variables
|
/// Offset to kernel percpu variables
|
||||||
//TODO: Use 64-bit fs offset to enable this pub const KERNEL_PERCPU_OFFSET: usize = KERNEL_HEAP_OFFSET - PML4_SIZE;
|
//TODO: Use 64-bit fs offset to enable this pub const KERNEL_PERCPU_OFFSET: usize = KERNEL_HEAP_OFFSET - PML4_SIZE;
|
||||||
|
@ -33,6 +33,7 @@ extern crate lazy_static;
|
|||||||
extern crate bit_field;
|
extern crate bit_field;
|
||||||
extern crate syscall as redox_syscall;
|
extern crate syscall as redox_syscall;
|
||||||
extern crate xmas_elf;
|
extern crate xmas_elf;
|
||||||
|
extern crate arrayvec;
|
||||||
|
|
||||||
#[macro_use] // print!
|
#[macro_use] // print!
|
||||||
mod io;
|
mod io;
|
||||||
@ -76,7 +77,7 @@ pub extern "C" fn rust_main(multiboot_information_address: usize) -> ! {
|
|||||||
|
|
||||||
// FIXME: 开启SMP后,导致switch_to_user中设置rsp无效
|
// FIXME: 开启SMP后,导致switch_to_user中设置rsp无效
|
||||||
// arch::smp::start_other_cores(&acpi, &mut memory_controller);
|
// arch::smp::start_other_cores(&acpi, &mut memory_controller);
|
||||||
process::init(&mut memory_controller);
|
process::init(memory_controller);
|
||||||
|
|
||||||
unsafe{ arch::interrupt::enable(); }
|
unsafe{ arch::interrupt::enable(); }
|
||||||
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
use memory::{Frame, FrameAllocator, PhysAddr};
|
use memory::{Frame, FrameAllocator, PhysAddr};
|
||||||
use multiboot2::{MemoryAreaIter, MemoryArea};
|
use multiboot2::{MemoryAreaIter, MemoryArea};
|
||||||
|
use arrayvec::ArrayVec;
|
||||||
|
|
||||||
pub struct AreaFrameAllocator {
|
pub struct AreaFrameAllocator {
|
||||||
next_free_frame: Frame,
|
next_free_frame: Frame,
|
||||||
current_area: Option<&'static MemoryArea>,
|
current_area: Option<Area>,
|
||||||
areas: MemoryAreaIter,
|
areas: ArrayVec<[Area; 4]>,
|
||||||
kernel_start: Frame,
|
kernel_start: Frame,
|
||||||
kernel_end: Frame,
|
kernel_end: Frame,
|
||||||
multiboot_start: Frame,
|
multiboot_start: Frame,
|
||||||
@ -62,10 +63,12 @@ impl AreaFrameAllocator {
|
|||||||
multiboot_start: PhysAddr, multiboot_end: PhysAddr,
|
multiboot_start: PhysAddr, multiboot_end: PhysAddr,
|
||||||
memory_areas: MemoryAreaIter) -> AreaFrameAllocator
|
memory_areas: MemoryAreaIter) -> AreaFrameAllocator
|
||||||
{
|
{
|
||||||
|
let areas: ArrayVec<[Area; 4]> = memory_areas.map(|a| Area::from(a)).collect();
|
||||||
|
|
||||||
let mut allocator = AreaFrameAllocator {
|
let mut allocator = AreaFrameAllocator {
|
||||||
next_free_frame: Frame::of_addr(0),
|
next_free_frame: Frame::of_addr(0),
|
||||||
current_area: None,
|
current_area: None,
|
||||||
areas: memory_areas,
|
areas,
|
||||||
kernel_start: Frame::of_addr(kernel_start.0 as usize),
|
kernel_start: Frame::of_addr(kernel_start.0 as usize),
|
||||||
kernel_end: Frame::of_addr(kernel_end.0 as usize),
|
kernel_end: Frame::of_addr(kernel_end.0 as usize),
|
||||||
multiboot_start: Frame::of_addr(multiboot_start.0 as usize),
|
multiboot_start: Frame::of_addr(multiboot_start.0 as usize),
|
||||||
@ -76,10 +79,11 @@ impl AreaFrameAllocator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn choose_next_area(&mut self) {
|
fn choose_next_area(&mut self) {
|
||||||
self.current_area = self.areas.clone().filter(|area| {
|
self.current_area = self.areas.iter().filter(|area| {
|
||||||
let address = area.end_address() - 1;
|
let address = area.end_address() - 1;
|
||||||
Frame::of_addr(address as usize) >= self.next_free_frame
|
Frame::of_addr(address as usize) >= self.next_free_frame
|
||||||
}).min_by_key(|area| area.start_address());
|
}).min_by_key(|area| area.start_address())
|
||||||
|
.map(|area| area.clone());
|
||||||
|
|
||||||
if let Some(area) = self.current_area {
|
if let Some(area) = self.current_area {
|
||||||
let start_frame = Frame::of_addr(area.start_address());
|
let start_frame = Frame::of_addr(area.start_address());
|
||||||
@ -89,3 +93,30 @@ impl AreaFrameAllocator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Area {
|
||||||
|
start: usize,
|
||||||
|
end: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Area {
|
||||||
|
pub fn start_address(&self) -> usize {
|
||||||
|
self.start
|
||||||
|
}
|
||||||
|
pub fn end_address(&self) -> usize {
|
||||||
|
self.end
|
||||||
|
}
|
||||||
|
pub fn size(&self) -> usize {
|
||||||
|
self.end - self.start
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a MemoryArea> for Area {
|
||||||
|
fn from(a: &'a MemoryArea) -> Self {
|
||||||
|
Area {
|
||||||
|
start: a.start_address(),
|
||||||
|
end: a.end_address(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -44,6 +44,14 @@ impl MemoryArea {
|
|||||||
name,
|
name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub unsafe fn as_slice(&self) -> &[u8] {
|
||||||
|
use core::slice;
|
||||||
|
slice::from_raw_parts(self.start_addr as *const u8, self.end_addr - self.start_addr)
|
||||||
|
}
|
||||||
|
pub unsafe fn as_slice_mut(&self) -> &mut [u8] {
|
||||||
|
use core::slice;
|
||||||
|
slice::from_raw_parts_mut(self.start_addr as *mut u8, self.end_addr - self.start_addr)
|
||||||
|
}
|
||||||
pub fn contains(&self, addr: VirtAddr) -> bool {
|
pub fn contains(&self, addr: VirtAddr) -> bool {
|
||||||
addr >= self.start_addr && addr < self.end_addr
|
addr >= self.start_addr && addr < self.end_addr
|
||||||
}
|
}
|
||||||
@ -58,16 +66,17 @@ impl MemoryArea {
|
|||||||
|
|
||||||
/// 内存空间集合,包含若干段连续空间
|
/// 内存空间集合,包含若干段连续空间
|
||||||
/// 对应ucore中 `mm_struct`
|
/// 对应ucore中 `mm_struct`
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct MemorySet {
|
pub struct MemorySet {
|
||||||
areas: Vec<MemoryArea>,
|
areas: Vec<MemoryArea>,
|
||||||
page_table: Option<InactivePageTable>,
|
// page_table: Option<InactivePageTable>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MemorySet {
|
impl MemorySet {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
MemorySet {
|
MemorySet {
|
||||||
areas: Vec::<MemoryArea>::new(),
|
areas: Vec::<MemoryArea>::new(),
|
||||||
page_table: None,
|
// page_table: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Used for remap_kernel() where heap alloc is unavailable
|
/// Used for remap_kernel() where heap alloc is unavailable
|
||||||
@ -76,7 +85,7 @@ impl MemorySet {
|
|||||||
let cap = slice.len() / size_of::<MemoryArea>();
|
let cap = slice.len() / size_of::<MemoryArea>();
|
||||||
MemorySet {
|
MemorySet {
|
||||||
areas: Vec::<MemoryArea>::from_raw_parts(slice.as_ptr() as *mut MemoryArea, 0, cap),
|
areas: Vec::<MemoryArea>::from_raw_parts(slice.as_ptr() as *mut MemoryArea, 0, cap),
|
||||||
page_table: None,
|
// page_table: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn find_area(&self, addr: VirtAddr) -> Option<&MemoryArea> {
|
pub fn find_area(&self, addr: VirtAddr) -> Option<&MemoryArea> {
|
||||||
@ -88,8 +97,8 @@ impl MemorySet {
|
|||||||
.is_none(), "memory area overlap");
|
.is_none(), "memory area overlap");
|
||||||
self.areas.push(area);
|
self.areas.push(area);
|
||||||
}
|
}
|
||||||
pub fn map(&mut self, pt: &mut Mapper) {
|
pub fn map(&self, pt: &mut Mapper) {
|
||||||
for area in self.areas.iter_mut() {
|
for area in self.areas.iter() {
|
||||||
match area.phys_start_addr {
|
match area.phys_start_addr {
|
||||||
Some(phys_start) => {
|
Some(phys_start) => {
|
||||||
for page in Page::range_of(area.start_addr, area.end_addr) {
|
for page in Page::range_of(area.start_addr, area.end_addr) {
|
||||||
@ -105,13 +114,16 @@ impl MemorySet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn unmap(&mut self, pt: &mut Mapper) {
|
pub fn unmap(&self, pt: &mut Mapper) {
|
||||||
for area in self.areas.iter_mut() {
|
for area in self.areas.iter() {
|
||||||
for page in Page::range_of(area.start_addr, area.end_addr) {
|
for page in Page::range_of(area.start_addr, area.end_addr) {
|
||||||
pt.unmap(page);
|
pt.unmap(page);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item=&MemoryArea> {
|
||||||
|
self.areas.iter()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for MemorySet {
|
impl Debug for MemorySet {
|
||||||
|
@ -18,8 +18,6 @@ mod stack_allocator;
|
|||||||
mod address;
|
mod address;
|
||||||
mod frame;
|
mod frame;
|
||||||
|
|
||||||
pub static ACITVE_PAGETABLE: Mutex<ActivePageTable> = Mutex::new(unsafe { ActivePageTable::new() });
|
|
||||||
|
|
||||||
pub static FRAME_ALLOCATOR: Mutex<Option<AreaFrameAllocator>> = Mutex::new(None);
|
pub static FRAME_ALLOCATOR: Mutex<Option<AreaFrameAllocator>> = Mutex::new(None);
|
||||||
|
|
||||||
pub fn alloc_frame() -> Frame {
|
pub fn alloc_frame() -> Frame {
|
||||||
@ -31,6 +29,8 @@ pub fn alloc_frame() -> Frame {
|
|||||||
pub fn init(boot_info: BootInformation) -> MemoryController {
|
pub fn init(boot_info: BootInformation) -> MemoryController {
|
||||||
assert_has_not_been_called!("memory::init must be called only once");
|
assert_has_not_been_called!("memory::init must be called only once");
|
||||||
|
|
||||||
|
debug!("{:?}", boot_info);
|
||||||
|
|
||||||
let memory_map_tag = boot_info.memory_map_tag().expect(
|
let memory_map_tag = boot_info.memory_map_tag().expect(
|
||||||
"Memory map tag required");
|
"Memory map tag required");
|
||||||
let elf_sections_tag = boot_info.elf_sections_tag().expect(
|
let elf_sections_tag = boot_info.elf_sections_tag().expect(
|
||||||
@ -70,14 +70,12 @@ pub fn init(boot_info: BootInformation) -> MemoryController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remap_the_kernel(boot_info: BootInformation) -> Stack
|
pub fn remap_the_kernel(boot_info: BootInformation) -> Stack {
|
||||||
{
|
let mut active_table = unsafe { ActivePageTable::new() };
|
||||||
let mut active_table = ACITVE_PAGETABLE.lock();
|
|
||||||
let mut memory_set = MemorySet::from(boot_info.elf_sections_tag().unwrap());
|
let mut memory_set = MemorySet::from(boot_info.elf_sections_tag().unwrap());
|
||||||
|
|
||||||
use consts::{KERNEL_HEAP_OFFSET, KERNEL_HEAP_SIZE};
|
use consts::{KERNEL_HEAP_OFFSET, KERNEL_HEAP_SIZE};
|
||||||
memory_set.push(MemoryArea::new_identity(0xb8000, 0xb9000, EntryFlags::WRITABLE, "VGA"));
|
memory_set.push(MemoryArea::new_identity(0xb8000, 0xb9000, EntryFlags::WRITABLE, "VGA"));
|
||||||
memory_set.push(MemoryArea::new_identity(boot_info.start_address(), boot_info.end_address(), EntryFlags::PRESENT, "multiboot"));
|
|
||||||
memory_set.push(MemoryArea::new(KERNEL_HEAP_OFFSET, KERNEL_HEAP_OFFSET + KERNEL_HEAP_SIZE, EntryFlags::WRITABLE, "kernel_heap"));
|
memory_set.push(MemoryArea::new(KERNEL_HEAP_OFFSET, KERNEL_HEAP_OFFSET + KERNEL_HEAP_SIZE, EntryFlags::WRITABLE, "kernel_heap"));
|
||||||
|
|
||||||
let mut page_table = InactivePageTable::new(alloc_frame(), &mut active_table);
|
let mut page_table = InactivePageTable::new(alloc_frame(), &mut active_table);
|
||||||
@ -179,7 +177,7 @@ impl MemoryController {
|
|||||||
let flags = EntryFlags::WRITABLE;
|
let flags = EntryFlags::WRITABLE;
|
||||||
self.active_table.map_to(page, frame, flags);
|
self.active_table.map_to(page, frame, flags);
|
||||||
}
|
}
|
||||||
pub fn make_page_table(&mut self, set: &mut MemorySet) -> InactivePageTable {
|
pub fn make_page_table(&mut self, set: &MemorySet) -> InactivePageTable {
|
||||||
let mut page_table = InactivePageTable::new(alloc_frame(), &mut self.active_table);
|
let mut page_table = InactivePageTable::new(alloc_frame(), &mut self.active_table);
|
||||||
|
|
||||||
use consts::{KERNEL_HEAP_PML4, KERNEL_PML4};
|
use consts::{KERNEL_HEAP_PML4, KERNEL_PML4};
|
||||||
|
@ -37,14 +37,14 @@ extern {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn init(mc: &mut MemoryController) {
|
pub fn init(mut mc: MemoryController) {
|
||||||
PROCESSOR.call_once(|| {Mutex::new({
|
PROCESSOR.call_once(|| {Mutex::new({
|
||||||
let mut processor = Processor::new();
|
let initproc = Process::new_init(&mut mc);
|
||||||
let initproc = Process::new_init(mc);
|
let idleproc = Process::new("idle", idle_thread, &mut mc);
|
||||||
let idleproc = Process::new("idle", idle_thread, mc);
|
|
||||||
#[cfg(feature = "link_user_program")]
|
#[cfg(feature = "link_user_program")]
|
||||||
let forktest = Process::new_user(_binary_user_forktest_start as usize,
|
let forktest = Process::new_user(_binary_user_forktest_start as usize,
|
||||||
_binary_user_forktest_end as usize, mc);
|
_binary_user_forktest_end as usize, &mut mc);
|
||||||
|
let mut processor = Processor::new(mc);
|
||||||
processor.add(initproc);
|
processor.add(initproc);
|
||||||
processor.add(idleproc);
|
processor.add(idleproc);
|
||||||
processor.add(forktest);
|
processor.add(forktest);
|
||||||
@ -71,6 +71,7 @@ extern fn idle_thread() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fork the current process
|
||||||
pub fn fork(tf: &TrapFrame) {
|
pub fn fork(tf: &TrapFrame) {
|
||||||
unimplemented!()
|
PROCESSOR.try().unwrap().lock().fork(tf);
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use memory::{Stack, InactivePageTable};
|
use memory::{self, Stack, InactivePageTable};
|
||||||
use xmas_elf::{ElfFile, program::{Flags, ProgramHeader}, header::HeaderPt2};
|
use xmas_elf::{ElfFile, program::{Flags, ProgramHeader}, header::HeaderPt2};
|
||||||
use core::slice;
|
use core::slice;
|
||||||
use alloc::rc::Rc;
|
use alloc::rc::Rc;
|
||||||
@ -71,7 +71,7 @@ impl Process {
|
|||||||
let mut memory_set = MemorySet::from(&elf);
|
let mut memory_set = MemorySet::from(&elf);
|
||||||
memory_set.push(MemoryArea::new(USER_STACK_OFFSET, USER_STACK_OFFSET + USER_STACK_SIZE,
|
memory_set.push(MemoryArea::new(USER_STACK_OFFSET, USER_STACK_OFFSET + USER_STACK_SIZE,
|
||||||
EntryFlags::WRITABLE | EntryFlags::NO_EXECUTE | EntryFlags::USER_ACCESSIBLE, "user_stack"));
|
EntryFlags::WRITABLE | EntryFlags::NO_EXECUTE | EntryFlags::USER_ACCESSIBLE, "user_stack"));
|
||||||
let page_table = mc.make_page_table(&mut memory_set);
|
let page_table = mc.make_page_table(&memory_set);
|
||||||
debug!("{:#x?}", memory_set);
|
debug!("{:#x?}", memory_set);
|
||||||
|
|
||||||
// Temporary switch to it, in order to copy data
|
// Temporary switch to it, in order to copy data
|
||||||
@ -108,9 +108,42 @@ impl Process {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Fork
|
/// Fork
|
||||||
pub fn fork(&mut self) -> Self {
|
pub fn fork(&self, tf: &TrapFrame, mc: &mut MemoryController) -> Self {
|
||||||
assert!(self.is_user);
|
assert!(self.is_user);
|
||||||
unimplemented!()
|
|
||||||
|
// Clone memory set, make a new page table
|
||||||
|
let memory_set = self.memory_set.as_ref().unwrap().clone();
|
||||||
|
let page_table = mc.make_page_table(&memory_set);
|
||||||
|
|
||||||
|
// Copy data to temp space
|
||||||
|
use alloc::Vec;
|
||||||
|
let datas: Vec<Vec<u8>> = memory_set.iter().map(|area| {
|
||||||
|
Vec::from(unsafe { area.as_slice() })
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
// Temporary switch to it, in order to copy data
|
||||||
|
let page_table = mc.with(page_table, || {
|
||||||
|
for (area, data) in memory_set.iter().zip(datas.iter()) {
|
||||||
|
unsafe { area.as_slice_mut() }.copy_from_slice(data.as_slice())
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Allocate kernel stack and push trap frame
|
||||||
|
let kstack = mc.alloc_stack(7).unwrap();
|
||||||
|
let mut tf = tf.clone();
|
||||||
|
tf.scratch.rax = 1;
|
||||||
|
let rsp = kstack.push_at_top(tf);
|
||||||
|
|
||||||
|
Process {
|
||||||
|
pid: 0,
|
||||||
|
name: "fork",
|
||||||
|
kstack,
|
||||||
|
memory_set: Some(memory_set),
|
||||||
|
page_table: Some(page_table),
|
||||||
|
status: Status::Ready,
|
||||||
|
rsp,
|
||||||
|
is_user: true,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
use alloc::BTreeMap;
|
use alloc::BTreeMap;
|
||||||
use memory::{ActivePageTable, InactivePageTable};
|
use memory::{ActivePageTable, InactivePageTable};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use core::cell::RefCell;
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Processor {
|
pub struct Processor {
|
||||||
|
mc: RefCell<MemoryController>,
|
||||||
procs: BTreeMap<Pid, Process>,
|
procs: BTreeMap<Pid, Process>,
|
||||||
current_pid: Pid,
|
current_pid: Pid,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Processor {
|
impl Processor {
|
||||||
pub fn new() -> Self {
|
pub fn new(mc: MemoryController) -> Self {
|
||||||
Processor {
|
Processor {
|
||||||
|
mc: RefCell::new(mc),
|
||||||
procs: BTreeMap::<Pid, Process>::new(),
|
procs: BTreeMap::<Pid, Process>::new(),
|
||||||
current_pid: 0,
|
current_pid: 0,
|
||||||
}
|
}
|
||||||
@ -73,4 +75,14 @@ impl Processor {
|
|||||||
self.current_pid = pid;
|
self.current_pid = pid;
|
||||||
debug!("Processor: switch from {} to {}\n rsp: {:#x} -> {:#x}", pid0, pid, rsp0, rsp);
|
debug!("Processor: switch from {} to {}\n rsp: {:#x} -> {:#x}", pid0, pid, rsp0, rsp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn current(&self) -> &Process {
|
||||||
|
self.procs.get(&self.current_pid).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fork the current process
|
||||||
|
pub fn fork(&mut self, tf: &TrapFrame) {
|
||||||
|
let new = self.current().fork(tf, &mut self.mc.borrow_mut());
|
||||||
|
self.add(new);
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user