os/task/
process.rs

1use super::TaskControlBlock;
2use super::id::RecycleAllocator;
3use super::manager::insert_into_pid2process;
4use super::{PidHandle, pid_alloc};
5use super::{SignalFlags, add_task};
6use crate::fs::{File, Stdin, Stdout};
7use crate::mm::{KERNEL_SPACE, MemorySet, translated_refmut};
8use crate::sync::{Condvar, Mutex, Semaphore, UPSafeCell};
9use crate::trap::{TrapContext, trap_handler};
10use alloc::string::String;
11use alloc::sync::{Arc, Weak};
12use alloc::vec;
13use alloc::vec::Vec;
14use core::cell::RefMut;
15
16pub struct ProcessControlBlock {
17    // immutable
18    pub pid: PidHandle,
19    // mutable
20    inner: UPSafeCell<ProcessControlBlockInner>,
21}
22
23pub struct ProcessControlBlockInner {
24    pub is_zombie: bool,
25    pub memory_set: MemorySet,
26    pub parent: Option<Weak<ProcessControlBlock>>,
27    pub children: Vec<Arc<ProcessControlBlock>>,
28    pub exit_code: i32,
29    pub fd_table: Vec<Option<Arc<dyn File + Send + Sync>>>,
30    pub signals: SignalFlags,
31    pub tasks: Vec<Option<Arc<TaskControlBlock>>>,
32    pub task_res_allocator: RecycleAllocator,
33    pub mutex_list: Vec<Option<Arc<dyn Mutex>>>,
34    pub semaphore_list: Vec<Option<Arc<Semaphore>>>,
35    pub condvar_list: Vec<Option<Arc<Condvar>>>,
36}
37
38impl ProcessControlBlockInner {
39    #[allow(unused)]
40    pub fn get_user_token(&self) -> usize {
41        self.memory_set.token()
42    }
43
44    pub fn alloc_fd(&mut self) -> usize {
45        if let Some(fd) = (0..self.fd_table.len()).find(|fd| self.fd_table[*fd].is_none()) {
46            fd
47        } else {
48            self.fd_table.push(None);
49            self.fd_table.len() - 1
50        }
51    }
52
53    pub fn alloc_tid(&mut self) -> usize {
54        self.task_res_allocator.alloc()
55    }
56
57    pub fn dealloc_tid(&mut self, tid: usize) {
58        self.task_res_allocator.dealloc(tid)
59    }
60
61    pub fn thread_count(&self) -> usize {
62        self.tasks.len()
63    }
64
65    pub fn get_task(&self, tid: usize) -> Arc<TaskControlBlock> {
66        self.tasks[tid].as_ref().unwrap().clone()
67    }
68}
69
70impl ProcessControlBlock {
71    pub fn inner_exclusive_access(&self) -> RefMut<'_, ProcessControlBlockInner> {
72        self.inner.exclusive_access()
73    }
74
75    pub fn new(elf_data: &[u8]) -> Arc<Self> {
76        // memory_set with elf program headers/trampoline/trap context/user stack
77        let (memory_set, ustack_base, entry_point) = MemorySet::from_elf(elf_data);
78        // allocate a pid
79        let pid_handle = pid_alloc();
80        let process = Arc::new(Self {
81            pid: pid_handle,
82            inner: unsafe {
83                UPSafeCell::new(ProcessControlBlockInner {
84                    is_zombie: false,
85                    memory_set,
86                    parent: None,
87                    children: Vec::new(),
88                    exit_code: 0,
89                    fd_table: vec![
90                        // 0 -> stdin
91                        Some(Arc::new(Stdin)),
92                        // 1 -> stdout
93                        Some(Arc::new(Stdout)),
94                        // 2 -> stderr
95                        Some(Arc::new(Stdout)),
96                    ],
97                    signals: SignalFlags::empty(),
98                    tasks: Vec::new(),
99                    task_res_allocator: RecycleAllocator::new(),
100                    mutex_list: Vec::new(),
101                    semaphore_list: Vec::new(),
102                    condvar_list: Vec::new(),
103                })
104            },
105        });
106        // create a main thread, we should allocate ustack and trap_cx here
107        let task = Arc::new(TaskControlBlock::new(
108            Arc::clone(&process),
109            ustack_base,
110            true,
111        ));
112        // prepare trap_cx of main thread
113        let task_inner = task.inner_exclusive_access();
114        let trap_cx = task_inner.get_trap_cx();
115        let ustack_top = task_inner.res.as_ref().unwrap().ustack_top();
116        let kstack_top = task.kstack.get_top();
117        drop(task_inner);
118        *trap_cx = TrapContext::app_init_context(
119            entry_point,
120            ustack_top,
121            KERNEL_SPACE.exclusive_access().token(),
122            kstack_top,
123            trap_handler as usize,
124        );
125        // add main thread to the process
126        let mut process_inner = process.inner_exclusive_access();
127        process_inner.tasks.push(Some(Arc::clone(&task)));
128        drop(process_inner);
129        insert_into_pid2process(process.getpid(), Arc::clone(&process));
130        // add main thread to scheduler
131        add_task(task);
132        process
133    }
134
135    /// Only support processes with a single thread.
136    pub fn exec(self: &Arc<Self>, elf_data: &[u8], args: Vec<String>) {
137        assert_eq!(self.inner_exclusive_access().thread_count(), 1);
138        // memory_set with elf program headers/trampoline/trap context/user stack
139        let (memory_set, ustack_base, entry_point) = MemorySet::from_elf(elf_data);
140        let new_token = memory_set.token();
141        // substitute memory_set
142        self.inner_exclusive_access().memory_set = memory_set;
143        // then we alloc user resource for main thread again
144        // since memory_set has been changed
145        let task = self.inner_exclusive_access().get_task(0);
146        let mut task_inner = task.inner_exclusive_access();
147        task_inner.res.as_mut().unwrap().ustack_base = ustack_base;
148        task_inner.res.as_mut().unwrap().alloc_user_res();
149        task_inner.trap_cx_ppn = task_inner.res.as_mut().unwrap().trap_cx_ppn();
150        // push arguments on user stack
151        let mut user_sp = task_inner.res.as_mut().unwrap().ustack_top();
152        user_sp -= (args.len() + 1) * core::mem::size_of::<usize>();
153        let argv_base = user_sp;
154        let mut argv: Vec<_> = (0..=args.len())
155            .map(|arg| {
156                translated_refmut(
157                    new_token,
158                    (argv_base + arg * core::mem::size_of::<usize>()) as *mut usize,
159                )
160            })
161            .collect();
162        *argv[args.len()] = 0;
163        for i in 0..args.len() {
164            user_sp -= args[i].len() + 1;
165            *argv[i] = user_sp;
166            let mut p = user_sp;
167            for c in args[i].as_bytes() {
168                *translated_refmut(new_token, p as *mut u8) = *c;
169                p += 1;
170            }
171            *translated_refmut(new_token, p as *mut u8) = 0;
172        }
173        // make the user_sp aligned to 8B for k210 platform
174        user_sp -= user_sp % core::mem::size_of::<usize>();
175        // initialize trap_cx
176        let mut trap_cx = TrapContext::app_init_context(
177            entry_point,
178            user_sp,
179            KERNEL_SPACE.exclusive_access().token(),
180            task.kstack.get_top(),
181            trap_handler as usize,
182        );
183        trap_cx.x[10] = args.len();
184        trap_cx.x[11] = argv_base;
185        *task_inner.get_trap_cx() = trap_cx;
186    }
187
188    /// Only support processes with a single thread.
189    pub fn fork(self: &Arc<Self>) -> Arc<Self> {
190        let mut parent = self.inner_exclusive_access();
191        assert_eq!(parent.thread_count(), 1);
192        // clone parent's memory_set completely including trampoline/ustacks/trap_cxs
193        let memory_set = MemorySet::from_existed_user(&parent.memory_set);
194        // alloc a pid
195        let pid = pid_alloc();
196        // copy fd table
197        let mut new_fd_table: Vec<Option<Arc<dyn File + Send + Sync>>> = Vec::new();
198        for fd in parent.fd_table.iter() {
199            if let Some(file) = fd {
200                new_fd_table.push(Some(file.clone()));
201            } else {
202                new_fd_table.push(None);
203            }
204        }
205        // create child process pcb
206        let child = Arc::new(Self {
207            pid,
208            inner: unsafe {
209                UPSafeCell::new(ProcessControlBlockInner {
210                    is_zombie: false,
211                    memory_set,
212                    parent: Some(Arc::downgrade(self)),
213                    children: Vec::new(),
214                    exit_code: 0,
215                    fd_table: new_fd_table,
216                    signals: SignalFlags::empty(),
217                    tasks: Vec::new(),
218                    task_res_allocator: RecycleAllocator::new(),
219                    mutex_list: Vec::new(),
220                    semaphore_list: Vec::new(),
221                    condvar_list: Vec::new(),
222                })
223            },
224        });
225        // add child
226        parent.children.push(Arc::clone(&child));
227        // create main thread of child process
228        let task = Arc::new(TaskControlBlock::new(
229            Arc::clone(&child),
230            parent
231                .get_task(0)
232                .inner_exclusive_access()
233                .res
234                .as_ref()
235                .unwrap()
236                .ustack_base(),
237            // here we do not allocate trap_cx or ustack again
238            // but mention that we allocate a new kstack here
239            false,
240        ));
241        // attach task to child process
242        let mut child_inner = child.inner_exclusive_access();
243        child_inner.tasks.push(Some(Arc::clone(&task)));
244        drop(child_inner);
245        // modify kstack_top in trap_cx of this thread
246        let task_inner = task.inner_exclusive_access();
247        let trap_cx = task_inner.get_trap_cx();
248        trap_cx.kernel_sp = task.kstack.get_top();
249        drop(task_inner);
250        insert_into_pid2process(child.getpid(), Arc::clone(&child));
251        // add this thread to scheduler
252        add_task(task);
253        child
254    }
255
256    pub fn getpid(&self) -> usize {
257        self.pid.0
258    }
259}