rcore-step-by-step
  • 0. 从零开始写 OS
  • 1. 独立式可执行程序
  • 2. 最小化内核
  • 3. 格式化输出
  • 4. 实现中断
    • 4.1.1 Trap
    • 4.1.2 中断跳转
    • 4.2. 时钟中断
  • 5. 页表简介
  • 6. 内存分配
  • 7. 创建页表
  • 8. 内核线程
  • 9. 线程调度
  • 10. 用户进程
  • 11. 命令行
    • 11.1. 命令行——输出
    • 11.2. 命令行——输入(信号量)
    • 11.3. 命令行——执行程序
Powered by GitBook
On this page
  • 用户程序
  • 一些 bug

Was this helpful?

  1. 11. 命令行

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())
    }
Previous11.2. 命令行——输入(信号量)

Last updated 5 years ago

Was this helpful?