Skip to main content

user_lib/
lib.rs

1#![no_std]
2//!
3//! 教程阅读建议:
4//!
5//! - `_start` 展示用户程序最小运行时(初始化控制台、堆、调用 main);
6//! - 其余辅助函数(sleep/pipe_*)展示了常见 syscall 组合用法。
7
8mod heap;
9mod sync_lab;
10
11extern crate alloc;
12
13use tg_console::log;
14
15pub use tg_console::{print, println};
16pub use tg_syscall::*;
17pub use sync_lab::*;
18
19#[unsafe(no_mangle)]
20#[unsafe(link_section = ".text.entry")]
21pub extern "C" fn _start() -> ! {
22    // 用户态运行时初始化顺序与内核类似:先 I/O,再堆,再进入 main。
23    tg_console::init_console(&Console);
24    tg_console::set_log_level(option_env!("LOG"));
25    heap::init();
26
27    unsafe extern "C" {
28        fn main() -> i32;
29    }
30
31    // SAFETY: main 函数由用户程序提供,链接器保证其存在且符合 C ABI
32    exit(unsafe { main() });
33    unreachable!()
34}
35
36#[panic_handler]
37fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! {
38    let err = panic_info.message();
39    if let Some(location) = panic_info.location() {
40        log::error!("Panicked at {}:{}, {err}", location.file(), location.line());
41    } else {
42        log::error!("Panicked: {err}");
43    }
44    exit(1);
45    unreachable!()
46}
47
48pub fn getchar() -> u8 {
49    let mut c = [0u8; 1];
50    read(STDIN, &mut c);
51    c[0]
52}
53
54struct Console;
55
56impl tg_console::Console for Console {
57    #[inline]
58    fn put_char(&self, c: u8) {
59        tg_syscall::write(STDOUT, &[c]);
60    }
61
62    #[inline]
63    fn put_str(&self, s: &str) {
64        tg_syscall::write(STDOUT, s.as_bytes());
65    }
66}
67
68pub fn sleep(period_ms: usize) {
69    // 轮询时钟 + 主动让出 CPU 的教学实现,便于理解 time/yield 系统调用协作。
70    let mut time: TimeSpec = TimeSpec::ZERO;
71    clock_gettime(ClockId::CLOCK_MONOTONIC, &mut time as *mut _ as _);
72    let time = time + TimeSpec::from_millsecond(period_ms);
73    loop {
74        let mut now: TimeSpec = TimeSpec::ZERO;
75        clock_gettime(ClockId::CLOCK_MONOTONIC, &mut now as *mut _ as _);
76        if now > time {
77            break;
78        }
79        sched_yield();
80    }
81}
82
83pub fn get_time() -> isize {
84    let mut time: TimeSpec = TimeSpec::ZERO;
85    clock_gettime(ClockId::CLOCK_MONOTONIC, &mut time as *mut _ as _);
86    (time.tv_sec * 1000 + time.tv_nsec / 1_000_000) as isize
87}
88
89pub fn trace_read(ptr: *const u8) -> Option<u8> {
90    let ret = trace(0, ptr as usize, 0);
91    if ret >= 0 && ret <= 255 {
92        Some(ret as u8)
93    } else {
94        None
95    }
96}
97
98pub fn trace_write(ptr: *const u8, value: u8) -> isize {
99    trace(1, ptr as usize, value as usize)
100}
101
102pub fn count_syscall(syscall_id: usize) -> isize {
103    trace(2, syscall_id, 0)
104}
105
106/// t2l4 实验使用的虚拟 tick 请求号。
107pub const LAB_TICK_REQUEST: usize = 100;
108
109/// 主动向内核发送一个“虚拟时钟 tick”。
110///
111/// `tg-rcore-tutorial-t2l4` 会把这个请求映射到 `Scheduler::on_tick()`。
112pub fn lab_tick() -> isize {
113    trace(LAB_TICK_REQUEST, 0, 0)
114}
115
116/// 用纯计算制造一个可重复的 CPU burst。
117pub fn busy_spin(rounds: usize, seed: usize) -> usize {
118    let mut value = seed.wrapping_add(1);
119    for i in 0..rounds {
120        value = value
121            .wrapping_mul(1_664_525)
122            .wrapping_add(1_013_904_223)
123            .wrapping_add(i);
124        core::hint::black_box(value);
125    }
126    value
127}
128
129/// 从管道读取数据
130/// 返回实际读取的总字节数,负数表示错误
131pub fn pipe_read(pipe_fd: usize, buffer: &mut [u8]) -> isize {
132    let mut total_read = 0usize;
133    let len = buffer.len();
134    loop {
135        if total_read >= len {
136            return total_read as isize;
137        }
138        let ret = read(pipe_fd, &mut buffer[total_read..]);
139        if ret == -2 {
140            // 暂时无数据,让出 CPU 后重试
141            sched_yield();
142            continue;
143        } else if ret == 0 {
144            // EOF,写端关闭
145            return total_read as isize;
146        } else if ret < 0 {
147            // 其他错误
148            return ret;
149        } else {
150            total_read += ret as usize;
151        }
152    }
153}
154
155/// 向管道写入数据
156/// 返回实际写入的总字节数,负数表示错误
157pub fn pipe_write(pipe_fd: usize, buffer: &[u8]) -> isize {
158    let mut total_write = 0usize;
159    let len = buffer.len();
160    loop {
161        if total_write >= len {
162            return total_write as isize;
163        }
164        let ret = write(pipe_fd, &buffer[total_write..]);
165        if ret == -2 {
166            // 缓冲区满,让出 CPU 后重试
167            sched_yield();
168            continue;
169        } else if ret < 0 {
170            // 其他错误
171            return ret;
172        } else {
173            total_write += ret as usize;
174        }
175    }
176}