11.3. 命令行——执行程序

本章代码对应 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 :

  1. 在 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;
        }
        ...
    }
  2. 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())
    }

Last updated