命令行——执行程序
本章代码对应 commit :ca94d49d69c18ce2925e3949d718cd74ddc3432c
用户程序
在 linux 中,ls, cd, pwd 等命令,其实都是可执行程序。这里我们创建一个简单的 hello 程序:
// in usr/rust/src/bin/hello.rs
#![no_std]
#![no_main]
#[macro_use]
extern crate rust;
#[no_mangle]
pub fn main() -> i32 {
println!("Hello world!");
return 0;
}
接下来,需要修改命令行,使得其能够通过系统调用创建并执行程序:
// in usr/rust/src/bin/shell.rs
#![no_std]
#![no_main]
#![feature(alloc)]
extern crate alloc;
#[macro_use]
extern crate rust;
use rust::io::getc;
use rust::syscall::sys_exec;
use alloc::string::String;
const LF: u8 = 0x0au8;
const CR: u8 = 0x0du8;
// IMPORTANT: Must define main() like this
#[no_mangle]
pub fn main() -> i32 {
println!("Rust user shell");
let mut line: String = String::new();
print!(">> ");
loop {
let c = getc();
match c {
LF | CR => {
println!("");
if !line.is_empty() {
sys_exec(line.as_ptr());
line.clear();
}
print!(">> ");
}
_ => {
print!("{}", c as char);
line.push(c as char)
}
}
}
}
// in usr/rust/bin/shell.rs
pub fn sys_exec(path : *const u8) {
sys_call(SyscallId::Exec, path as usize, 0, 0, 0);
}
enum SyscallId {
...
Exec = 221,
}
用户程序将输入的字符串的指针作为参数传给 os ,os 需要将其转换回字符串,再进行处理:
...
pub const SYS_EXEC: usize = 221;
pub fn syscall(id: usize, args: [usize;3], tf: &mut TrapFrame) -> isize {
match id {
...
SYS_EXEC => {
sys_exec(args[0] as *const u8);
},
_ => {
panic!("unknown syscall id {}", id);
},
};
return 0;
}
pub unsafe fn from_cstr(s: *const u8) -> &'static str {
use core::{slice, str};
let len = (0usize..).find(|&i| *s.add(i) == 0).unwrap();
str::from_utf8(slice::from_raw_parts(s, len)).unwrap()
}
fn sys_exec(path : *const u8) -> isize {
process::excute(unsafe{ from_cstr(path) });
return 0;
}
执行程序的代码在 process::init
中其实已经有了哦,就是创建 shell 的部分:
// in process/mod.rs
pub fn excute(name : &str) {
println!("excutint program: {}", name);
let data = ROOT_INODE
.lookup(name)
.unwrap()
.read_as_vec()
.unwrap();
let thread = unsafe{ Thread::new_user(data.as_slice()) };
CPU.add_thread(thread);
}
pub fn init() {
println!("+------ now to initialize process ------+");
let scheduler = Scheduler::new(1);
let thread_pool = ThreadPool::new(100, scheduler);
println!("+------ now to initialize processor ------+");
CPU.init(Thread::new_idle(), Box::new(thread_pool));
excute("rust/shell");
}
这一章没有用到任何新知识呢,是不是学起来很快乐呢(反正我写起来挺快乐。。。(什))
一些 bug
发现了一些以前写的 bug :
在 alloc tid 的时候,通过
threads[id].is_none()
判断 id 是否被分配,但是程序 exit 的时候并没有将其还原为 None 。所以需要进行一些修改:pub fn ThreadPool::exit(&mut self, tid: Tid, code: usize) { self.threads[tid] = None; self.scheduler.exit(tid); println!("exit code: {}", code); } pub fn ThreadPool::retrieve(&mut self, tid: Tid, thread: Box<Thread> ) { if (self.threads[tid].is_none()) { return; } ... }
在
rust/src/lang_items.rs
中,程序结束调用sys_exit(0)
,返回值被写死了,应该改为:fn rust::lang_items::main() -> usize { panic!("No main() linked"); } #[no_mangle] pub extern "C" fn rust::lang_items::_start(_argc: isize, _argv: *const *const u8) -> ! { init_heap(); sys_exit(main()) }