Skip to main content

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