Lab1 编程作业
读完前 3 章终于迎来了第一个 Lab,感觉前面都忘完了
先跟着敲了一遍第 3 章(os3-ref)的代码,稍微对代码框架了解了一点
首先考虑实现获取 syscall_times,注意是查询当前任务的调用次数,不是所有任务调用的总和
在 TaskControlBlock 中维护,调用 syscall 的时候修改即可。
1 2 3 4 5 6 7
   | 
  pub struct TaskControlBlock {     pub task_status: TaskStatus,     pub task_cx: TaskContext,     pub syscall_times: [u32; MAX_SYSCALL_NUM], }
 
  | 
 
1 2 3 4 5 6 7 8 9 10
   | 
  use crate::task::count_syscall;
  pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize {     count_syscall(syscall_id);     match syscall_id {         ...     } }
 
  | 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
   | 
  lazy_static! {     ...         let mut tasks = [TaskControlBlock {                 task_cx: TaskContext::zero_init(),                 task_status: TaskStatus::UnInit,                 syscall_times: [0; MAX_SYSCALL_NUM],             }; MAX_APP_NUM];     ... }
  impl TaskManager {     fn count_syscall(&self, syscall_id: usize) {         if syscall_id < MAX_SYSCALL_NUM {             let mut inner = TASK_MANAGER.inner.exclusive_access();             let current_task = inner.current_task;             inner.tasks[current_task].syscall_times[syscall_id] += 1;         }     }
      fn get_syscall_times(&self) -> [u32; MAX_SYSCALL_NUM] {         let inner = TASK_MANAGER.inner.exclusive_access();         inner.tasks[inner.current_task].syscall_times     } }
  pub fn count_syscall(syscall_id: usize) {     TASK_MANAGER.count_syscall(syscall_id); }
  pub fn get_syscall_times() -> [u32; MAX_SYSCALL_NUM] {     TASK_MANAGER.get_syscall_times() }
 
  | 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   | 
  use crate::task::get_syscall_times
  pub fn sys_task_info(ti: *mut TaskInfo) -> isize {     unsafe {         *ti = TaskInfo {             status: ?,             syscall_times: get_syscall_times(),             time: ?,         }     }     0 }
 
  | 
 
接着考虑实现获取当前任务状态(虽然一定是 TaskStatus::Running)
往上回溯,TaskStatus -> TaskControlBlock -> TaskManagerInner -> TaskManager -> static ref TASK_MANAGER
同时可以在 TaskManagerInner 中找到 current_task
于是可以直接调用得到状态,在 TaskManager 中实现接口:
1 2 3 4 5 6 7 8 9 10 11 12
   | 
  impl TaskManager {     fn get_current_task_status(&self) -> TaskStatus {         let inner = self.inner.exclusive_access();         inner.tasks[inner.current_task].task_status     } }
  pub fn get_current_task_status() -> TaskStatus {     TASK_MANAGER.get_current_task_status() }
 
  | 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   | 
  use crate::task::get_current_task_status;
  pub fn sys_task_info(ti: *mut TaskInfo) -> isize {     unsafe {         *ti = TaskInfo {             status: get_current_task_status(),             syscall_times: get_syscall_times(),             time: ?,         }     }     0 }
 
  | 
 
对于任务总运行时长,同样在 TaskControlBlock 中维护,记一个 start_time: Option<usize> 变量表示开始运行的时间,初始化为 None。
然后调用是判断是否是初次运行并记录,查询时直接计算即可。
而所有任务运行前都会调用 __switch 进行上下文切换,考虑在调用 __switch 前后对 start_time 进行记录。
1 2 3 4 5 6 7 8
   | 
  pub struct TaskControlBlock {     pub task_status: TaskStatus,     pub task_cx: TaskContext,     pub syscall_times: [u32; MAX_SYSCALL_NUM],     pub start_time: Option<usize>, }
 
  | 
 
1 2 3 4 5 6 7 8 9 10 11 12
   | 
  lazy_static! {     ...         let mut tasks = [TaskControlBlock {             task_cx: TaskContext::zero_init(),             task_status: TaskStatus::UnInit,             syscall_times: [0; MAX_SYSCALL_NUM],             start_time: None,         }; MAX_APP_NUM];     ... }
 
  | 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
   | 
  impl TaskManager {     fn run_first_task(&self) -> ! {         ...         let next_task_cx_ptr = &task0.task_cx as *const TaskContext;         task0.start_time = Some(get_time());         drop(inner);         ...     }
      fn run_next_task(&self) {         ...             let next_task_cx_ptr = &inner.tasks[next].task_cx as *const TaskContext;             if inner.tasks[next].start_time.is_none() {                 inner.tasks[next].start_time = Some(get_time());             }             drop(inner);         ...     }
      fn get_current_run_time(&self) -> usize {         let inner = TASK_MANAGER.inner.exclusive_access();         (get_time() - inner.tasks[inner.current_task].start_time.unwrap()) / (CLOCK_FREQ / 1000)     } }
  pub fn get_current_run_time() -> usize {     TASK_MANAGER.get_current_run_time() }
 
  | 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   | 
  use crate::task::get_current_run_time;
  pub fn sys_task_info(ti: *mut TaskInfo) -> isize {     unsafe {         *ti = TaskInfo {             status: get_current_task_status(),             syscall_times: get_syscall_times(),             time: get_current_run_time(),         }     }     0 }
 
  | 
 
然后这时直接进行测试的话会发现直接卡在启动前了,根据 Github 中一个 issue 提到,大概是因为开的 syscall_times 爆栈了,解决方案里面写了几种,我选择把数组用 Box 包起来分配到堆上,因为 Box 没有实现 Copy Trait,注意要把 TaskManagerInner 中的 tasks 改为 Vec 实现。
最后,发现得到的任务运行时间总和测例有差距,看了看测例发现用了一种很奇怪的算法(反正我是没看懂),直接搬了
1 2 3 4 5 6
   | 
  pub fn get_time_ms() -> usize {     let t = get_time();     (t / CLOCK_FREQ & 0xffff) * 1000 + (t / (CLOCK_FREQ / 1_000_000) % 1_000_000 / 1000) }
 
  | 
 
Github Repository