Introduction
TIPS: This post had been rewritten for rCore Tutorial Guide 2024 Autumn
In the lab of Chapter 3, we have been asked to implement the sys_get_taskinfo
system call. This syscall is used to get the information of a task. In this post, I will share my implementation of this syscall.
The syscall is defined as follows:
1
| fn sys_task_info(ti: *mut TaskInfo) -> isize
|
The parameter ti
is a pointer to a TaskInfo
structure. The TaskInfo
structure is defined as follows:
1
2
3
4
5
6
| struct TaskInfo {
id: usize,
status: TaskStatus,
syscall_times: [u32; MAX_SYSCALL_NUM],
time: usize
}
|
When the syscall executes successfully, it should return 0. Otherwise, it should return -1.
Analysis
By review the test case of this lab, we can known we must record the system call times before it be called. And review the sys_get_time
syscall, we can known TaskInfo.time
should be the running milliseconds of the task.
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
35
36
37
38
39
40
41
42
43
44
45
46
| #![no_std]
#![no_main]
extern crate user_lib;
use user_lib::{
get_time, println, sleep, task_info, TaskInfo, TaskStatus, SYSCALL_EXIT, SYSCALL_GETTIMEOFDAY,
SYSCALL_TASK_INFO, SYSCALL_WRITE, SYSCALL_YIELD,
};
#[no_mangle]
pub fn main() -> usize {
let t1 = get_time() as usize;
let info = TaskInfo::new();
get_time();
sleep(500);
let t2: usize = get_time() as usize;
// 注意本次 task info 调用也计入
assert_eq!(0, task_info(&info));
let t3 = get_time() as usize;
assert!(3 <= info.syscall_times[SYSCALL_GETTIMEOFDAY]);
assert_eq!(1, info.syscall_times[SYSCALL_TASK_INFO]);
assert_eq!(0, info.syscall_times[SYSCALL_WRITE]);
assert!(0 < info.syscall_times[SYSCALL_YIELD]);
assert_eq!(0, info.syscall_times[SYSCALL_EXIT]);
assert!(t2 - t1 <= info.time + 1);
assert!(info.time < t3 - t1 + 100);
assert!(info.status == TaskStatus::Running);
// 想想为什么 write 调用是两次
println!("string from task info test\n");
let t4 = get_time() as usize;
assert_eq!(0, task_info(&info));
let t5 = get_time() as usize;
assert!(5 <= info.syscall_times[SYSCALL_GETTIMEOFDAY]);
assert_eq!(2, info.syscall_times[SYSCALL_TASK_INFO]);
assert_eq!(2, info.syscall_times[SYSCALL_WRITE]);
assert!(0 < info.syscall_times[SYSCALL_YIELD]);
assert_eq!(0, info.syscall_times[SYSCALL_EXIT]);
assert!(t4 - t1 <= info.time + 1);
assert!(info.time < t5 - t1 + 100);
assert!(info.status == TaskStatus::Running);
println!("Test task info OK!");
0
}
|
Implementation
We need to add the SYSCALL_GET_TASKINFO
constant to define the system call number of sys_get_taskinfo
and the MAX_SYSCALL_NUM
constant to define the maximum number of system calls.
1
2
3
4
5
| // os/src/syscall/mod.rs
const SYSCALL_GET_TASKINFO: usize = 410;
pub const MAX_SYSCALL_NUM: usize = 411;
|
Implement System Calls Counter
It’s easy to do this by adding a method to the TaskManager
structure and expose it to the outside.
1
2
3
4
5
6
7
8
9
10
11
12
13
| // os/src/task/mod.rs
impl TaskManager {
fn record_syscall_times(&self, syscall_id: usize) {
let mut inner = self.inner.exclusive_access();
let current = inner.current_task;
inner.tasks[current].syscall_times[syscall_id] += 1;
}
}
pub fn record_syscall_times(syscall_id: usize) {
TASK_MANAGER.record_syscall_times(syscall_id);
}
|
And we need to call this method in the syscall
function.
1
2
3
4
5
6
7
8
9
10
11
12
13
| // os/src/syscall/mod.rs
pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize {
record_syscall_times(syscall_id);
match syscall_id {
SYSCALL_WRITE => sys_write(args[0], args[1] as *const u8, args[2]),
SYSCALL_EXIT => sys_exit(args[0] as i32),
SYSCALL_YIELD => sys_yield(),
SYSCALL_GET_TIME => sys_get_time(args[0] as *mut TimeVal, args[1]),
_ => panic!("Unsupported syscall_id: {}", syscall_id),
}
}
|
Calculate Running Time
At first, we must implement the get_time_ms
function to get the current time in milliseconds.
1
2
3
4
5
| // os/src/time.rs
pub fn get_time_ms() -> usize {
time::read() / (CLOCK_FREQ / MILLI_PER_SEC)
}
|
We have add start_time
field to the TaskControlBlock
structure, so we can record the start time of the task when first running.
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
35
36
37
38
39
40
41
| // os/src/task/mod.rs
impl TaskManager {
fn run_first_task(&self) -> ! {
let mut inner = self.inner.exclusive_access();
let task0 = &mut inner.tasks[0];
task0.task_status = TaskStatus::Running;
task0.start_time = Some(get_time_ms()); // record the start time of the task
let next_task_cx_ptr = &task0.task_cx as *const TaskContext;
drop(inner);
let mut _unused = TaskContext::zero_init();
// before this, we should drop local variables that must be dropped manually
unsafe {
__switch(&mut _unused as *mut TaskContext, next_task_cx_ptr);
}
panic!("unreachable in run_first_task!");
}
fn run_next_task(&self) {
if let Some(next) = self.find_next_task() {
let mut inner = self.inner.exclusive_access();
let current = inner.current_task;
inner.tasks[next].task_status = TaskStatus::Running;
// record the start time of the task
if inner.tasks[next].start_time.is_none() {
inner.tasks[next].start_time = Some(get_time_ms());
}
inner.current_task = next;
let current_task_cx_ptr = &mut inner.tasks[current].task_cx as *mut TaskContext;
let next_task_cx_ptr = &inner.tasks[next].task_cx as *const TaskContext;
drop(inner);
// before this, we should drop local variables that must be dropped manually
unsafe {
__switch(current_task_cx_ptr, next_task_cx_ptr);
}
// go back to user mode
} else {
panic!("All applications completed!");
}
}
}
|
The we can calculate the running time of the task by the following code:
1
| get_time_ms() - self.inner.tasks[current].start_time.unwrap()
|
Implement A Method for Get TaskInfo
Now we can implement a method to get the TaskInfo
structure of a task.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| // os/src/task/mod.rs
impl TaskManager {
fn get_taskinfo(&self, ) -> TaskInfo {
let inner = self.inner.exclusive_access();
let current = inner.current_task;
let task = inner.tasks[current];
TaskInfo {
status: task.task_status,
syscall_times: task.syscall_times,
time: get_time_ms() - task.start_time.unwrap(),
}
}
}
/// Get the task information of the current task
pub fn get_taskinfo() -> TaskInfo {
TASK_MANAGER.get_taskinfo()
}
|
Implement the sys_get_taskinfo
System Call
Finally, we can implement the sys_get_taskinfo
system call. When the task id is invalid, we should return -1. Otherwise, we should copy the TaskInfo
structure to the memory pointed by the ts
parameter.
1
2
3
4
5
6
7
8
9
10
11
12
| // os/src/syscall/process.rs
pub fn sys_task_info(ti: *mut TaskInfo) -> isize {
trace!("kernel: sys_task_info");
let task_info = get_taskinfo();
unsafe {
*ti = task_info;
}
0
}
|
Don’t forget to add the sys_get_taskinfo
function to the syscall
function.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // os/src/syscall/mod.rs
pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize {
record_syscall_times(syscall_id);
match syscall_id {
SYSCALL_WRITE => sys_write(args[0], args[1] as *const u8, args[2]),
SYSCALL_EXIT => sys_exit(args[0] as i32),
SYSCALL_YIELD => sys_yield(),
SYSCALL_GET_TASKINFO => sys_get_taskinfo(args[0] as usize, args[1] as *mut TaskInfo),
SYSCALL_GET_TIME => sys_get_time(args[0] as *mut TimeVal, args[1]),
_ => panic!("Unsupported syscall_id: {}", syscall_id),
}
}
|
Test in User Space
After implementing the system call, we can test it in user space. First, we should add some structures and constants to the user library.
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
| // user/src/lib.rs
#[derive(Debug, PartialEq)]
pub enum TaskStatus {
UnInit,
Ready,
Running,
Exited,
}
#[derive(Debug)]
pub struct TaskInfo {
pub id: usize,
pub status: TaskStatus,
pub syscall_times: [u32; MAX_SYSCALL_NUM],
pub time: usize,
}
impl TaskInfo {
pub fn new() -> Self {
Self {
id: 0,
status: TaskStatus::UnInit,
syscall_times: [0; MAX_SYSCALL_NUM],
time: 0,
}
}
}
|
1
2
3
4
5
| // user/src/syscall.rs
pub const SYSCALL_TASK_INFO: usize = 410;
pub const MAX_SYSCALL_NUM: usize = 411;
|
Then we can implement the task_info
function to get the task information.
1
2
3
4
5
| // user/src/syscall.rs
pub fn sys_get_taskinfo(ti: &TaskInfo) -> isize {
syscall(SYSCALL_TASK_INFO, [ts as *const _ as usize, 0, 0])
}
|
1
2
3
4
5
| // user/src/lib.rs
pub fn task_info(ti: &TaskInfo) -> isize {
sys_get_taskinfo(ti)
}
|
Finally, we can test this lab by run make run TEST=1
. The test case had been shown in the analysis section.
Conclusion
In this post, I shared my implementation of the sys_get_taskinfo
system call. I will continue to study the rCore Tutorials and share my notes in the future. If you have any questions or suggestions, please email me (Emails are in the about page). Thank you for reading.