文件系统服务线程

前面已经介绍了文件系统请求和文件系统请求的处理器,现在我们就来说明内核创建文件系统请求并转发给文件系统内核线程的完整过程:

sys_open()

/// 当前进程打开文件
///
/// 异步系统调用,发送请求给fs内核线程并异步等待被唤醒
/// 所以不能直接使用寄存器传递返回fd,需要将fd指针传递
/// 给内核线程,服务完成后线程将fd写入用户态
///
/// 若fs线程不存在或发生其他错误,则返回(usize::MAX)
pub fn sys_open(path_ptr: usize, flags: usize, fd_ptr: usize) -> (usize, usize) {
    let fs_kthread = KTHREAD_MAP.get().get(&KthreadType::FS);
    match fs_kthread {
        Some(fs_kthread) => {
            let current_thread = CURRENT_THREAD.get().as_ref().unwrap().clone();
            let pid = current_thread.proc().unwrap().pid();
            // 构造fsreq
            let fsreq = FsReqDescription::Open(pid, path_ptr, flags as _, fd_ptr)
                .as_bytes()
                .to_vec();
            // 发送请求给fskthread
            let fs_kthread = fs_kthread.clone();
            let req_id = fs_kthread.add_request(fsreq);
            // 当前线程进入异步等待
            current_thread.set_state(trap::ThreadState::Waiting);
            // 生成等待协程
            executor::spawn(WaitForKthread::new(current_thread, fs_kthread, req_id));
            return (0, 0);
        }
        None => {
            println!("[Kernel] Error when sys_open, FS kthread not exist!");
            return (usize::MAX, 0);
        }
    }
}

sys_open系统调用会首先从全局内核线程映射中获取fs内核线程,再将相关的信息收集起来并打包构造为FsReqDescription::Open请求再转化为字节,并加入到fs内核的请求队列中,再创建一个WaitForKthread协程并令当前线程进入等待。

sys_read()

/// 读取当前进程的fd对应的文件
///
/// 若是标准输入输出则直接读取,
/// 磁盘文件则发送请求给内核线程
///
/// 不存在此文件则返回usize::MAX
/// (0, 0)表示异步,返回值还未写入;
/// (read_size, 0)表示同步,可以直接使用返回值
pub fn sys_read(fd: usize, buf_ptr: usize, buf_len: usize, result_ptr: usize) -> (usize, usize) {
    let current_thread = CURRENT_THREAD.get().as_ref().unwrap().clone();
    let current_proc = current_thread.proc().unwrap();
    let file_table = current_proc.file_table();
    let file = if let Some(Some(file)) = file_table.get(fd) {
        file.clone()
    // 不存在这个文件,直接返回不进行任何处理
    } else {
        return (usize::MAX, 0);
    };
    // 磁盘文件OSInode,则发送请求给fs内核线程
    if let Ok(_osinode) = file.clone().downcast_arc::<OSInode>() {
        let fs_kthread = KTHREAD_MAP.get().get(&KthreadType::FS);
        match fs_kthread {
            Some(fs_kthread) => {
                let current_thread = CURRENT_THREAD.get().as_ref().unwrap().clone();
                let pid = current_thread.proc().unwrap().pid();
                // 构造fsreq
                let fsreq = FsReqDescription::Read(pid, fd, buf_ptr, buf_len, result_ptr)
                    .as_bytes()
                    .to_vec();
                // 发送请求给fskthread
                let fs_kthread = fs_kthread.clone();
                let req_id = fs_kthread.add_request(fsreq);
                // 当前线程进入异步等待
                current_thread.set_state(trap::ThreadState::Waiting);
                // 生成等待协程
                executor::spawn(WaitForKthread::new(current_thread, fs_kthread, req_id));
                return (0, 0);
            }
            // fs-server线程不存在,返回usize::MAX
            None => {
                println!("[Kernel] Error when sys_open, FS kthread not exist!");
                return (usize::MAX, 0);
            }
        }
    // 若是标准输入输出则直接读取不发送请求
    } else {
        let buf_ptr = buf_ptr as *mut u8;
        let buf = unsafe { core::slice::from_raw_parts_mut(buf_ptr, buf_len) };
        let read_size = file.read(buf);
        (read_size, 0)
    }
}

类似于sys_open()sys_read()创建FsReqDescription::Read请求并转发给内核线程。但需要主要的是如果读取的是Stdin即标准输入则直接在内核主线程中完成,没有必要进入内核线程。这是因为标准输入的read()方法只是简单地从串口读取一个字节,并不涉及复杂的底层驱动程序,我们认为它是安全的。

sys_write()

/// 写入当前进程的fd对应的文件
///
/// 若是标准输入输出则直接写入
/// 磁盘文件则发送请求给内核线程
///
/// 出错则返回usize::MAX
/// (0, 0)表示异步,返回值还未写入;
/// (read_size, 0)表示同步,可以直接使用返回值
pub fn sys_write(fd: usize, buf_ptr: usize, buf_len: usize, result_ptr: usize) -> (usize, usize) {
    let current_thread = CURRENT_THREAD.get().as_ref().unwrap().clone();
    let current_proc = current_thread.proc().unwrap();
    let file_table = current_proc.file_table();
    let file = if let Some(Some(file)) = file_table.get(fd) {
        file.clone()
    // 不存在这个文件,直接返回不进行任何处理
    } else {
        return (usize::MAX, 0);
    };
    // 磁盘文件OSInode,则发送请求给fs内核线程
    if let Ok(_osinode) = file.clone().downcast_arc::<OSInode>() {
        let fs_kthread = KTHREAD_MAP.get().get(&KthreadType::FS);
        match fs_kthread {
            Some(fs_kthread) => {
                let current_thread = CURRENT_THREAD.get().as_ref().unwrap().clone();
                let pid = current_thread.proc().unwrap().pid();
                // 构造fsreq
                let fsreq = FsReqDescription::Write(pid, fd, buf_ptr, buf_len, result_ptr)
                    .as_bytes()
                    .to_vec();
                // 发送请求给fskthread
                let fs_kthread = fs_kthread.clone();
                let req_id = fs_kthread.add_request(fsreq);
                // 当前线程进入异步等待
                current_thread.set_state(trap::ThreadState::Waiting);
                // 生成等待协程
                executor::spawn(WaitForKthread::new(current_thread, fs_kthread, req_id));
                return (0, 0);
            }
            // fs-server线程不存在,返回usize::MAX
            None => {
                println!("[Kernel] Error when sys_open, FS kthread not exist!");
                return (usize::MAX, 0);
            }
        }
    // 若是标准输入输出则直接写入不发送请求
    } else {
        let buf_ptr = buf_ptr as *const u8;
        let buf = unsafe { core::slice::from_raw_parts(buf_ptr, buf_len) };
        let read_size = file.write(buf);
        (read_size, 0)
    }
}

sys_write()sys_open()非常类似,FsReqDescription::Write请求并转发给内核线程。但如果写入的是Stdin即标准输入则直接在内核主线程中完成,没有必要进入内核线程。这是因为标准输入的write()方法只是简单地发送一个字节给串口,并不涉及复杂的底层驱动程序,我们认为它是安全的。

其他内核线程的服务模型与fs线程类似,需要完成

  • 自定义请求

  • 编写请求处理器

  • 添加相关系统调用构造请求

三个步骤