Lab1 编程作业

读完前 3 章终于迎来了第一个 Lab,感觉前面都忘完了

先跟着敲了一遍第 3 章(os3-ref)的代码,稍微对代码框架了解了一点

首先考虑实现获取 syscall_times,注意是查询当前任务的调用次数,不是所有任务调用的总和

TaskControlBlock 中维护,调用 syscall 的时候修改即可。

1
2
3
4
5
6
7
// os/src/task/task.rs

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
// os/src/syscall/mod.rs

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
// os/src/task/mod.rs

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
// os/src/syscall/process.rs

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
// os/src/task/mod.rs

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
// os/src/syscall/process.rs

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
// os/src/task/task.rs

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
// os/src/task/mod.rs

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
// os/src/task/mod.rs

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
// os/src/syscall/process.rs

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
// os/src/timer.rs

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