good_os_framework/task/
process.rs

1use alloc::collections::VecDeque;
2use alloc::string::String;
3use alloc::sync::{Arc, Weak};
4use core::fmt::Debug;
5use core::sync::atomic::{AtomicU64, Ordering};
6use object::{File, Object, ObjectSegment};
7use spin::{Lazy, RwLock};
8use x86_64::instructions::interrupts;
9use x86_64::structures::paging::mapper::CleanUp;
10use x86_64::structures::paging::PageTableFlags;
11use x86_64::VirtAddr;
12
13use super::signal::SignalManager;
14use super::thread::{SharedThread, Thread};
15use crate::memory::MemoryManager;
16use crate::memory::{create_page_table_from_kernel, HeapType, ProcessHeap};
17use crate::memory::{GeneralPageTable, FRAME_ALLOCATOR};
18
19pub(super) type SharedProcess = Arc<RwLock<Process>>;
20pub(super) type WeakSharedProcess = Weak<RwLock<Process>>;
21
22static PROCESSES: RwLock<VecDeque<SharedProcess>> = RwLock::new(VecDeque::new());
23pub static KERNEL_PROCESS: Lazy<SharedProcess> = Lazy::new(|| Process::new_kernel_process());
24
25const KERNEL_PROCESS_NAME: &str = "kernel";
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
28pub struct ProcessId(pub u64);
29
30impl ProcessId {
31    fn new() -> Self {
32        static NEXT_ID: AtomicU64 = AtomicU64::new(0);
33        ProcessId(NEXT_ID.fetch_add(1, Ordering::Relaxed))
34    }
35}
36
37#[allow(dead_code)]
38pub struct Process {
39    pub id: ProcessId,
40    name: String,
41    pub page_table: GeneralPageTable,
42    pub threads: VecDeque<SharedThread>,
43    pub heap: ProcessHeap,
44    pub signal_manager: SignalManager,
45    pub father: Option<WeakSharedProcess>,
46}
47
48impl Process {
49    /// Creates a new process.
50    /// Don't use this function directly, use `new_user_process` instead.
51    pub fn new(name: &str, heap_type: HeapType) -> Self {
52        let page_table = create_page_table_from_kernel();
53        let pid = ProcessId::new();
54        let process = Process {
55            id: pid,
56            name: String::from(name),
57            page_table,
58            threads: Default::default(),
59            heap: ProcessHeap::new(heap_type),
60            signal_manager: SignalManager::new(64),
61            father: None,
62        };
63
64        process
65    }
66
67    /// Creates a new kernel process.
68    /// Dont't use this function. There should be only one kernel process.
69    pub fn new_kernel_process() -> SharedProcess {
70        let process = Arc::new(RwLock::new(Self::new(
71            KERNEL_PROCESS_NAME,
72            HeapType::Kernel,
73        )));
74        process.read().heap.init(Arc::downgrade(&process));
75        process
76    }
77
78    /// Creates a new user process.
79    pub fn new_user_process(name: &str, elf_data: &'static [u8]) -> SharedProcess {
80        let binary = ProcessBinary::parse(elf_data);
81        let process = Arc::new(RwLock::new(Self::new(name, HeapType::User)));
82        process.read().heap.init(Arc::downgrade(&process));
83        ProcessBinary::map_segments(&binary, &mut process.write().page_table);
84        //log::info!("User Entry Point: {:x} ID: {:?}", binary.entry(),process.read().id);
85        Thread::new_user_thread(Arc::downgrade(&process), binary.entry() as usize);
86        PROCESSES.write().push_back(process.clone());
87        process
88    }
89
90    pub fn exit_process(&self) {
91        let mut processes = PROCESSES.write();
92        if let Some(index) = processes
93            .iter()
94            .position(|process| process.read().id == self.id)
95        {
96            processes.remove(index);
97        }
98    }
99}
100
101impl Drop for Process {
102    /// drop the data of the process.
103    fn drop(&mut self) {
104        unsafe { self.page_table.clean_up(&mut *FRAME_ALLOCATOR.lock()) };
105    }
106}
107
108struct ProcessBinary;
109
110impl ProcessBinary {
111    fn parse(bin: &'static [u8]) -> File<'static> {
112        File::parse(bin).expect("Failed to parse ELF binary!")
113    }
114
115    fn map_segments(elf_file: &File, page_table: &mut GeneralPageTable) {
116        interrupts::without_interrupts(|| {
117            for segment in elf_file.segments() {
118                let segment_address = VirtAddr::new(segment.address() as u64);
119
120                let flags = PageTableFlags::PRESENT
121                    | PageTableFlags::WRITABLE
122                    | PageTableFlags::USER_ACCESSIBLE;
123
124                <MemoryManager>::alloc_range(segment_address, segment.size(), flags, page_table)
125                    .expect("Failed to allocate memory for ELF segment!");
126
127                if let Ok(data) = segment.data() {
128                    page_table.write(data, segment_address).unwrap();
129                }
130            }
131        });
132    }
133}