diff --git a/.gitignore b/.gitignore
index 74b654547bb2e2e556c0f5178b2ce84486503e86..ec0d4718f5d0f1731c09f119ffb761a6b0441345 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
 .idea/*
 os/target/*
 os/.idea/*
+os/Cargo.lock
 os/src/link_app.S
 os/Cargo.lock
 user/target/*
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..bbc25b5319f5ef5bdca5544287915fb27f47b79f
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,11 @@
+default:
+  image: wangrunji0408/rcore-lab
+
+stages:
+  - test
+
+test-code-job:
+  stage: test
+  script:
+    - git clone https://gitlab-ci-token:i9tQiwpeUjAtG9Unrx23@git.tsinghua.edu.cn/os-21/rcore_tutorial_tests.git
+    - cd rcore_tutorial_tests && make test CHAPTER=`echo $CI_COMMIT_REF_NAME | grep -oP 'ch\K[0-9]'`
\ No newline at end of file
diff --git a/bootloader/rustsbi-k210.bin b/bootloader/rustsbi-k210.bin
index e696137ab183ea5129e14e86ba980af53ae025d2..58d1c1a7aab97a8253864fa22811a0c8334cb60d 100755
Binary files a/bootloader/rustsbi-k210.bin and b/bootloader/rustsbi-k210.bin differ
diff --git a/bootloader/rustsbi-qemu.bin b/bootloader/rustsbi-qemu.bin
index 5ccab433b2cac6c11e47ae827904839c4275dfa7..f293ced75934d33cb2585f729b62d019c954456f 100755
Binary files a/bootloader/rustsbi-qemu.bin and b/bootloader/rustsbi-qemu.bin differ
diff --git a/easy-fs-fuse/src/main.rs b/easy-fs-fuse/src/main.rs
index 498b61ec843d5c6d14bdf6063f36b775fe1e52c7..0ae7975e85884f967b4b35bbb57e9ed3abeb36a8 100644
--- a/easy-fs-fuse/src/main.rs
+++ b/easy-fs-fuse/src/main.rs
@@ -66,7 +66,7 @@ fn easy_fs_pack() -> std::io::Result<()> {
         1,
     );
     let root_inode = Arc::new(EasyFileSystem::root_inode(&efs));
-    let apps: Vec<_> = read_dir(src_path)
+    let mut apps: Vec<_> = read_dir(src_path)
         .unwrap()
         .into_iter()
         .map(|dir_entry| {
@@ -75,8 +75,11 @@ fn easy_fs_pack() -> std::io::Result<()> {
             name_with_ext
         })
         .collect();
+    apps.retain(|x| x!="");
+    println!("efs...apps number = {}",apps.len());
     for app in apps {
         // load app data from host file system
+        println!("[efs] loading app : {}{}",target_path, app);
         let mut host_file = File::open(format!("{}{}", target_path, app)).unwrap();
         let mut all_data: Vec<u8> = Vec::new();
         host_file.read_to_end(&mut all_data).unwrap();
diff --git a/easy-fs/src/block_cache.rs b/easy-fs/src/block_cache.rs
index 572658122a66707306bc7430ba11f2fa77f42bee..fbc70ecba43d5ed28bc381c874b716c028197796 100644
--- a/easy-fs/src/block_cache.rs
+++ b/easy-fs/src/block_cache.rs
@@ -14,6 +14,7 @@ pub struct BlockCache {
     modified: bool,
 }
 
+//一块BlockCache对应的是一块Block的缓存
 impl BlockCache {
     /// Load a new BlockCache from disk.
     pub fn new(
@@ -34,6 +35,8 @@ impl BlockCache {
         &self.cache[offset] as *const _ as usize
     }
 
+    //CPU需要读写磁盘的时候,大概是要调用这些函数的
+    //至于内容怎么真的写进磁盘,是这些函数已经帮忙写好了,似乎我们不用管
     pub fn get_ref<T>(&self, offset: usize) -> &T where T: Sized {
         let type_size = core::mem::size_of::<T>();
         assert!(offset + type_size <= BLOCK_SZ);
@@ -73,6 +76,7 @@ impl Drop for BlockCache {
 
 const BLOCK_CACHE_SIZE: usize = 16;
 
+//这个类型管理了用哪些、不用哪些
 pub struct BlockCacheManager {
     queue: VecDeque<(usize, Arc<Mutex<BlockCache>>)>,
 }
diff --git a/easy-fs/src/console.rs b/easy-fs/src/console.rs
new file mode 100644
index 0000000000000000000000000000000000000000..d7f2f1b5141136e335d1ca6313557305569d2fa0
--- /dev/null
+++ b/easy-fs/src/console.rs
@@ -0,0 +1,141 @@
+use crate::sbi::console_putchar;
+use core::fmt::{self, Write};
+
+// use lazy_static::lazy_static;
+use core::option_env;
+
+struct Stdout;
+
+impl Write for Stdout {
+    fn write_str(&mut self, s: &str) -> fmt::Result {
+        for c in s.chars() {
+            console_putchar(c as usize);
+        }
+        Ok(())
+    }
+}
+
+// /// Add escape sequence to print with color in Linux console
+// macro_rules! with_color {
+//     ($fmt: literal, $color_code: ident) => {
+//         concat!("\x1b[",$color_code,"m",$fmt, "\x1b[0m")
+//     };
+// }
+//================basics function===================
+
+pub fn print(args: fmt::Arguments) {
+    Stdout.write_fmt(args).unwrap();
+}
+
+#[macro_export]
+macro_rules! print {
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        $crate::console::print(format_args!($fmt $(, $($arg)+)?));
+    }
+}
+
+#[macro_export]
+macro_rules! println {
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?));
+    }
+}
+
+#[macro_export]
+macro_rules! fs_println{
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        if let Some(_key) = option_env!("LOG"){
+            $crate::console::print(format_args!(concat!("\x1b[96m[easy-fs] ",$fmt,"\n\x1b[0m") $(, $($arg)+)?));
+        }
+    }
+}
+
+#[allow(unused)]
+pub fn my_log(){
+    let key: Option<&'static str> = option_env!("LOG");
+    println!("In test.....");
+    println!("the secret key might be: {:?}", key);
+}
+
+//================more function===================
+#[macro_export]
+macro_rules! kernel_println{
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        // $crate::console::print(format_args!(concat!("\x1b[31m",$fmt, "\n","\x1b[0m") $(, $($arg)+)?));
+        if let Some(_key) = option_env!("LOG"){
+            $crate::console::print(format_args!(concat!("\x1b[35m[kernel] ",$fmt,"\n\x1b[0m") $(, $($arg)+)?));
+            // _ => {},
+        }
+    }
+}
+
+#[macro_export]
+macro_rules! error{
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        // $crate::console::print(format_args!(concat!("\x1b[31m",$fmt, "\n","\x1b[0m") $(, $($arg)+)?));
+        match option_env!("LOG"){
+            Some("TRACE")|Some("trace") => $crate::console::print(format_args!(concat!("\x1b[31m[ERROR]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            Some("DEBUG")|Some("debug") => $crate::console::print(format_args!(concat!("\x1b[31m[ERROR]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            Some("INFO")|Some("info") => $crate::console::print(format_args!(concat!("\x1b[31m[ERROR]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            Some("WARN")|Some("warn") => $crate::console::print(format_args!(concat!("\x1b[31m[ERROR]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            Some("ERROR")|Some("error") => $crate::console::print(format_args!(concat!("\x1b[31m[ERROR]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            // None => $crate::console::print(format_args!(concat!("\x1b[31m[ERROR]",$fmt,"\n","\x1b[0m") $(, $($arg)+)?)),
+            _ => {},
+        }
+    }
+}
+
+#[macro_export]
+macro_rules! warn{
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        //$crate::console::print(format_args!(concat!("\x1b[93m",$fmt, "\n","\x1b[0m") $(, $($arg)+)?));
+        match option_env!("LOG"){
+            Some("INFO")|Some("info") => $crate::console::print(format_args!(concat!("\x1b[93m[WARN]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            Some("WARN")|Some("warn") => $crate::console::print(format_args!(concat!("\x1b[93m[WARN]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            Some("DEBUG")|Some("debug") => $crate::console::print(format_args!(concat!("\x1b[93m[WARN]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            Some("TRACE")|Some("trace") => $crate::console::print(format_args!(concat!("\x1b[93m[WARN]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            // None => $crate::console::print(format_args!(concat!("\x1b[93m[WARN]",$fmt,"\n","\x1b[0m") $(, $($arg)+)?)),
+            _ => {},
+        }
+    }
+}
+
+#[macro_export]
+macro_rules! info{
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        //如果LOG等级>=INFO执行以下这一句
+        // let key: Option<&'static str> = option_env!("LOG");
+        // $crate::console::print(format_args!(concat!("\x1b[34m",$fmt, "\n","\x1b[0m") $(, $($arg)+)?));
+        //否则什么也不做
+        match option_env!("LOG"){
+            Some("TRACE")|Some("trace")|Some("Trace") => $crate::console::print(format_args!(concat!("\x1b[34m[INFO]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            Some("DEBUG")|Some("debug")|Some("Debug") => $crate::console::print(format_args!(concat!("\x1b[34m[INFO]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            Some("INFO")|Some("info")|Some("Info") => $crate::console::print(format_args!(concat!("\x1b[34m[INFO]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            // None => $crate::console::print(format_args!(concat!("\x1b[34m[INFO]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            _ => {},
+        }
+    }
+}
+
+#[macro_export]
+macro_rules! debug{
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        //$crate::console::print(format_args!(concat!("\x1b[32m",$fmt, "\n","\x1b[0m") $(, $($arg)+)?));
+        match option_env!("LOG"){
+            Some("DEBUG")|Some("debug") => $crate::console::print(format_args!(concat!("\x1b[32m[DEBUG]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            Some("TRACE")|Some("trace") => $crate::console::print(format_args!(concat!("\x1b[32m[DEBUG]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            _ => {},
+        }
+    }
+}
+
+#[macro_export]
+macro_rules! trace{
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        // $crate::console::print(format_args!(concat!("\x1b[90m",$fmt, "\n","\x1b[0m") $(, $($arg)+)?));
+        match option_env!("LOG"){
+            Some("TRACE")|Some("trace") => $crate::console::print(format_args!(concat!("\x1b[90m[TRACE]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            _ => {},
+        }
+    }
+}
diff --git a/easy-fs/src/efs.rs b/easy-fs/src/efs.rs
index 9e4445df5f7488eb4117934a19f6ab38c8771691..9db05eb4f31baced5c71ab9551418c6111f755d4 100644
--- a/easy-fs/src/efs.rs
+++ b/easy-fs/src/efs.rs
@@ -11,6 +11,8 @@ use super::{
 };
 use crate::BLOCK_SZ;
 
+//从这一层开始,所有的数据结构都放在内存上
+//问题:下面几层的数据结构,怎么让它们放在磁盘上?Orz
 pub struct EasyFileSystem {
     pub block_device: Arc<dyn BlockDevice>,
     pub inode_bitmap: Bitmap,
@@ -85,6 +87,7 @@ impl EasyFileSystem {
         Arc::new(Mutex::new(efs))
     }
 
+    //只要把编号为0的超级块读入
     pub fn open(block_device: Arc<dyn BlockDevice>) -> Arc<Mutex<Self>> {
         // read SuperBlock
         get_block_cache(0, Arc::clone(&block_device))
@@ -125,6 +128,8 @@ impl EasyFileSystem {
     }
     */
 
+    //这就真的开始给分配
+    //inode从磁盘上分配出的编号得知它们在磁盘上的实际位置
     pub fn get_disk_inode_pos(&self, inode_id: u32) -> (u32, usize) {
         let inode_size = core::mem::size_of::<DiskInode>();
         let inodes_per_block = (BLOCK_SZ / inode_size) as u32;
diff --git a/easy-fs/src/layout.rs b/easy-fs/src/layout.rs
index 7d14de467ced1f489aad53f599006bc1a6a4ee42..e66d4e2782552e5d08e2aa54a0a49ee5404c0811 100644
--- a/easy-fs/src/layout.rs
+++ b/easy-fs/src/layout.rs
@@ -71,8 +71,9 @@ pub enum DiskInodeType {
 type IndirectBlock = [u32; BLOCK_SZ / 4];
 type DataBlock = [u8; BLOCK_SZ];
 
+//需要保证总大小是128个字节
+//后面需要增加其他类型数据的时候,减少直接索引的大小就可以了哦
 #[repr(C)]
-/// Only support level-1 indirect now, **indirect2** field is always 0.
 pub struct DiskInode {
     pub size: u32,
     pub direct: [u32; INODE_DIRECT_COUNT],
@@ -81,8 +82,13 @@ pub struct DiskInode {
     type_: DiskInodeType,
 }
 
+//DiskInode的功能大概就类似一个目录(?)
+//反正似乎是把inode-id输入,就可以给出blockid的输出
+//然后拿着这个blockid操作磁盘就可以了
+//这个文件的意思就是,我可以保存这么多个数据块哦
 impl DiskInode {
     /// indirect1 and indirect2 block are allocated only when they are needed.
+    /// 初始化为文件或者目录
     pub fn initialize(&mut self, type_: DiskInodeType) {
         self.size = 0;
         self.direct.iter_mut().for_each(|v| *v = 0);
@@ -124,6 +130,12 @@ impl DiskInode {
         assert!(new_size >= self.size);
         Self::total_blocks(new_size) - Self::total_blocks(self.size)
     }
+    //每一个文件对应一个DiskInode。
+    //inner-id的含义是,我要取出这个文件中的第几个数据块
+    //也就是说第inner-id个数据块到底放在磁盘上的哪个block中
+    //到底是磁盘上的哪个数据块,存储了我这个inode的信息。
+    //我这个inode节点存储的信息就是文件的索引,
+    //文件的索引中可以找到真实存放文件的数据的磁盘块地址
     pub fn get_block_id(&self, inner_id: u32, block_device: &Arc<dyn BlockDevice>) -> u32 {
         let inner_id = inner_id as usize;
         if inner_id < INODE_DIRECT_COUNT {
@@ -237,13 +249,20 @@ impl DiskInode {
     }
     
     /*
+
+    /// Clear size to zero and return blocks that should be deallocated.
+    ///
+    /// We will clear the block contents to zero later.
     pub fn clear_size(&mut self, block_device: &Arc<dyn BlockDevice>) -> Vec<u32> {
         let mut v: Vec<u32> = Vec::new();
-        let blocks = self.blocks() as usize;
+        let mut data_blocks = self.data_blocks() as usize;
         self.size = 0;
-        for i in 0..blocks.min(INODE_DIRECT_COUNT) {
-            v.push(self.direct[i]);
-            self.direct[i] = 0;
+        let mut current_blocks = 0usize;
+        // direct
+        while current_blocks < data_blocks.min(INODE_DIRECT_COUNT) {
+            v.push(self.direct[current_blocks]);
+            self.direct[current_blocks] = 0;
+            current_blocks += 1;
         }
         if blocks > INODE_DIRECT_COUNT {
             get_block_cache(
@@ -257,7 +276,79 @@ impl DiskInode {
                     indirect_block[i] = 0;
                 }
             });
+        // indirect1 block
+        if data_blocks > INODE_DIRECT_COUNT {
+            v.push(self.indirect1);
+            data_blocks -= INODE_DIRECT_COUNT;
+            current_blocks = 0;
+        } else {
+            return v;
+        }
+        // indirect1
+        get_block_cache(
+            self.indirect1 as usize,
+            Arc::clone(block_device),
+        )
+        .lock()
+        .modify(0, |indirect1: &mut IndirectBlock| {
+            while current_blocks < data_blocks.min(INODE_INDIRECT1_COUNT) {
+                v.push(indirect1[current_blocks]);
+                //indirect1[current_blocks] = 0;
+                current_blocks += 1;
+            }
+        });
+        self.indirect1 = 0;
+        // indirect2 block
+        if data_blocks > INODE_INDIRECT1_COUNT {
+            v.push(self.indirect2);
+            data_blocks -= INODE_INDIRECT1_COUNT;
+        } else {
+            return v;
         }
+        // indirect2
+        assert!(data_blocks <= INODE_INDIRECT2_COUNT);
+        let a1 = data_blocks / INODE_INDIRECT1_COUNT;
+        let b1 = data_blocks % INODE_INDIRECT1_COUNT;
+        get_block_cache(
+            self.indirect2 as usize,
+            Arc::clone(block_device),
+        )
+        .lock()
+        .modify(0, |indirect2: &mut IndirectBlock| {
+            // full indirect1 blocks
+            for i in 0..a1 {
+                v.push(indirect2[i]);
+                get_block_cache(
+                    indirect2[i] as usize,
+                    Arc::clone(block_device),
+                )
+                .lock()
+                .modify(0, |indirect1: &mut IndirectBlock| {
+                    for j in 0..INODE_INDIRECT1_COUNT {
+                        v.push(indirect1[j]);
+                        //indirect1[j] = 0;
+                    }
+                });
+                //indirect2[i] = 0;
+            }
+            // last indirect1 block
+            if b1 > 0 {
+                v.push(indirect2[a1]);
+                get_block_cache(
+                    indirect2[a1] as usize,
+                    Arc::clone(block_device),
+                )
+                .lock()
+                .modify(0, |indirect1: &mut IndirectBlock| {
+                    for j in 0..b1 {
+                        v.push(indirect1[j]);
+                        //indirect1[j] = 0;
+                    }
+                });
+                //indirect2[a1] = 0;
+            }
+        });
+        self.indirect2 = 0;
         v
     }
     */
@@ -426,6 +517,14 @@ impl DiskInode {
     }
 }
 
+//DirEntry的含义是,对于磁盘中的每一个目录,里面都放了许多的文件哦
+//一个目录项表示其中一个文件的信息:name+inode_number
+//一个目录就实现为很多个目录项的组合
+//目录项说的应该是这个
+//事实上只要把目录项的inode-number修改过去就可以了
+//对于目录项来说,实际上就是在根目录下面新增加了一个DirEntry
+//这个DirEntry的name可能是新的,inode也是新分配的inode
+//但是block-id是和之前的一致的
 #[repr(C)]
 pub struct DirEntry {
     name: [u8; NAME_LENGTH_LIMIT + 1],
@@ -434,10 +533,13 @@ pub struct DirEntry {
 
 pub const DIRENT_SZ: usize = 32;
 
-//pub type DirentBlock = [DirEntry; BLOCK_SZ / DIRENT_SZ];
-pub type DirentBytes = [u8; DIRENT_SZ];
-
 impl DirEntry {
+    pub fn empty() -> Self {
+        Self {
+            name: [0u8; NAME_LENGTH_LIMIT + 1],
+            inode_number: 0,
+        }
+    }
     pub fn new(name: &str, inode_number: u32) -> Self {
         let mut bytes = [0u8; NAME_LENGTH_LIMIT + 1];
         &mut bytes[..name.len()].copy_from_slice(name.as_bytes());
@@ -446,18 +548,20 @@ impl DirEntry {
             inode_number,
         }
     }
-    pub fn into_bytes(&self) -> &DirentBytes {
+    pub fn as_bytes(&self) -> &[u8] {
         unsafe {
-            &*(self as *const Self as usize as *const DirentBytes)
+            core::slice::from_raw_parts(
+                self as *const _ as usize as *const u8,
+                DIRENT_SZ,
+            )
         }
     }
-    pub fn from_bytes(bytes: &DirentBytes) -> &Self {
-        unsafe { &*(bytes.as_ptr() as usize as *const Self) }
-    }
-    #[allow(unused)]
-    pub fn from_bytes_mut(bytes: &mut DirentBytes) -> &mut Self {
+    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
         unsafe {
-            &mut *(bytes.as_mut_ptr() as usize as *mut Self)
+            core::slice::from_raw_parts_mut(
+                self as *mut _ as usize as *mut u8,
+                DIRENT_SZ,
+            )
         }
     }
     pub fn name(&self) -> &str {
diff --git a/easy-fs/src/lib.rs b/easy-fs/src/lib.rs
index 10a5af76ef287785b3627f7a1f0b6fabc5166c6e..3b444a2843a747143f6c5dd561b299ac5e043d87 100644
--- a/easy-fs/src/lib.rs
+++ b/easy-fs/src/lib.rs
@@ -1,7 +1,12 @@
 #![no_std]
+#![feature(global_asm)]
+#![feature(llvm_asm)]
 
 extern crate alloc;
 
+#[macro_use]
+mod console;
+mod sbi;
 mod block_dev;
 mod layout;
 mod efs;
diff --git a/easy-fs/src/sbi.rs b/easy-fs/src/sbi.rs
new file mode 100644
index 0000000000000000000000000000000000000000..abeeb7e1571645e00e375fb18ea0765320f5f24a
--- /dev/null
+++ b/easy-fs/src/sbi.rs
@@ -0,0 +1,44 @@
+#![allow(unused)]
+#![feature(global_asm)]
+#![feature(llvm_asm)]
+
+const SBI_SET_TIMER: usize = 0;
+const SBI_CONSOLE_PUTCHAR: usize = 1;
+const SBI_CONSOLE_GETCHAR: usize = 2;
+const SBI_CLEAR_IPI: usize = 3;
+const SBI_SEND_IPI: usize = 4;
+const SBI_REMOTE_FENCE_I: usize = 5;
+const SBI_REMOTE_SFENCE_VMA: usize = 6;
+const SBI_REMOTE_SFENCE_VMA_ASID: usize = 7;
+const SBI_SHUTDOWN: usize = 8;
+
+#[inline(always)]
+fn sbi_call(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
+    let mut ret=0;
+    // unsafe {
+    //     llvm_asm!("ecall"
+    //         : "={x10}" (ret)
+    //         : "{x10}" (arg0), "{x11}" (arg1), "{x12}" (arg2), "{x17}" (which)
+    //         : "memory"
+    //         : "volatile"
+    //     );
+    // }
+    ret
+}
+
+pub fn set_timer(timer: usize) {
+    sbi_call(SBI_SET_TIMER, timer, 0, 0);
+}
+
+pub fn console_putchar(c: usize) {
+    sbi_call(SBI_CONSOLE_PUTCHAR, c, 0, 0);
+}
+
+pub fn console_getchar() -> usize {
+    sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0)
+}
+
+pub fn shutdown() -> ! {
+    sbi_call(SBI_SHUTDOWN, 0, 0, 0);
+    panic!("It should shutdown!");
+}
diff --git a/easy-fs/src/vfs.rs b/easy-fs/src/vfs.rs
index f10a8c8a39415dfdf2d559b06a745c9fb356ebaa..4b2e44a80d4152d45575de1ec715480ad9c39e65 100644
--- a/easy-fs/src/vfs.rs
+++ b/easy-fs/src/vfs.rs
@@ -1,9 +1,9 @@
+
 use super::{
     BlockDevice,
     DiskInode,
     DiskInodeType,
     DirEntry,
-    DirentBytes,
     EasyFileSystem,
     DIRENT_SZ,
     get_block_cache,
@@ -12,15 +12,22 @@ use alloc::sync::Arc;
 use alloc::string::String;
 use alloc::vec::Vec;
 use spin::{Mutex, MutexGuard};
+// use super::lib::*;
 
+//注意,这个数据结构是放在内存里面的
+//某种意义上说可以随便修改······
 pub struct Inode {
-    block_id: usize,
+    //新增.我就很想知道为什么这个结构里面不直接存一个inode-id好了???
+    //为什么每次都要这么麻烦···因为访问硬盘很慢很慢!!!的啊!!!!
+    my_inode_id: u32,//还是非常需要的
+    block_id: usize,//表明这个inode存储在磁盘的哪个块上s
     block_offset: usize,
     fs: Arc<Mutex<EasyFileSystem>>,
     block_device: Arc<dyn BlockDevice>,
 }
 
 impl Inode {
+    //==============================================
     pub fn new(
         inode_id: u32,
         fs: Arc<Mutex<EasyFileSystem>>,
@@ -28,6 +35,7 @@ impl Inode {
     ) -> Self {
         let (block_id, block_offset) = fs.lock().get_disk_inode_pos(inode_id);
         Self {
+            my_inode_id: inode_id,//new
             block_id: block_id as usize,
             block_offset,
             fs,
@@ -55,25 +63,35 @@ impl Inode {
     }
     */
 
+    //这个返回的大概就是stat想要的inode编号吧
+    //这个函数调用的时候就disk_inode使用根目录就可以了
+    //给定一个DiskInode,也就是一个文件,而且要假设它是一个目录
+    //遍历这个目录下面的所有文件
+    //如果发现那个文件和自己想要找的文件名一样
+    //那么就返回那个文件的inode_id
+    //看起来这就是需要返回的ino了
+    //已经完全搞懂这是在做什么了,就是把一个文件夹下面的文件都遍历一遍
+    //有了inode-id,就可以从超级块里面访问文件了
     fn find_inode_id(
         &self,
         name: &str,
         disk_inode: &DiskInode,
     ) -> Option<u32> {
         // assert it is a directory
+        fs_println!("find_inode_id::disk_node is dir...{}",disk_inode.is_dir());
         assert!(disk_inode.is_dir());
         let file_count = (disk_inode.size as usize) / DIRENT_SZ;
-        let mut dirent_space: DirentBytes = Default::default();
+        let mut dirent = DirEntry::empty();
         for i in 0..file_count {
             assert_eq!(
                 disk_inode.read_at(
                     DIRENT_SZ * i,
-                    &mut dirent_space,
+                    dirent.as_bytes_mut(),
                     &self.block_device,
                 ),
                 DIRENT_SZ,
             );
-            let dirent = DirEntry::from_bytes(&dirent_space);
+            fs_println!("find_inode_id::dirent name is {}, inode number is {}",dirent.name(),dirent.inode_number());
             if dirent.name() == name {
                 return Some(dirent.inode_number() as u32);
             }
@@ -81,6 +99,48 @@ impl Inode {
         None
     }
 
+    //也限制只有根目录可以调用好了
+    pub fn get_inode_id(&self, name: &str) -> Option<u32>{
+        let _ = self.fs.lock();
+        self.read_disk_inode(|disk_inode| {
+            self.find_inode_id(name, disk_inode)
+        })
+    }
+
+    pub fn get_my_inode_id(&self) ->Option<u32>{
+        return Some(self.my_inode_id)
+    }
+
+    //已知有一个inode类型
+    //希望知道我代表的文件的inode是多少号
+    pub fn get_my_data(&self) ->Option<u32>{
+        let _ = self.fs.lock();
+        self.read_disk_inode(|disk_inode| {
+            fs_println!("Inode::get_my_data::disk_node is dir...{}",disk_inode.is_dir());
+            let file_count = (disk_inode.size as usize) / DIRENT_SZ;
+            let mut dirent = DirEntry::empty();
+            for i in 0..file_count {
+                assert_eq!(
+                    disk_inode.read_at(
+                        DIRENT_SZ * i,
+                        dirent.as_bytes_mut(),
+                        &self.block_device,
+                    ),
+                    DIRENT_SZ,
+                );
+                //TODO这里要修改输出,能够返回文件名鸭
+                // fs_println!("get_my_data::dirent name is {}, inode number is {}",dirent.name(),dirent.inode_number());
+                if dirent.inode_number() == self.my_inode_id {
+                    // return Some((dirent.inode_number() as u32, dirent.name()));
+                    fs_println!("get_my_data::dirent name is {}, inode number is {},but return None",dirent.name(),dirent.inode_number());
+                    return None;
+                }
+            }
+            return None;
+        })
+    }
+
+    //刚刚意识到哪里有问题!其实存inode编号就可以了?
     pub fn find(&self, name: &str) -> Option<Arc<Inode>> {
         let _ = self.fs.lock();
         self.read_disk_inode(|disk_inode| {
@@ -144,7 +204,7 @@ impl Inode {
             let dirent = DirEntry::new(name, new_inode_id);
             root_inode.write_at(
                 file_count * DIRENT_SZ,
-                dirent.into_bytes(),
+                dirent.as_bytes(),
                 &self.block_device,
             );
         });
@@ -158,22 +218,271 @@ impl Inode {
         )))
     }
 
+    //这个函数只允许ROOT调用
+    //一旦建立了linker,根目录下面的两个DirEntry的地位就是差不多的
+    pub fn create_linker(&self, name: &str, old_name: &str) -> Option<Arc<Inode>> {
+        let mut fs = self.fs.lock();
+
+        //先检查原来的文件是否存在
+        if self.modify_disk_inode(|root_inode| {
+            // assert it is a directory
+            assert!(root_inode.is_dir());
+            // has the file been created?
+            self.find_inode_id(old_name, root_inode)
+        }).is_none() {
+            return None;
+        }
+
+        //就不用新建inode了
+        //先获取旧的文件名和inode-id
+        //再新建目录项就可以了
+        let mut inode_id = 0;
+        //新建一个目录项
+        self.modify_disk_inode(|root_inode| {
+            // append file in the dirent
+            let file_count = (root_inode.size as usize) / DIRENT_SZ;
+            let new_size = (file_count + 1) * DIRENT_SZ;
+            // increase size
+            self.increase_size(new_size as u32, root_inode, &mut fs);
+            // write dirent
+            let inode_id = self.find_inode_id(old_name, root_inode).unwrap();//因为前面已经检查过合法性了
+            //所以这个inode-id一定是合法的
+            let dirent = DirEntry::new(name, inode_id);
+            root_inode.write_at(
+                file_count * DIRENT_SZ,
+                dirent.as_bytes(),
+                &self.block_device,
+            );
+        });
+        // release efs lock manually because we will acquire it again in Inode::new
+        drop(fs);
+        // return inode
+        Some(Arc::new(Self::new(
+            inode_id,
+            self.fs.clone(),
+            self.block_device.clone(),
+        )))
+    }
+
+    //这个函数也只允许ROOT调用
+    //这要怎么写······不如我改一下好了,比如说增加一个valid位?
+    //todo:fix return type, Option<bool>
+    pub fn delete_linker(&self, name: &str) -> bool{
+        let fs = self.fs.lock();
+
+        //先检查想要unlink的文件是否存在
+        if self.modify_disk_inode(|root_inode| {
+            // assert it is a directory
+            assert!(root_inode.is_dir());
+            // has the file been created?
+            self.find_inode_id(name, root_inode)
+        }).is_none() {
+            return false;
+        }
+
+        //就是不用考虑只剩下这一个DirEntry到文件还有link的情况,直接删除即可
+        //把那个Entry设置成不合法即可
+        let mut result = false;
+        //新建一个目录项
+        self.modify_disk_inode(|root_inode| {
+            // append file in the dirent
+            let file_count = (root_inode.size as usize) / DIRENT_SZ;
+            let mut dirent = DirEntry::empty();
+            for i in 0..file_count {
+                assert_eq!(
+                    root_inode.read_at(
+                        DIRENT_SZ * i,
+                        dirent.as_bytes_mut(),
+                        &self.block_device,
+                    ),
+                    DIRENT_SZ,
+                );
+                //TODO这里要修改输出,能够返回文件名鸭
+                // fs_println!("get_my_data::dirent name is {}, inode number is {}",dirent.name(),dirent.inode_number());
+                if dirent.name() == name {
+                    fs_println!("get_my_data::dirent name is {}, inode number is {},but return None",
+                        dirent.name(),dirent.inode_number());
+                    //如果找到了,那么就把空的写进去
+                    //就是用一个不合法的内容替换的意思
+                    let mut dirent_unlink = DirEntry::empty();
+                    assert_eq!(
+                        root_inode.write_at(
+                            i * DIRENT_SZ,
+                            dirent_unlink.as_bytes(),
+                            &self.block_device,
+                        ),
+                        DIRENT_SZ,
+                    );
+                    result = true;
+                }
+            }
+        });
+        // release efs lock manually because we will acquire it again in Inode::new
+        drop(fs);
+        // return nothing
+        return result;
+    }
+
+    //只有根目录可以调用
+    pub fn count_files(&self, name: &str) -> Option<usize>{
+        let fs = self.fs.lock();
+
+        //先检查想要计数的文件是否存在
+        if self.modify_disk_inode(|root_inode| {
+            // assert it is a directory
+            assert!(root_inode.is_dir());
+            // has the file been created?
+            self.find_inode_id(name, root_inode)
+        }).is_none() {
+            return None;
+        }
+
+        //
+        let mut counter = 0 as usize;
+
+        //就是不用考虑只剩下这一个DirEntry到文件还有link的情况,直接删除即可
+        //把那个Entry设置成不合法即可
+        let mut inode_id = 0;
+        //新建一个目录项
+        self.modify_disk_inode(|root_inode| {
+            // append file in the dirent
+            let file_count = (root_inode.size as usize) / DIRENT_SZ;
+            let mut dirent = DirEntry::empty();
+            for i in 0..file_count {
+                assert_eq!(
+                    root_inode.read_at(
+                        DIRENT_SZ * i,
+                        dirent.as_bytes_mut(),
+                        &self.block_device,
+                    ),
+                    DIRENT_SZ,
+                );
+                //得到了自己的inode-number
+                if dirent.name() == name {
+                    fs_println!("count_files::get_inode_id::dirent name is {}, inode number is {},but return None",
+                        dirent.name(),dirent.inode_number());
+                    //如果找到了,那么就把空的写进去
+                    //就是用一个不合法的内容替换的意思
+                    inode_id = dirent.inode_number();
+                    break;
+                }
+            }
+
+            //下面是计数的内容
+            for i in 0..file_count {
+                assert_eq!(
+                    root_inode.read_at(
+                        DIRENT_SZ * i,
+                        dirent.as_bytes_mut(),
+                        &self.block_device,
+                    ),
+                    DIRENT_SZ,
+                );
+                //得到了自己的inode-number
+                if dirent.inode_number() == inode_id {
+                    fs_println!("count_files::counting::dirent name is {}, inode number is {},but return None",
+                        dirent.name(),dirent.inode_number());
+                    //如果找到了,那么就把空的写进去
+                    //就是用一个不合法的内容替换的意思
+                    counter = counter + 1;
+                }
+            }
+        });
+        // release efs lock manually because we will acquire it again in Inode::new
+        drop(fs);
+        // return nothing
+        return Some(counter);
+    }
+
+    //只有根目录可以调用
+    pub fn count_files_from_me(&self) -> Option<usize>{
+        let fs = self.fs.lock();
+
+        let mut counter = 0 as usize;
+        //新建一个目录项
+        self.modify_disk_inode(|root_inode| {
+            // append file in the dirent
+            let file_count = (root_inode.size as usize) / DIRENT_SZ;
+            let mut dirent = DirEntry::empty();
+            //下面是计数的内容
+            for i in 0..file_count {
+                assert_eq!(
+                    root_inode.read_at(
+                        DIRENT_SZ * i,
+                        dirent.as_bytes_mut(),
+                        &self.block_device,
+                    ),
+                    DIRENT_SZ,
+                );
+                //得到了自己的inode-number
+                if dirent.inode_number() == self.my_inode_id {
+                    fs_println!("count_files::counting::dirent name is {}, inode number is {},",
+                        dirent.name(),dirent.inode_number());
+                    //如果找到了,那么就把空的写进去
+                    //就是用一个不合法的内容替换的意思
+                    counter = counter + 1;
+                }
+            }
+        });
+        // release efs lock manually because we will acquire it again in Inode::new
+        drop(fs);
+        // return nothing
+        return Some(counter);
+    }
+
+    //只有根目录可以调用
+    pub fn count_files_from_id(&self,id:u32) -> Option<usize>{
+        let fs = self.fs.lock();
+
+        let mut counter = 0 as usize;
+        //新建一个目录项
+        self.modify_disk_inode(|root_inode| {
+            // append file in the dirent
+            let file_count = (root_inode.size as usize) / DIRENT_SZ;
+            let mut dirent = DirEntry::empty();
+            //下面是计数的内容
+            for i in 0..file_count {
+                assert_eq!(
+                    root_inode.read_at(
+                        DIRENT_SZ * i,
+                        dirent.as_bytes_mut(),
+                        &self.block_device,
+                    ),
+                    DIRENT_SZ,
+                );
+                //得到了自己的inode-number
+                if dirent.inode_number() == id {
+                    fs_println!("count_files::counting::dirent name is {}, inode number is {},",
+                        dirent.name(),dirent.inode_number());
+                    //如果找到了,那么就把空的写进去
+                    //就是用一个不合法的内容替换的意思
+                    counter = counter + 1;
+                }
+            }
+        });
+        // release efs lock manually because we will acquire it again in Inode::new
+        drop(fs);
+        // return nothing
+        return Some(counter);
+    }
+
+
     pub fn ls(&self) -> Vec<String> {
         let _ = self.fs.lock();
         self.read_disk_inode(|disk_inode| {
             let file_count = (disk_inode.size as usize) / DIRENT_SZ;
             let mut v: Vec<String> = Vec::new();
             for i in 0..file_count {
-                let mut dirent_bytes: DirentBytes = Default::default();
+                let mut dirent = DirEntry::empty();
                 assert_eq!(
                     disk_inode.read_at(
                         i * DIRENT_SZ,
-                        &mut dirent_bytes,
+                        dirent.as_bytes_mut(),
                         &self.block_device,
                     ),
                     DIRENT_SZ,
                 );
-                v.push(String::from(DirEntry::from_bytes(&dirent_bytes).name()));
+                v.push(String::from(dirent.name()));
             }
             v
         })
diff --git a/os/.cargo/config b/os/.cargo/config
index 4275fcad6f8a3605fed9c98a841a8de4ef5e94a9..a3fa674332e971e07bdb0adb57bf42bdf31b075a 100644
--- a/os/.cargo/config
+++ b/os/.cargo/config
@@ -5,3 +5,8 @@ target = "riscv64gc-unknown-none-elf"
 rustflags = [
     "-Clink-arg=-Tsrc/linker.ld", "-Cforce-frame-pointers=yes"
 ]
+
+[source.crates-io]
+replace-with = 'tuna'
+[source.tuna]
+registry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git"
diff --git a/os/Cargo.toml b/os/Cargo.toml
index bded64091260e8a867c49f072a9ae21e563cf809..e8a62369744b72dc02442c2204ce62017e06cd9b 100644
--- a/os/Cargo.toml
+++ b/os/Cargo.toml
@@ -8,6 +8,7 @@ edition = "2018"
 
 [dependencies]
 riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] }
+# riscv = { git = "https://gitee.com/chyyuu/riscv", features = ["inline-asm"] }
 lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
 buddy_system_allocator = "0.6"
 spin = "0.7.0"
@@ -17,8 +18,16 @@ virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers" }
 k210-pac = { git = "https://github.com/wyfcyx/k210-pac" }
 k210-hal = { git = "https://github.com/wyfcyx/k210-hal" }
 k210-soc = { git = "https://github.com/wyfcyx/k210-soc" }
+#k210-pac = { git = "https://gitee.com/chyyuu/k210-pac" }
+#k210-hal = { git = "https://gitee.com/chyyuu/k210-hal" }
+#k210-soc = { git = "https://gitee.com/chyyuu/k210-soc" }
 easy-fs = { path = "../easy-fs" }
+log = "0.4"
+
+# rvm
+# rvm = { git = "https://github.com/rcore-os/RVM", rev = "939eb0a", optional = true }
+#rvm = { git = "https://github.com/chenzm-plusplus/RVM", rev = "382fc60", optional = true }
 
 [features]
 board_qemu = []
-board_k210 = []
\ No newline at end of file
+board_k210 = []
diff --git a/os/Makefile b/os/Makefile
index 4d86c12477953c68ab3e23c21d5bb18407a340a4..f6285a488ca91151e941008f654d405000282443 100644
--- a/os/Makefile
+++ b/os/Makefile
@@ -71,10 +71,9 @@ disasm-vim: kernel
 	@vim $(DISASM_TMP)
 	@rm $(DISASM_TMP)
 
-run: tools run-inner
+run: run-inner
 
-tools:
-	(which $(K210-BURNER)) || (cd .. && git clone https://github.com/sipeed/kflash.py.git && mv kflash.py tools)
+	
 
 run-inner: build
 ifeq ($(BOARD),qemu)
@@ -86,6 +85,7 @@ ifeq ($(BOARD),qemu)
 		-drive file=$(FS_IMG),if=none,format=raw,id=x0 \
         -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0
 else
+	(which $(K210-BURNER)) || (cd .. && git clone https://github.com/sipeed/kflash.py.git && mv kflash.py tools)
 	@cp $(BOOTLOADER) $(BOOTLOADER).copy
 	@dd if=$(KERNEL_BIN) of=$(BOOTLOADER).copy bs=$(K210_BOOTLOADER_SIZE) seek=1
 	@mv $(BOOTLOADER).copy $(KERNEL_BIN)
@@ -100,4 +100,4 @@ debug: build
 		tmux split-window -h "riscv64-unknown-elf-gdb -ex 'file $(KERNEL_ELF)' -ex 'set arch riscv:rv64' -ex 'target remote localhost:1234'" && \
 		tmux -2 attach-session -d
 
-.PHONY: build env kernel clean disasm disasm-vim run-inner tools
+.PHONY: build env kernel clean disasm disasm-vim run-inner
\ No newline at end of file
diff --git a/os/src/config.rs b/os/src/config.rs
index 0633c4da9b255dd90784b1275822def127f0fc8c..46e3e8db12b33b3a9c46c71ff448694eaa48f6f1 100644
--- a/os/src/config.rs
+++ b/os/src/config.rs
@@ -4,12 +4,44 @@ pub const USER_STACK_SIZE: usize = 4096 * 2;
 pub const KERNEL_STACK_SIZE: usize = 4096 * 2;
 pub const KERNEL_HEAP_SIZE: usize = 0x20_0000;
 pub const MEMORY_END: usize = 0x80800000;
+
+// #[cfg(feature = "board_k210")]
+// pub const MEMORY_END: usize = 0x80600000;
+
+// #[cfg(feature = "board_qemu")]
+// pub const MEMORY_END: usize = 0x80800000;
+
 pub const PAGE_SIZE: usize = 0x1000;
 pub const PAGE_SIZE_BITS: usize = 0xc;
 
 pub const TRAMPOLINE: usize = usize::MAX - PAGE_SIZE + 1;
 pub const TRAP_CONTEXT: usize = TRAMPOLINE - PAGE_SIZE;
 
+pub const TASK_PRIORITY_INIT: usize = 16;
+//===============
+// pub const PAGE_SIZE: usize = 0x1000;//16进制的100就是4096个字节
+// pub const PAGE_SIZE_BITS: usize = 0xc;//表示我要用多少位的数表示一个page,也就是page offset的位数。12位
+pub const MEMORY_MAP_SIZE: usize = 0x4_000_000;//表示进行一次内存映射最多映射多少字节
+
+// pub const TRAMPOLINE: usize = usize::MAX - PAGE_SIZE + 1;
+// pub const TRAP_CONTEXT: usize = TRAMPOLINE - PAGE_SIZE;
+// /// Return (bottom, top) of a kernel stack in kernel space.
+// pub fn kernel_stack_position(app_id: usize) -> (usize, usize) {
+//     let top = TRAMPOLINE - app_id * (KERNEL_STACK_SIZE + PAGE_SIZE);//体现在这里,两个相邻内核栈之间会预留一个保护page
+//     let bottom = top - KERNEL_STACK_SIZE;
+//     (bottom, top)
+// }
+pub const ISIZI_MAX: isize = isize::MAX;
+// // pub const BIG_STRIDE: usize = 1024;//一个预先定义的大常数···多大好呢?就这么大好了
+pub const BIG_STRIDE: usize = 4096*4096;
+// pub const MAX_RUN_TIME_MS: usize = 600;
+pub const MAIL_SIZE: usize = 256;
+
+
+
+
+//for map
+
 #[cfg(feature = "board_k210")]
 pub const CLOCK_FREQ: usize = 403000000 / 62;
 
@@ -18,7 +50,7 @@ pub const CLOCK_FREQ: usize = 12500000;
 
 #[cfg(feature = "board_qemu")]
 pub const MMIO: &[(usize, usize)] = &[
-    (0x10000000, 0x10000),
+    (0x10001000, 0x1000),
 ];
 
 #[cfg(feature = "board_k210")]
@@ -39,4 +71,4 @@ pub const MMIO: &[(usize, usize)] = &[
     (0x5200_0000, 0x1000),      /* SPI0      */
     (0x5300_0000, 0x1000),      /* SPI1      */
     (0x5400_0000, 0x1000),      /* SPI2      */
-];
\ No newline at end of file
+];
diff --git a/os/src/console.rs b/os/src/console.rs
index 2bd5593046ac358ae6f950486336924a088bfbdc..e46da8cca97c5e491f0dc96e66196bd5157d62f6 100644
--- a/os/src/console.rs
+++ b/os/src/console.rs
@@ -1,5 +1,8 @@
-use core::fmt::{self, Write};
 use crate::sbi::console_putchar;
+use core::fmt::{self, Write};
+
+// use lazy_static::lazy_static;
+use core::option_env;
 
 struct Stdout;
 
@@ -12,6 +15,14 @@ impl Write for Stdout {
     }
 }
 
+// /// Add escape sequence to print with color in Linux console
+// macro_rules! with_color {
+//     ($fmt: literal, $color_code: ident) => {
+//         concat!("\x1b[",$color_code,"m",$fmt, "\x1b[0m")
+//     };
+// }
+//================basics function===================
+
 pub fn print(args: fmt::Arguments) {
     Stdout.write_fmt(args).unwrap();
 }
@@ -30,4 +41,102 @@ macro_rules! println {
     }
 }
 
+#[macro_export]
+macro_rules! fs_println{
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        // $crate::console::print(format_args!(concat!("\x1b[31m",$fmt, "\n","\x1b[0m") $(, $($arg)+)?));
+        if let Some(key) = option_env!("LOG"){
+            $crate::console::print(format_args!(concat!("\x1b[38m[easy-fs] ",$fmt,"\n\x1b[0m") $(, $($arg)+)?));
+        }
+    }
+}
+
+#[allow(unused)]
+pub fn my_log(){
+    let key: Option<&'static str> = option_env!("LOG");
+    println!("In test.....");
+    println!("the secret key might be: {:?}", key);
+}
+
+//================more function===================
+#[macro_export]
+macro_rules! kernel_println{
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        // $crate::console::print(format_args!(concat!("\x1b[31m",$fmt, "\n","\x1b[0m") $(, $($arg)+)?));
+        if let Some(_key) = option_env!("LOG"){
+            $crate::console::print(format_args!(concat!("\x1b[35m[kernel] ",$fmt,"\n\x1b[0m") $(, $($arg)+)?));
+            // _ => {},
+        }
+    }
+}
+
+#[macro_export]
+macro_rules! error{
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        // $crate::console::print(format_args!(concat!("\x1b[31m",$fmt, "\n","\x1b[0m") $(, $($arg)+)?));
+        match option_env!("LOG"){
+            Some("TRACE")|Some("trace") => $crate::console::print(format_args!(concat!("\x1b[31m[ERROR]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            Some("DEBUG")|Some("debug") => $crate::console::print(format_args!(concat!("\x1b[31m[ERROR]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            Some("INFO")|Some("info") => $crate::console::print(format_args!(concat!("\x1b[31m[ERROR]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            Some("WARN")|Some("warn") => $crate::console::print(format_args!(concat!("\x1b[31m[ERROR]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            Some("ERROR")|Some("error") => $crate::console::print(format_args!(concat!("\x1b[31m[ERROR]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            // None => $crate::console::print(format_args!(concat!("\x1b[31m[ERROR]",$fmt,"\n","\x1b[0m") $(, $($arg)+)?)),
+            _ => {},
+        }
+    }
+}
+
+#[macro_export]
+macro_rules! warn{
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        //$crate::console::print(format_args!(concat!("\x1b[93m",$fmt, "\n","\x1b[0m") $(, $($arg)+)?));
+        match option_env!("LOG"){
+            Some("INFO")|Some("info") => $crate::console::print(format_args!(concat!("\x1b[93m[WARN]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            Some("WARN")|Some("warn") => $crate::console::print(format_args!(concat!("\x1b[93m[WARN]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            Some("DEBUG")|Some("debug") => $crate::console::print(format_args!(concat!("\x1b[93m[WARN]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            Some("TRACE")|Some("trace") => $crate::console::print(format_args!(concat!("\x1b[93m[WARN]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            // None => $crate::console::print(format_args!(concat!("\x1b[93m[WARN]",$fmt,"\n","\x1b[0m") $(, $($arg)+)?)),
+            _ => {},
+        }
+    }
+}
+
+#[macro_export]
+macro_rules! info{
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        //如果LOG等级>=INFO执行以下这一句
+        // let key: Option<&'static str> = option_env!("LOG");
+        // $crate::console::print(format_args!(concat!("\x1b[34m",$fmt, "\n","\x1b[0m") $(, $($arg)+)?));
+        //否则什么也不做
+        match option_env!("LOG"){
+            Some("TRACE")|Some("trace")|Some("Trace") => $crate::console::print(format_args!(concat!("\x1b[34m[INFO]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            Some("DEBUG")|Some("debug")|Some("Debug") => $crate::console::print(format_args!(concat!("\x1b[34m[INFO]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            Some("INFO")|Some("info")|Some("Info") => $crate::console::print(format_args!(concat!("\x1b[34m[INFO]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            // None => $crate::console::print(format_args!(concat!("\x1b[34m[INFO]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            _ => {},
+        }
+    }
+}
 
+#[macro_export]
+macro_rules! debug{
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        //$crate::console::print(format_args!(concat!("\x1b[32m",$fmt, "\n","\x1b[0m") $(, $($arg)+)?));
+        match option_env!("LOG"){
+            Some("DEBUG")|Some("debug") => $crate::console::print(format_args!(concat!("\x1b[32m[DEBUG]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            Some("TRACE")|Some("trace") => $crate::console::print(format_args!(concat!("\x1b[32m[DEBUG]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            _ => {},
+        }
+    }
+}
+
+#[macro_export]
+macro_rules! trace{
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        // $crate::console::print(format_args!(concat!("\x1b[90m",$fmt, "\n","\x1b[0m") $(, $($arg)+)?));
+        match option_env!("LOG"){
+            Some("TRACE")|Some("trace") => $crate::console::print(format_args!(concat!("\x1b[90m[TRACE]",$fmt,"\n\x1b[0m") $(, $($arg)+)?)),
+            _ => {},
+        }
+    }
+}
diff --git a/os/src/fs/inode.rs b/os/src/fs/inode.rs
index 04ccb45f7fcf1c9cd613483d4e70886f702aa80c..4d621bc4adff8a911eda4d84f975ffa69a44925d 100644
--- a/os/src/fs/inode.rs
+++ b/os/src/fs/inode.rs
@@ -51,6 +51,20 @@ impl OSInode {
         }
         v
     }
+    pub fn get_my_inode_id(&self) -> Option<u32>{
+        let mut inner = self.inner.lock();
+        return inner.inode.get_my_inode_id();
+    }
+
+    pub fn count_files_from_me(&self) ->Option<usize>{
+        let mut inner = self.inner.lock();
+        return inner.inode.count_files_from_me();
+    }
+
+    pub fn count_files_from_id(&self,id:u32) ->Option<usize>{
+        let mut inner = self.inner.lock();
+        return inner.inode.count_files_from_id(id);
+    }
 }
 
 lazy_static! {
@@ -95,8 +109,10 @@ impl OpenFlags {
 pub fn open_file(name: &str, flags: OpenFlags) -> Option<Arc<OSInode>> {
     let (readable, writable) = flags.read_write();
     if flags.contains(OpenFlags::CREATE) {
+        kernel_println!("[open_file] creating file");
         if let Some(inode) = ROOT_INODE.find(name) {
             // clear size
+            // kernel_println!("[open_file] creating file");
             inode.clear();
             Some(Arc::new(OSInode::new(
                 readable,
@@ -129,9 +145,34 @@ pub fn open_file(name: &str, flags: OpenFlags) -> Option<Arc<OSInode>> {
     }
 }
 
+//perhaps done
+pub fn get_inode_id(name: &str) -> Option<u32>{
+    ROOT_INODE.get_inode_id(name)
+}
+
+// pub fn create(&self, name: &str,old_name: &str) -> Option<Arc<Inode>>
+pub fn create_linker(name:&str,old_name:&str) -> Option<Arc<Inode>>{
+    ROOT_INODE.create_linker(name,old_name)
+}
+
+// pub fn delete_linker(&self, name: &str) -> Option<bool>
+pub fn delete_linker(name: &str) -> bool{
+    ROOT_INODE.delete_linker(name)
+}
+
+// pub fn count_files(&self, name: &str) -> Option<usize>
+pub fn count_files(name:&str) ->Option<usize>{
+    ROOT_INODE.count_files(name)
+}
+
+pub fn count_files_from_id(id: u32) ->Option<usize>{
+    ROOT_INODE.count_files_from_id(id)
+}
+
 impl File for OSInode {
     fn readable(&self) -> bool { self.readable }
     fn writable(&self) -> bool { self.writable }
+    fn inode_id(&self) -> Option<u32> { self.get_my_inode_id() }
     fn read(&self, mut buf: UserBuffer) -> usize {
         let mut inner = self.inner.lock();
         let mut total_read_size = 0usize;
diff --git a/os/src/fs/mail.rs b/os/src/fs/mail.rs
new file mode 100644
index 0000000000000000000000000000000000000000..5a615e1530f75ef5e5ca991b6fbd337cf251d5b8
--- /dev/null
+++ b/os/src/fs/mail.rs
@@ -0,0 +1,252 @@
+//根据测例可以知道,事实上mail要做的事情就是从缓冲区读写,然后存到进程的mail缓冲里面
+//为了实现进程之间通信,还是离不开用户程序自己的控制呀
+use alloc::collections::VecDeque;
+use super::File;
+use alloc::sync::{Arc, Weak};
+use spin::Mutex;
+use crate::mm::{
+    UserBuffer,
+};
+use crate::config::MAIL_SIZE;
+
+#[derive(Copy,Clone,PartialEq)]
+pub struct Mail{
+    fd_read: usize,
+    // fd_write: usize,
+}
+
+impl Mail{
+    pub fn new(read_end:usize)->Self{
+        Self{
+            fd_read: read_end,
+            // fd_write: write_end,
+        }
+    }
+    pub fn get_read_fd(&self)->usize{
+        self.fd_read
+    }
+}
+
+const MAIL_NUMBER_LIMIT:usize = 16;
+
+#[derive(Copy, Clone, PartialEq)]
+enum MailBoxStatus {
+    FULL,
+    EMPTY,
+    NORMAL,
+}
+
+pub struct MailBox{
+    mails: VecDeque<Mail>,
+    limit: usize,
+    status: MailBoxStatus,
+}
+
+impl MailBox{
+    pub fn new() ->Self{
+        Self{
+            mails:VecDeque::new(),//初始化为空
+            limit: MAIL_NUMBER_LIMIT,
+            status: MailBoxStatus::EMPTY,
+        }
+    }
+    pub fn can_add_mail(&self)->bool{
+        self.status != MailBoxStatus::FULL
+    }
+    pub fn can_read_mail(&self)->bool{
+        self.status != MailBoxStatus::EMPTY
+    }
+    pub fn add_mail(&mut self,mail:Mail)->usize{
+        if self.status==MailBoxStatus::FULL{
+            return 0;
+        }
+        self.mails.push_back(mail);
+        if self.mails.len()>=self.limit{
+            self.status=MailBoxStatus::FULL;
+        }
+        self.mails.len()
+    }
+    //VecDeque自己就会返回一个Option类型,所以这里函数的返回值使用Option类就可以类
+    pub fn get_mail(&mut self)->Option<Mail>{
+        let m = self.mails.pop_front();
+        if self.mails.len() < self.limit{
+            self.status=MailBoxStatus::NORMAL;
+        }
+        if self.mails.len() == 0{
+            self.status=MailBoxStatus::EMPTY;
+        }
+        m
+    }
+}
+//=====================================================================================
+
+//比如说我要创建一个MPipe,其实就是新建一个MPipeBuffer,
+//然后读端和写端都包装成一个MPipe,用来和进程之间交互
+
+pub struct MPipe {
+    readable: bool,
+    writable: bool,
+    buffer: Arc<Mutex<MPipeRingBuffer>>,
+}
+
+impl MPipe {
+    pub fn read_end_with_buffer(buffer: Arc<Mutex<MPipeRingBuffer>>) -> Self {
+        Self {
+            readable: true,
+            writable: false,
+            buffer,
+        }
+    }
+    pub fn write_end_with_buffer(buffer: Arc<Mutex<MPipeRingBuffer>>) -> Self {
+        Self {
+            readable: false,
+            writable: true,
+            buffer,
+        }
+    }
+}
+
+// const RING_BUFFER_SIZE: usize = 32;
+// const RING_BUFFER_SIZE: usize = 256;
+const RING_BUFFER_SIZE: usize = MAIL_SIZE;
+
+#[derive(Copy, Clone, PartialEq)]
+enum RingBufferStatus {
+    FULL,
+    EMPTY,
+    NORMAL,
+}
+
+pub struct MPipeRingBuffer {
+    arr: [u8; RING_BUFFER_SIZE],
+    head: usize,
+    tail: usize,
+    status: RingBufferStatus,
+    write_end: Option<Weak<MPipe>>,
+    //一个MPipebuffer要知道谁在写它
+}
+
+impl MPipeRingBuffer {
+    pub fn new() -> Self {
+        Self {
+            arr: [0; RING_BUFFER_SIZE],
+            head: 0,
+            tail: 0,
+            status: RingBufferStatus::EMPTY,
+            write_end: None,
+        }
+    }
+    pub fn set_write_end(&mut self, write_end: &Arc<MPipe>) {
+        self.write_end = Some(Arc::downgrade(write_end));
+    }
+    pub fn write_byte(&mut self, byte: u8) {
+        self.status = RingBufferStatus::NORMAL;
+        self.arr[self.tail] = byte;
+        self.tail = (self.tail + 1) % RING_BUFFER_SIZE;
+        if self.tail == self.head {
+            self.status = RingBufferStatus::FULL;
+        }
+    }
+    pub fn read_byte(&mut self) -> u8 {
+        self.status = RingBufferStatus::NORMAL;
+        let c = self.arr[self.head];
+        self.head = (self.head + 1) % RING_BUFFER_SIZE;
+        if self.head == self.tail {
+            self.status = RingBufferStatus::EMPTY;
+        }
+        c
+    }
+    pub fn available_read(&self) -> usize {
+        if self.status == RingBufferStatus::EMPTY {
+            0
+        } else {
+            if self.tail > self.head {
+                self.tail - self.head
+            } else {
+                self.tail + RING_BUFFER_SIZE - self.head
+            }
+        }
+    }
+    pub fn available_write(&self) -> usize {
+        if self.status == RingBufferStatus::FULL {
+            0
+        } else {
+            RING_BUFFER_SIZE - self.available_read()
+        }
+    }
+    pub fn all_write_ends_closed(&self) -> bool {
+        self.write_end.as_ref().unwrap().upgrade().is_none()
+    }
+}
+
+/// Return (read_end, write_end)
+pub fn make_mpipe() -> (Arc<MPipe>, Arc<MPipe>) {
+    let buffer = Arc::new(Mutex::new(MPipeRingBuffer::new()));
+    let read_end = Arc::new(
+        MPipe::read_end_with_buffer(buffer.clone())
+    );
+    let write_end = Arc::new(
+        MPipe::write_end_with_buffer(buffer.clone())
+    );
+    buffer.lock().set_write_end(&write_end);
+    (read_end, write_end)
+}
+
+impl File for MPipe {
+    fn readable(&self) -> bool { self.readable }
+    fn writable(&self) -> bool { self.writable }
+    fn inode_id(&self) -> Option<u32> { None }
+    fn read(&self, buf: UserBuffer) -> usize {
+        assert_eq!(self.readable, true);
+        let mut buf_iter = buf.into_iter();
+        let mut read_size = 0usize;
+        loop {
+            let mut ring_buffer = self.buffer.lock();
+            let loop_read = ring_buffer.available_read();
+            debug!("MPipe::avalable_read is {}",loop_read);
+            if loop_read == 0 {
+                if ring_buffer.all_write_ends_closed() {
+                    return read_size;
+                }
+                warn!("MPipe::still have bytes...");
+                // suspend_current_and_run_next();
+                // break;
+                return loop_read;
+            }
+            // read at most loop_read bytes
+            for _ in 0..loop_read {
+                if let Some(byte_ref) = buf_iter.next() {
+                    unsafe { *byte_ref = ring_buffer.read_byte(); }
+                    read_size += 1;
+                } else {
+                    return read_size;
+                }
+            }
+        }
+    }
+    fn write(&self, buf: UserBuffer) -> usize {
+        assert_eq!(self.writable, true);
+        let mut buf_iter = buf.into_iter();
+        let mut write_size = 0usize;
+        loop {
+            let mut ring_buffer = self.buffer.lock();
+            let loop_write = ring_buffer.available_write();
+            if loop_write == 0 {
+                drop(ring_buffer);
+                // warn!("MPipe::no space...");
+                // suspend_current_and_run_next();
+                // break;
+                return loop_write;
+            }
+            // write at most loop_write bytes
+            for _ in 0..loop_write {
+                if let Some(byte_ref) = buf_iter.next() {
+                    ring_buffer.write_byte(unsafe { *byte_ref });
+                    write_size += 1;
+                } else {
+                    return write_size;
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/os/src/fs/mod.rs b/os/src/fs/mod.rs
index c015702d2dc2f20c013ebf46afa8776329b2f450..525df12cc63122dae5e493ca85cb961f53640e44 100644
--- a/os/src/fs/mod.rs
+++ b/os/src/fs/mod.rs
@@ -1,16 +1,24 @@
 mod pipe;
 mod stdio;
 mod inode;
+mod mail;
 
 use crate::mm::UserBuffer;
 
 pub trait File : Send + Sync {
     fn readable(&self) -> bool;
     fn writable(&self) -> bool;
+    fn inode_id(&self) -> Option<u32>;
     fn read(&self, buf: UserBuffer) -> usize;
     fn write(&self, buf: UserBuffer) -> usize;
 }
 
 pub use pipe::{Pipe, make_pipe};
 pub use stdio::{Stdin, Stdout};
-pub use inode::{OSInode, open_file, OpenFlags, list_apps};
\ No newline at end of file
+pub use inode::{
+    OSInode, open_file, OpenFlags, list_apps,
+    get_inode_id,
+    create_linker,delete_linker,count_files,
+    count_files_from_id,
+};
+pub use mail::{Mail,MailBox,MPipe,make_mpipe};
diff --git a/os/src/fs/pipe.rs b/os/src/fs/pipe.rs
index 1027b472b09870f6f40ba6ed030a749d951192a4..2350436535d7dda4a8c88701ade49fcf58d7a142 100644
--- a/os/src/fs/pipe.rs
+++ b/os/src/fs/pipe.rs
@@ -5,6 +5,10 @@ use crate::mm::{
     UserBuffer,
 };
 use crate::task::suspend_current_and_run_next;
+use crate::config::MAIL_SIZE;
+
+//比如说我要创建一个Pipe,其实就是新建一个PipeBuffer,
+//然后读端和写端都包装成一个Pipe,用来和进程之间交互
 
 pub struct Pipe {
     readable: bool,
@@ -29,7 +33,9 @@ impl Pipe {
     }
 }
 
-const RING_BUFFER_SIZE: usize = 32;
+// const RING_BUFFER_SIZE: usize = 32;
+// const RING_BUFFER_SIZE: usize = 256;
+const RING_BUFFER_SIZE: usize = MAIL_SIZE;
 
 #[derive(Copy, Clone, PartialEq)]
 enum RingBufferStatus {
@@ -44,6 +50,7 @@ pub struct PipeRingBuffer {
     tail: usize,
     status: RingBufferStatus,
     write_end: Option<Weak<Pipe>>,
+    //一个pipebuffer要知道谁在写它
 }
 
 impl PipeRingBuffer {
@@ -115,6 +122,7 @@ pub fn make_pipe() -> (Arc<Pipe>, Arc<Pipe>) {
 impl File for Pipe {
     fn readable(&self) -> bool { self.readable }
     fn writable(&self) -> bool { self.writable }
+    fn inode_id(&self) -> Option<u32> { None }
     fn read(&self, buf: UserBuffer) -> usize {
         assert_eq!(self.readable(), true);
         let mut buf_iter = buf.into_iter();
@@ -150,6 +158,7 @@ impl File for Pipe {
             let loop_write = ring_buffer.available_write();
             if loop_write == 0 {
                 drop(ring_buffer);
+                // warn!("Pipe::write, may cause dead lock...");
                 suspend_current_and_run_next();
                 continue;
             }
diff --git a/os/src/fs/stdio.rs b/os/src/fs/stdio.rs
index e8df79508f4a15bc7d86a3e5b7e279d0a39eac60..f3358ac76f0737e7c28bc80ea1db0f19fa782d8e 100644
--- a/os/src/fs/stdio.rs
+++ b/os/src/fs/stdio.rs
@@ -10,6 +10,7 @@ pub struct Stdout;
 impl File for Stdin {
     fn readable(&self) -> bool { true }
     fn writable(&self) -> bool { false }
+    fn inode_id(&self) -> Option<u32> { None }
     fn read(&self, mut user_buf: UserBuffer) -> usize {
         assert_eq!(user_buf.len(), 1);
         // busy loop
@@ -35,6 +36,7 @@ impl File for Stdin {
 impl File for Stdout {
     fn readable(&self) -> bool { false }
     fn writable(&self) -> bool { true }
+    fn inode_id(&self) -> Option<u32> { None }
     fn read(&self, _user_buf: UserBuffer) -> usize{
         panic!("Cannot read from stdout!");
     }
diff --git a/os/src/lang_items.rs b/os/src/lang_items.rs
index 3f5462abbb720703adcc9ed8cc7aa1f8a07c61f3..619f4f5f436f5929c61b22c549c4ec8d9db66382 100644
--- a/os/src/lang_items.rs
+++ b/os/src/lang_items.rs
@@ -1,5 +1,5 @@
-use core::panic::PanicInfo;
 use crate::sbi::shutdown;
+use core::panic::PanicInfo;
 
 #[panic_handler]
 fn panic(info: &PanicInfo) -> ! {
diff --git a/os/src/linker-k210.ld b/os/src/linker-k210.ld
index 4f9d2171fc557ee4d75e0ab6b4e2fe54b61aca36..eaa2c9ff1edbed9dea1d370dbf35054a6a2bd332 100644
--- a/os/src/linker-k210.ld
+++ b/os/src/linker-k210.ld
@@ -22,6 +22,7 @@ SECTIONS
     srodata = .;
     .rodata : {
         *(.rodata .rodata.*)
+        *(.srodata .srodata.*)
     }
 
     . = ALIGN(4K);
@@ -29,6 +30,7 @@ SECTIONS
     sdata = .;
     .data : {
         *(.data .data.*)
+        *(.sdata .sdata.*)
     }
 
     . = ALIGN(4K);
@@ -38,6 +40,7 @@ SECTIONS
         *(.bss.stack)
         sbss = .;
         *(.bss .bss.*)
+        *(.sbss .sbss.*)
     }
 
     . = ALIGN(4K);
diff --git a/os/src/linker-qemu.ld b/os/src/linker-qemu.ld
index 6b06e91646cee37c333ada71b3fc84172402f67a..5baafbd0a8bde097a69c458473520beca283b2f3 100644
--- a/os/src/linker-qemu.ld
+++ b/os/src/linker-qemu.ld
@@ -22,6 +22,7 @@ SECTIONS
     srodata = .;
     .rodata : {
         *(.rodata .rodata.*)
+        *(.srodata .srodata.*)
     }
 
     . = ALIGN(4K);
@@ -29,6 +30,7 @@ SECTIONS
     sdata = .;
     .data : {
         *(.data .data.*)
+        *(.sdata .sdata.*)
     }
 
     . = ALIGN(4K);
@@ -38,6 +40,7 @@ SECTIONS
         *(.bss.stack)
         sbss = .;
         *(.bss .bss.*)
+        *(.sbss .sbss.*)
     }
 
     . = ALIGN(4K);
diff --git a/os/src/main.rs b/os/src/main.rs
index e9a34bb8578dc78cbb951b2ef0d92a0d3744ccea..06308f66fbe42b2d6803d86bc5bbf9743e541188 100644
--- a/os/src/main.rs
+++ b/os/src/main.rs
@@ -31,15 +31,14 @@ fn clear_bss() {
         fn sbss();
         fn ebss();
     }
-    (sbss as usize..ebss as usize).for_each(|a| {
-        unsafe { (a as *mut u8).write_volatile(0) }
-    });
+    (sbss as usize..ebss as usize).for_each(|a| unsafe { (a as *mut u8).write_volatile(0) });
 }
 
 #[no_mangle]
 pub fn rust_main() -> ! {
     clear_bss();
-    println!("[kernel] Hello, world!");
+    kernel_println!("Hello, world!");
+    //分页模式是在内核初始化期间开启的,也就是说现在已经开启分页模式了!
     mm::init();
     mm::remap_test();
     trap::init();
@@ -49,4 +48,4 @@ pub fn rust_main() -> ! {
     task::add_initproc();
     task::run_tasks();
     panic!("Unreachable in rust_main!");
-}
\ No newline at end of file
+}
diff --git a/os/src/mm/address.rs b/os/src/mm/address.rs
index d5828ed275727c3ac1557471f30d0f45696feaba..a0e0ea94d4a7a5597b95993d93712cc779d0e7bb 100644
--- a/os/src/mm/address.rs
+++ b/os/src/mm/address.rs
@@ -86,6 +86,8 @@ impl From<VirtAddr> for VirtPageNum {
 impl From<VirtPageNum> for VirtAddr {
     fn from(v: VirtPageNum) -> Self { Self(v.0 << PAGE_SIZE_BITS) }
 }
+
+// 物理地址实现的功能是:给出物理地址可以返回物理页号
 impl PhysAddr {
     pub fn floor(&self) -> PhysPageNum { PhysPageNum(self.0 / PAGE_SIZE) }
     pub fn ceil(&self) -> PhysPageNum { PhysPageNum((self.0 - 1 + PAGE_SIZE) / PAGE_SIZE) }
@@ -159,6 +161,8 @@ impl StepByOne for PhysPageNum {
     }
 }
 
+//根据代码可以看出,这是一个左闭右开区间
+//[start,end]
 #[derive(Copy, Clone)]
 pub struct SimpleRange<T> where
     T: StepByOne + Copy + PartialEq + PartialOrd + Debug, {
diff --git a/os/src/mm/frame_allocator.rs b/os/src/mm/frame_allocator.rs
index 2cb6427bf0bb99701325b2996dd2e41a2bd9ddbe..5068cc8a52f3dab50f5ec2232da2c06b97fcd306 100644
--- a/os/src/mm/frame_allocator.rs
+++ b/os/src/mm/frame_allocator.rs
@@ -36,8 +36,10 @@ trait FrameAllocator {
     fn new() -> Self;
     fn alloc(&mut self) -> Option<PhysPageNum>;
     fn dealloc(&mut self, ppn: PhysPageNum);
+    fn frame_left(&self)->usize;
 }
 
+//[current,end)左闭右开区间表示还有多少个frame未分配
 pub struct StackFrameAllocator {
     current: usize,
     end: usize,
@@ -61,12 +63,14 @@ impl FrameAllocator for StackFrameAllocator {
     }
     fn alloc(&mut self) -> Option<PhysPageNum> {
         if let Some(ppn) = self.recycled.pop() {
+            trace!("alloc...{} pages left...",self.frame_left());
             Some(ppn.into())
         } else {
             if self.current == self.end {
                 None
             } else {
                 self.current += 1;
+                trace!("alloc...{} pages left...",self.frame_left());
                 Some((self.current - 1).into())
             }
         }
@@ -83,8 +87,15 @@ impl FrameAllocator for StackFrameAllocator {
         // recycle
         self.recycled.push(ppn);
     }
+    fn frame_left(&self) -> usize {
+        return self.end - self.current + self.recycled.len();
+    }
 }
 
+// impl StackFrameAllocator {
+    
+// }
+
 type FrameAllocatorImpl = StackFrameAllocator;
 
 lazy_static! {
@@ -101,11 +112,19 @@ pub fn init_frame_allocator() {
         .init(PhysAddr::from(ekernel as usize).ceil(), PhysAddr::from(MEMORY_END).floor());
 }
 
+//问题:这个ppn是哪来的?
 pub fn frame_alloc() -> Option<FrameTracker> {
     FRAME_ALLOCATOR
         .lock()
         .alloc()
         .map(|ppn| FrameTracker::new(ppn))
+    //至于map哪个ppn,是分配方法决定的,就不需要我们管了
+    //返回的是一个物理页帧
+}
+
+//向其他模块提供public接口,知道现在还有多少个物理页帧可以分配
+pub fn frame_left() -> usize{
+    FRAME_ALLOCATOR.lock().frame_left()
 }
 
 pub fn frame_dealloc(ppn: PhysPageNum) {
@@ -118,7 +137,7 @@ pub fn frame_dealloc(ppn: PhysPageNum) {
 pub fn frame_allocator_test() {
     let mut v: Vec<FrameTracker> = Vec::new();
     for i in 0..5 {
-        let frame = frame_alloc().unwrap();
+        let frame = frame_alloc().unwrap();//使用frame_alloc函数可以得到一个物理页帧
         println!("{:?}", frame);
         v.push(frame);
     }
diff --git a/os/src/mm/heap_allocator.rs b/os/src/mm/heap_allocator.rs
index 2c7468f2c12c0aba1090855297d524209b0e73ce..9132c33fa128df8d4b4dfb8a45a45b14312b49ba 100644
--- a/os/src/mm/heap_allocator.rs
+++ b/os/src/mm/heap_allocator.rs
@@ -11,6 +11,8 @@ pub fn handle_alloc_error(layout: core::alloc::Layout) -> ! {
 
 static mut HEAP_SPACE: [u8; KERNEL_HEAP_SIZE] = [0; KERNEL_HEAP_SIZE];
 
+
+//我的理解是,用所有的程序一共只有这么多空间能分出去。
 pub fn init_heap() {
     unsafe {
         HEAP_ALLOCATOR
diff --git a/os/src/mm/memory_set.rs b/os/src/mm/memory_set.rs
index b3a8b2e4927b0ef60dbb6fbdbea57d997cf928bd..ba9ad1f39546e6ed158f8919896527fc79d9e34c 100644
--- a/os/src/mm/memory_set.rs
+++ b/os/src/mm/memory_set.rs
@@ -1,6 +1,6 @@
 use super::{PageTable, PageTableEntry, PTEFlags};
 use super::{VirtPageNum, VirtAddr, PhysPageNum, PhysAddr};
-use super::{FrameTracker, frame_alloc};
+use super::{FrameTracker, frame_alloc, frame_left};
 use super::{VPNRange, StepByOne};
 use alloc::collections::BTreeMap;
 use alloc::vec::Vec;
@@ -11,6 +11,7 @@ use spin::Mutex;
 use crate::config::{
     MEMORY_END,
     PAGE_SIZE,
+    PAGE_SIZE_BITS,
     TRAMPOLINE,
     TRAP_CONTEXT,
     USER_STACK_SIZE,
@@ -30,6 +31,9 @@ extern "C" {
     fn strampoline();
 }
 
+//KERNEL_SPACE是每一个app有一份的
+//但是问题来了:怎么取得特定的app的KERNEL_SPACE?
+//在进程控制块里面,每一个进程都有一个对应的KERNEL_SPACE
 lazy_static! {
     pub static ref KERNEL_SPACE: Arc<Mutex<MemorySet>> = Arc::new(Mutex::new(
         MemorySet::new_kernel()
@@ -45,6 +49,19 @@ pub struct MemorySet {
     areas: Vec<MapArea>,
 }
 
+fn convert_usize_to_permission(port: usize)->Option<MapPermission>{
+    match port{
+        1 => Some(MapPermission::R),
+        2 => Some(MapPermission::W),
+        3 => Some(MapPermission::R | MapPermission::W),
+        4 => Some(MapPermission::X),
+        5 => Some(MapPermission::X | MapPermission::R),
+        6 => Some(MapPermission::X | MapPermission::W),
+        7 => Some(MapPermission::X | MapPermission::W | MapPermission::R),
+        _ => None
+    }
+}
+
 impl MemorySet {
     pub fn new_bare() -> Self {
         Self {
@@ -55,7 +72,9 @@ impl MemorySet {
     pub fn token(&self) -> usize {
         self.page_table.token()
     }
-    /// Assume that no conflicts.
+
+    /// 假设已经分配好了物理页面,建立一个对应关系
+    /// 这个函数只能在已经申请完空间才能调用
     pub fn insert_framed_area(&mut self, start_va: VirtAddr, end_va: VirtAddr, permission: MapPermission) {
         self.push(MapArea::new(
             start_va,
@@ -199,19 +218,26 @@ impl MemorySet {
         ), None);
         (memory_set, user_stack_top, elf.header.pt2.entry_point() as usize)
     }
+    //
     pub fn from_existed_user(user_space: &MemorySet) -> MemorySet {
+        //先创建一个空的地址空间
         let mut memory_set = Self::new_bare();
         // map trampoline
+        //映射跳转页面(???其实上一个实验我就没有太理解什么是跳板页面,不过看起来暂时不是非常影响哦
         memory_set.map_trampoline();
         // copy data sections/trap_context/user_stack
         for area in user_space.areas.iter() {
             let new_area = MapArea::from_another(area);
+            //先把别人的area复制过来
+            //放进memory_set里面
+            //注意,这里push进去的时候其实就调用了area的map,就已经分配了物理页帧
             memory_set.push(new_area, None);
             // copy data from another space
             for vpn in area.vpn_range {
                 let src_ppn = user_space.translate(vpn).unwrap().ppn();
                 let dst_ppn = memory_set.translate(vpn).unwrap().ppn();
                 dst_ppn.get_bytes_array().copy_from_slice(src_ppn.get_bytes_array());
+                //所以这里就是可以直接复制数据的!
             }
         }
         memory_set
@@ -230,6 +256,120 @@ impl MemorySet {
         //*self = Self::new_bare();
         self.areas.clear();
     }
+
+    pub fn v2p(&self,va:VirtAddr)->Option<PhysAddr>{
+        let vpn = va.floor();
+        let page_offset = va.page_offset();
+        // let pte = self.translate(vpn);
+        if let Some(pte) = self.translate(vpn){
+            return Some(PhysAddr(((usize::from(pte.ppn())<< PAGE_SIZE_BITS) + page_offset) as usize));
+        }
+        return None;
+    }
+
+    pub fn unmap_the_chosen_area(&mut self,range: VPNRange)->isize{
+        for area in &mut self.areas{
+            let size = area.unmap_the_chosen_area(&mut self.page_table,range) as isize;
+            if size != -1 {
+                return size;
+            }
+        }
+        return -1 as isize;
+    }
+
+    pub fn mmap(&mut self,start: usize, len: usize, port: usize) -> isize{
+        //要检查的内容:
+        //1. 物理内存还够用吗
+        //2. 这个地址范围内是不是有哪些已经被映射过了
+    
+        if start % PAGE_SIZE != 0 {
+            return -1 as isize;
+        }
+        if len == 0 {
+            // warn!("in mmap...number=0");
+            return 0 as isize;
+        }
+        let number = ((len - 1 + PAGE_SIZE) /PAGE_SIZE )as usize;
+        //向上取整,表示会用到几个page
+    
+        //以防万一,再检查一遍读写权限问题
+        if (port & !0x7 != 0)||(port & 0x7 == 0) {
+            return -1 as isize;
+        }
+    
+        //检查一下现在还有几个page,是不是够用.done
+        if frame_left() < number {
+            return -1 as isize;
+        }
+    
+        let permission = convert_usize_to_permission(port);
+        match permission{
+            Some(MapPermission) => {},
+            None => return -1 as isize,
+        }
+
+        //这个地址范围是不是有人已经映射过了?
+        //根据代码,调用translate检查即可
+        //mmap给分配的空间都是在用户态下使用的,因此可以给U权限哦
+        let mut area = MapArea::new((start).into(),
+                                (start+len).into(),
+                                MapType::Framed,
+                                permission.unwrap() | MapPermission::U);
+                                
+        //调用translate,检查是否全部能完成映射
+        if area.not_map_check(&self.page_table)==false {
+            // warn!("[kernel] have mapped!");
+            return -1 as isize;
+        }
+
+        area.map(&mut self.page_table);
+    
+        let size = usize::from(area.vpn_range.get_end()) - usize::from(area.vpn_range.get_start());
+    
+        self.areas.push(area);
+    
+        //以上合法性检查结束之后,可以直接分配。分为2步:
+        //1,申请物理页面(怎么申请?申请完给谁?)
+        // 这里不用手动写,因为在map的代码里面已经调用了分配物理页帧的函数
+    
+        //问题:现在的困难在于,每一个不同的进程都会有不同的映射规则。
+        //我在这里怎么访问“当前进程下的。。。。”呢,KERNELSPACE好像是一个不同进程下的东西
+        // debug!("[kernel] in mmap...size alloc is {},{}",number,size);
+        assert_eq!(number, size);
+        // debug!("[kernel] in mmap...size alloc is {}",size);
+    
+        return (size*PAGE_SIZE) as isize;
+    }
+
+    /// 接口:fn unmmap(start: usize, len: usize) -> isize
+    /// 在系统调用处已经进行了数据合法性检查,因此在这里可以直接进行分配
+    /// start:开始地址
+    /// number:要分配几个page
+    pub fn munmap(&mut self,start: usize, len: usize) -> isize{
+        //要检查的内容:
+        //1. 物理内存还够用吗
+        //2. 这个地址范围内是不是有哪些已经被映射过了
+
+        if start % PAGE_SIZE != 0 {
+            return -1 as isize;
+        }
+        if len == 0 {
+            // warn!("in mmap...number=0");
+            return 0 as isize;
+        }
+
+        let start_va: VirtAddr = start.into();
+        let end_va: VirtAddr = (start+len).into();
+        let start_vpn: VirtPageNum = start_va.floor();
+        let end_vpn: VirtPageNum = end_va.ceil();
+
+        //这是被映射过去的maparea里面的地址范围
+        let range = VPNRange::new(start_vpn, end_vpn);
+
+        return self.unmap_the_chosen_area(range);
+        // return -1 as isize;
+    }
+
 }
 
 pub struct MapArea {
@@ -238,7 +378,7 @@ pub struct MapArea {
     map_type: MapType,
     map_perm: MapPermission,
 }
-
+//按照规则,一次只能分配整数个page
 impl MapArea {
     pub fn new(
         start_va: VirtAddr,
@@ -269,6 +409,7 @@ impl MapArea {
             MapType::Identical => {
                 ppn = PhysPageNum(vpn.0);
             }
+            //经常会遇到物理内存不足的问题啊
             MapType::Framed => {
                 let frame = frame_alloc().unwrap();
                 ppn = frame.ppn;
@@ -319,6 +460,60 @@ impl MapArea {
             current_vpn.step();
         }
     }
+    //print range
+    pub fn print_range(&self) {
+        debug!("[kernel] MapArea::range  ({:#x},{:#x})",
+            usize::from(self.vpn_range.get_start()),
+            usize::from(self.vpn_range.get_end())
+        );
+    }
+    //check if has mapped...
+    //找到问题了!这里不是应该寻找kernel space的页表
+    //而是寻找当前的页表是否有映射。。。
+    pub fn not_map_check(&self,page_table: &PageTable)-> bool{
+        for vpn in self.vpn_range{
+            if let Some(pte) = page_table.translate(vpn){
+                if pte.is_valid(){
+                    // warn!("[kernel] vpn have mapped is {:#x}",usize::from(pte.ppn()));
+                    return false
+                }
+            }
+        }
+        return true;
+    }
+    //check if not mapped...
+    pub fn have_mapped_check(&self,page_table: &PageTable)-> bool{
+        self.print_range();
+        for vpn in self.vpn_range{
+            if let Some(pte) = page_table.translate(vpn){
+                if !pte.is_valid(){
+                    // warn!("[kernel] vpn not mapped is {:#x}",usize::from(pte.ppn()));
+                    return false
+                }
+            }
+        }
+        return true;
+    }
+    pub fn match_area_with_vpnrange(&self, range: VPNRange)->bool{
+        if (self.vpn_range.get_start() == range.get_start()) && (self.vpn_range.get_end() == range.get_end()){
+            true
+        }else{
+            false
+        }
+    }
+    pub fn unmap_the_chosen_area(&mut self,page_table: &mut PageTable,range: VPNRange)->isize{
+        if self.match_area_with_vpnrange(range){
+            if !self.have_mapped_check(page_table){
+                //如果发现有还没有map过的页表项想要unmap
+                // warn!("[kernel] vpn not mapped yet ");
+                return -1 as isize;
+            }
+            self.unmap(page_table);
+            let size = usize::from(range.get_end()) - usize::from(range.get_start());
+            return size as isize;
+        }
+        return -1 as isize;
+    }
 }
 
 #[derive(Copy, Clone, PartialEq, Debug)]
@@ -327,6 +522,8 @@ pub enum MapType {
     Framed,
 }
 
+//done:在MapArea这里,要给Permission加上U权限啊
+
 bitflags! {
     pub struct MapPermission: u8 {
         const R = 1 << 1;
@@ -354,5 +551,6 @@ pub fn remap_test() {
         kernel_space.page_table.translate(mid_data.floor()).unwrap().executable(),
         false,
     );
+    // kernel_space.insert_framed_area();
     println!("remap_test passed!");
 }
\ No newline at end of file
diff --git a/os/src/mm/mod.rs b/os/src/mm/mod.rs
index dbd47fe798f177a599d9ec7272d23e3e35f17fee..3f07670595456879cfb4d0774cf465832b57a7ec 100644
--- a/os/src/mm/mod.rs
+++ b/os/src/mm/mod.rs
@@ -4,12 +4,21 @@ mod frame_allocator;
 mod page_table;
 mod memory_set;
 
-use page_table::PTEFlags;
-use address::VPNRange;
-pub use address::{PhysAddr, VirtAddr, PhysPageNum, VirtPageNum, StepByOne};
-pub use frame_allocator::{FrameTracker, frame_alloc, frame_dealloc,};
+
+pub use address::{PhysAddr, VirtAddr,VPNRange, PhysPageNum, VirtPageNum, StepByOne};
+// use crate::config::{
+//     PAGE_SIZE,
+// };
+
+pub use frame_allocator::{
+    FrameTracker, 
+    frame_alloc,
+    frame_dealloc,
+    frame_left,
+};
 pub use page_table::{
     PageTable,
+    PTEFlags,
     PageTableEntry,
     translated_byte_buffer,
     translated_str,
@@ -17,12 +26,19 @@ pub use page_table::{
     translated_refmut,
     UserBuffer,
     UserBufferIterator,
+    check_byte_buffer_valid,
+};
+
+pub use memory_set::{MemorySet, KERNEL_SPACE, MapPermission};
+pub use memory_set::{
+    remap_test,
+    kernel_token,
+    // mmap,
+    // munmap,
 };
-pub use memory_set::{MemorySet, KERNEL_SPACE, MapPermission, kernel_token};
-pub use memory_set::remap_test;
 
 pub fn init() {
     heap_allocator::init_heap();
     frame_allocator::init_frame_allocator();
     KERNEL_SPACE.lock().activate();
-}
\ No newline at end of file
+}
diff --git a/os/src/mm/page_table.rs b/os/src/mm/page_table.rs
index b1a227fa11c632cbdccfc9c6245f9bb99f86befd..2140068a67cc7233b298a8ab49ea57246f789ee0 100644
--- a/os/src/mm/page_table.rs
+++ b/os/src/mm/page_table.rs
@@ -12,6 +12,7 @@ use alloc::vec;
 use alloc::string::String;
 use bitflags::*;
 
+//物理页的标志
 bitflags! {
     pub struct PTEFlags: u8 {
         const V = 1 << 0;
@@ -45,7 +46,7 @@ impl PageTableEntry {
     pub fn ppn(&self) -> PhysPageNum {
         (self.bits >> 10 & ((1usize << 44) - 1)).into()
     }
-    pub fn flags(&self) -> PTEFlags {
+    pub fn flags(&self) -> PTEFlags {//页表的低8位是flags
         PTEFlags::from_bits(self.bits as u8).unwrap()
     }
     pub fn is_valid(&self) -> bool {
@@ -83,27 +84,35 @@ impl PageTable {
             frames: Vec::new(),
         }
     }
+    ///PageTable::find_pte_create 在多级页表找到一个虚拟页号对应的页表项的可变引用方便后续的读写。
+    /// 如果在 遍历的过程中发现有节点尚未创建则会新建一个节点。
+    /// 注意在更新页表项的时候,不仅要更新物理页号,还要将标志位 V 置 1, 
+    /// 不然硬件在查多级页表的时候,会认为这个页表项不合法,从而触发 Page Fault 而不能向下走。
     fn find_pte_create(&mut self, vpn: VirtPageNum) -> Option<&mut PageTableEntry> {
+        trace!("Pagetable::find_pte_create...token is {:#x}, vpn is {:#x}", self.token(), usize::from(vpn));
         let idxs = vpn.indexes();
         let mut ppn = self.root_ppn;
         let mut result: Option<&mut PageTableEntry> = None;
         for i in 0..3 {
+            // debug!("i is {}",i);
             let pte = &mut ppn.get_pte_array()[idxs[i]];
             if i == 2 {
                 result = Some(pte);
                 break;
             }
-            if !pte.is_valid() {
+            if !pte.is_valid() {//如果遍历的过程中发现有页表不存在,那么就创建它
                 let frame = frame_alloc().unwrap();
                 *pte = PageTableEntry::new(frame.ppn, PTEFlags::V);
                 self.frames.push(frame);
             }
             ppn = pte.ppn();
         }
+        // debug!("resule is...");
         result
     }
+    //这个函数的功能是给出虚拟地址,然后找到其物理地址
     fn find_pte(&self, vpn: VirtPageNum) -> Option<&PageTableEntry> {
-        let idxs = vpn.indexes();
+        let idxs = vpn.indexes();//说明了在页表中寻找映射的方法
         let mut ppn = self.root_ppn;
         let mut result: Option<&PageTableEntry> = None;
         for i in 0..3 {
@@ -113,24 +122,35 @@ impl PageTable {
                 break;
             }
             if !pte.is_valid() {
-                return None;
+                return None;//如果有某一个地方还没有映射,那就说明这个虚拟地址还没有映射
+                //返回值为None时,就可以建立映射关系了
             }
             ppn = pte.ppn();
         }
         result
     }
     #[allow(unused)]
-    pub fn map(&mut self, vpn: VirtPageNum, ppn: PhysPageNum, flags: PTEFlags) {
+    pub fn map(&mut self, vpn: VirtPageNum, ppn: PhysPageNum, flags: PTEFlags) ->bool{
+        trace!("Pagetable::mapping...token is {:#x} \n vpn is {:#x}, ppn is {:#x}",self.token(), usize::from(vpn), usize::from(ppn));
         let pte = self.find_pte_create(vpn).unwrap();
+        // 问题:为什么这里发现页表是已经not-valid的,就能说明之前map过了?
+        // 因为create函数无论如何都会返回一个pte的。如果是新创建的自然不会有问题,一定是valid的。
         assert!(!pte.is_valid(), "vpn {:?} is mapped before mapping", vpn);
         *pte = PageTableEntry::new(ppn, flags | PTEFlags::V);
+        return true;
     }
     #[allow(unused)]
-    pub fn unmap(&mut self, vpn: VirtPageNum) {
+    pub fn unmap(&mut self, vpn: VirtPageNum) ->bool{
+        trace!("Pagetable::unmapping...token is {:#x} \n vpn is {:#x}",self.token(),usize::from(vpn));
         let pte = self.find_pte_create(vpn).unwrap();
         assert!(pte.is_valid(), "vpn {:?} is invalid before unmapping", vpn);
         *pte = PageTableEntry::empty();
+        return true;
     }
+
+    //
+    //如果这个虚拟地址已经被映射了,那么就返回pte,否则返回None
+    //所以想要建立新的映射,应该先检查地址范围内返回值是不是None,如果有一个返回值为None,那么就不能用
     pub fn translate(&self, vpn: VirtPageNum) -> Option<PageTableEntry> {
         self.find_pte(vpn)
             .map(|pte| {pte.clone()})
@@ -144,37 +164,81 @@ impl PageTable {
                 (aligned_pa_usize + offset).into()
             })
     }
+    //注意!这里的token给出的并不是物理页号啊······
     pub fn token(&self) -> usize {
+        trace!("[kernel] PageTable::token is {:#x}",8usize << 60 | self.root_ppn.0);
         8usize << 60 | self.root_ppn.0
     }
 }
 
-pub fn translated_byte_buffer(token: usize, ptr: *const u8, len: usize) -> Vec<&'static mut [u8]> {
+pub fn translated_byte_buffer(token: usize, ptr: *const u8, len: usize) -> Option<Vec<&'static mut [u8]>> {
     let page_table = PageTable::from_token(token);
     let mut start = ptr as usize;
     let end = start + len;
     let mut v = Vec::new();
+    //check addr
     while start < end {
         let start_va = VirtAddr::from(start);
         let mut vpn = start_va.floor();
-        let ppn = page_table
-            .translate(vpn)
-            .unwrap()
-            .ppn();
-        vpn.step();
-        let mut end_va: VirtAddr = vpn.into();
-        end_va = end_va.min(VirtAddr::from(end));
-        if end_va.page_offset() == 0 {
-            v.push(&mut ppn.get_bytes_array()[start_va.page_offset()..]);
-        } else {
-            v.push(&mut ppn.get_bytes_array()[start_va.page_offset()..end_va.page_offset()]);
+        if let Some(pte) = page_table.translate(vpn){
+            let ppn = pte.ppn();
+            vpn.step();
+            let mut end_va: VirtAddr = vpn.into();
+            end_va = end_va.min(VirtAddr::from(end));
+            if end_va.page_offset() == 0 {
+                v.push(&mut ppn.get_bytes_array()[start_va.page_offset()..]);
+            } else {
+                v.push(&mut ppn.get_bytes_array()[start_va.page_offset()..end_va.page_offset()]);
+            }
+            start = end_va.into();
+        }else{
+            return None;
+        }
+    }
+    
+    // while start < end {
+    //     let start_va = VirtAddr::from(start);
+    //     let mut vpn = start_va.floor();
+    //     let ppn = page_table
+    //         .translate(vpn)
+    //         .unwrap()
+    //         .ppn();
+    //     vpn.step();
+    //     let mut end_va: VirtAddr = vpn.into();
+    //     end_va = end_va.min(VirtAddr::from(end));
+    //     if end_va.page_offset() == 0 {
+    //         v.push(&mut ppn.get_bytes_array()[start_va.page_offset()..]);
+    //     } else {
+    //         v.push(&mut ppn.get_bytes_array()[start_va.page_offset()..end_va.page_offset()]);
+    //     }
+    //     start = end_va.into();
+    // }
+    Some(v)
+}
+
+pub fn check_byte_buffer_valid(token: usize, ptr: *const u8, len: usize) -> bool {
+    let page_table = PageTable::from_token(token);
+    let mut start = ptr as usize;
+    let end = start + len;
+    //check addr
+    while start < end {
+        let start_va = VirtAddr::from(start);
+        let mut vpn = start_va.floor();
+        if let Some(pte) = page_table.translate(vpn){
+            let ppn = pte.ppn();
+            vpn.step();
+            let mut end_va: VirtAddr = vpn.into();
+            end_va = end_va.min(VirtAddr::from(end));
+            start = end_va.into();
+        }else{
+            return false;
         }
-        start = end_va.into();
     }
-    v
+    true
 }
 
 /// Load a string from other address spaces into kernel space without an end `\0`.
+//ptr其实是字符串的地址,试试看这个字符串是不是能翻译成String哦
 pub fn translated_str(token: usize, ptr: *const u8) -> String {
     let page_table = PageTable::from_token(token);
     let mut string = String::new();
diff --git a/os/src/sbi.rs b/os/src/sbi.rs
index 36e15fcc30b8f6a1f3cdd87cef99588cc5976f9e..1412c65f9a8afc7664e91b9f27df1d4a05820cf1 100644
--- a/os/src/sbi.rs
+++ b/os/src/sbi.rs
@@ -40,4 +40,3 @@ pub fn shutdown() -> ! {
     sbi_call(SBI_SHUTDOWN, 0, 0, 0);
     panic!("It should shutdown!");
 }
-
diff --git a/os/src/syscall/flinker.rs b/os/src/syscall/flinker.rs
new file mode 100644
index 0000000000000000000000000000000000000000..24791fe5fb268e56aebfd969f2117600e3172210
--- /dev/null
+++ b/os/src/syscall/flinker.rs
@@ -0,0 +1,210 @@
+
+#[repr(C)]
+#[derive(Debug,Clone,Copy)]
+pub struct Stat {
+    /// 文件所在磁盘驱动器号
+    pub dev: u64,
+    /// inode 文件所在 inode 编号
+    pub ino: u64,
+    /// 文件类型
+    pub mode: StatMode,
+    /// 硬链接数量,初始为1
+    pub nlink: u32,
+    /// 无需考虑,为了兼容性设计
+    pad: [u64; 7],
+}
+
+bitflags! {
+    pub struct StatMode: u32 {
+        const NULL  = 0;
+        /// directory
+        const DIR   = 0o040000;
+        /// ordinary regular file
+        const FILE  = 0o100000;
+    }
+}
+
+//下面的内容就是建立一个key-word的字典
+//1.字典里面存储的是映射关系,字符串到字符串,string到string的
+//2.这个映射关系存储在磁盘上某个文件中,启动OS的时候把这个文件加载出来
+//3.OS关闭的时候写回去即可
+
+use core::any::Any;
+use alloc::sync::Arc;
+use spin::Mutex;
+use lazy_static::*;
+use alloc::string::String;
+use alloc::collections::btree_map::BTreeMap;
+use super::fs::*;
+use crate::fs::{
+    make_pipe, OpenFlags, open_file, OSInode,
+    create_linker,delete_linker,count_files,
+    get_inode_id,
+    File, 
+    Stdin, 
+    Stdout,
+    count_files_from_id,
+};
+use crate::mm::{
+    translated_str,
+    VirtAddr,
+};
+use crate::task::{
+    current_user_token, 
+    current_task,
+    current_user_v2p,
+};
+use super::process::sys_getpid;
+
+lazy_static! {
+    pub static ref PATH_MAPPER: Mutex<BTreeMap<String,String>> = Mutex::new(BTreeMap::new());
+}
+
+pub fn put_link(fake_path:String, real_path:String){
+    PATH_MAPPER.lock().insert(fake_path,real_path);
+}
+
+pub fn get_link(fake_path:&String) -> Option<String>{
+    if let Some(str) = PATH_MAPPER.lock().get(fake_path){
+        Some(String::from(str))//这里必须这么写,
+        //因为get方法返回的是临时变量,事实上并不能直接返回给外面的函数
+    }else{
+        None
+    }
+}
+
+pub fn remove_link(fake_path:&String) ->Option<String>{
+    PATH_MAPPER.lock().remove(fake_path)
+}
+
+
+//=============================
+
+//lab7
+pub fn sys_linkat5(olddirfd: isize, oldpath: *const u8, newdirfd: isize, newpath: *const u8, flags: u32) -> isize{
+    info!("sys_linkat5...");
+    sys_linkat(oldpath, newpath, flags)
+}
+//我大概有思路了,大概就是磁盘上分配一个文件专门用来存储映射信息,存储一个dict,如果产生了这样的系统调用
+//就在dic里面添加值,存储把某个路径映射到哪里的信息
+//unlink就是把地址映射信息删除
+//看样子只要存储字符串就可以了
+//照理来说是要写在磁盘上的,但是现在可以暂时先写在内存里面
+//我知道了!只要照抄用户态,就可以知道如何open如何使用。我在这里确实需要一个文件来存储这些映射关系,
+//所以OS或许还需要对用户程序做出一些要求,比如说不允许使用以.开头的文件名,这些文件名限定为系统文件
+//but now, we can ignore the security and use any name we'd like to
+
+
+//事实上,系统调用接口是syscall有5个参数。但是我实在不知道怎么这样实现,所以为了先写完实验
+//可以先按syscall走,userlib里面的接口也改改就可以了
+//但是最后实现实验的时候大概还要再改改
+pub fn sys_linkat(oldpath: *const u8, newpath: *const u8, flags: u32) -> isize{
+    let token = current_user_token();
+    let real_path = translated_str(token, oldpath);
+    let fake_path = translated_str(token, newpath);
+    info!("[sys_linkat]...real_path:{},fake_path:{}",real_path,fake_path);
+    if let Some(inode) = create_linker(fake_path.as_str(),real_path.as_str()){
+        return -1 as isize;
+    }else{
+        return 0 as isize;
+    }
+}
+
+pub fn sys_unlinkat(dirfd: isize, path: *const u8, flags: u32) -> isize{
+    let token = current_user_token();
+    let fake_path = translated_str(token, path);
+    info!("[sys_unlinkat]...");
+    if delete_linker(fake_path.as_str()){
+        return 0 as isize;
+    }else{
+        return -1 as isize;
+    }
+}
+
+pub fn return_if_file(f: &(dyn Any + Send + Sync)) -> Option<&Arc<OSInode>>{
+    if let Some(file) = f.downcast_ref::<Arc<OSInode>>() {
+        println!("It's a Arc<OSInode>");
+        return Some(file)
+    } else {
+        warn!("Not a OSInode...");
+        return None;
+    }
+}
+
+pub fn sys_fstat(fd: usize, st: *mut Stat) -> isize{
+    info!("[sys_fstat]...fd:{:#x},st:{:#x}",fd,st as usize);
+
+    let task = current_task().unwrap();
+    //get-data
+    let mut inner = task.acquire_inner_lock();
+
+    //TODO这儿肯定会写出死锁!!!!
+    //也不一定。。。
+    if let Some(inode) = &inner.fd_table[fd]{
+        //判断这里是不是OSInode类型
+        let inode = inode.clone();
+        drop(inner);
+        if let Some(inode_id) = inode.inode_id(){
+            info!("[sys_fstat] inode_id is {}",inode_id);
+            //TODO:exception solve
+            
+            let count = count_files_from_id(inode_id).unwrap();
+
+            //这边drop掉。但是关键是···类型转换的时候会再加一次锁,这样就死锁了······Orz
+
+            //仿佛明白了······因为现在处理系统调用肯定是运行在内核态
+            //但是这里给的是用户态的虚拟地址。那这就确实是有问题的
+            //因此这里大概是真的需要取出物理地址,然后访问物理地址。
+            //因为OS在页表初始化的时候以及建立了物理地址到物理地址的映射,因此直接访问物理地址也是没问题的吧大概
+            let pa = current_user_v2p(VirtAddr::from(st as usize));
+
+            println!("v2p convert end");
+
+            // info!("[sys_fstat]...physics addr is {:#x}",pa_st as usize);
+            match pa {
+            //pub dev: u64,
+            // /// inode 文件所在 inode 编号
+            // pub ino: u64,
+            // /// 文件类型
+            // pub mode: StatMode,
+            // /// 硬链接数量,初始为1
+            // pub nlink: u32,
+                Some(pa_t)=>{
+                    // let pa_ts = usize::from(pa.unwrap()) as *mut TimeVal;
+                    let pa_st = usize::from(pa_t) as *mut Stat;
+                    info!("[sys_fstat]...physics addr is {:#x}",pa_st as usize);
+
+                    unsafe{
+                        match (*pa_st){
+                            Stat => {
+                                (*pa_st).dev = 1 as u64;
+                                (*pa_st).ino = inode_id as u64;
+                                (*pa_st).mode = StatMode::FILE;
+                                (*pa_st).nlink = count as u32;
+                            }
+                            _ => {
+                                warn!("[sys_fstat] NULL");
+                            },
+                        }
+                    }
+                },
+                _ =>{
+                    warn!("[get_time]  NONE");
+                }
+            }
+            debug!("sys_get_time return...");
+            return 0 as isize;
+            info!("[sys_fstat]...");
+            //todo:从文件描述符到Inode或者name
+            //todo:getfiledata如何得到文件类型是文件夹还是普通文件?
+            -1
+        }else{
+            warn!("[sys_fstat] cant't find inode id...");
+            return -1 as isize;
+        }
+    }else{
+        warn!("[sys_fstat] no file in fdtable[fd]...");
+        return -1 as isize;
+    }
+    //这里是避免发生所有权转移
+}
\ No newline at end of file
diff --git a/os/src/syscall/fs.rs b/os/src/syscall/fs.rs
index 79eac32aeddbe6eca7ee0a5166cc0633d1b577e8..4fb3d1573ef256504b5f64a75ebed901859c6cec 100644
--- a/os/src/syscall/fs.rs
+++ b/os/src/syscall/fs.rs
@@ -3,12 +3,48 @@ use crate::mm::{
     translated_byte_buffer,
     translated_refmut,
     translated_str,
+    check_byte_buffer_valid,
+};
+use crate::fs::{
+    make_pipe, OpenFlags, open_file,
 };
-use crate::task::{current_user_token, current_task};
-use crate::fs::{make_pipe, OpenFlags, open_file};
 use alloc::sync::Arc;
 
+use crate::task::{
+    current_user_token, 
+    mail_user_token_pid,
+
+    current_task,
+    mail_write_to_pid,
+    mail_write_to_me,
+    mail_get_from_me,
+    mail_not_full_me,
+    mail_not_full_pid,
+    mail_not_empty_me,
+    
+};
+
+use super::process::sys_getpid;
+use crate::config::{
+    MAIL_SIZE,
+};
+
+use super::flinker::{
+    get_link,
+};
+
+/// 代码段 .text 不允许被修改;
+/// 只读数据段 .rodata 不允许被修改,也不允许从它上面取指;
+/// .data/.bss 均允许被读写,但是不允许从它上面取指。
+/// 
+/// 功能:将内存中缓冲区中的数据写入文件。
+/// 参数:`fd` 表示待写入文件的文件描述符;
+///      `buf` 表示内存中缓冲区的起始地址;
+///      `len` 表示内存中缓冲区的长度。
+/// 返回值:返回成功写入的长度。
+/// syscall ID:64
 pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize {
+    debug!("sys_write...fd {}, buf {:#x}, len {}",fd,buf as usize,len);
     let token = current_user_token();
     let task = current_task().unwrap();
     let inner = task.acquire_inner_lock();
@@ -22,15 +58,21 @@ pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize {
         let file = file.clone();
         // release Task lock manually to avoid deadlock
         drop(inner);
-        file.write(
-            UserBuffer::new(translated_byte_buffer(token, buf, len))
-        ) as isize
+        // file.write(
+        //     UserBuffer::new(translated_byte_buffer(token, buf, len))
+        // ) as isize
+        if let Some(tsf) = translated_byte_buffer(token, buf, len){
+            file.write(UserBuffer::new(tsf)) as isize
+        }else{
+            -1
+        }
     } else {
         -1
     }
 }
 
 pub fn sys_read(fd: usize, buf: *const u8, len: usize) -> isize {
+    debug!("[sys_read]...");
     let token = current_user_token();
     let task = current_task().unwrap();
     let inner = task.acquire_inner_lock();
@@ -43,16 +85,20 @@ pub fn sys_read(fd: usize, buf: *const u8, len: usize) -> isize {
             return -1;
         }
         // release Task lock manually to avoid deadlock
+        // 问题:为什么是在这里drop的?
         drop(inner);
-        file.read(
-            UserBuffer::new(translated_byte_buffer(token, buf, len))
-        ) as isize
+        if let Some(tsf) = translated_byte_buffer(token, buf, len){
+            file.read(UserBuffer::new(tsf)) as isize
+        }else{
+            -1
+        }
     } else {
         -1
     }
 }
 
 pub fn sys_open(path: *const u8, flags: u32) -> isize {
+    info!("[sys_open]...");
     let task = current_task().unwrap();
     let token = current_user_token();
     let path = translated_str(token, path);
@@ -60,12 +106,26 @@ pub fn sys_open(path: *const u8, flags: u32) -> isize {
         path.as_str(),
         OpenFlags::from_bits(flags).unwrap()
     ) {
+        //inode类型是OSInode,就是一个文件(神奇!)
         let mut inner = task.acquire_inner_lock();
         let fd = inner.alloc_fd();
         inner.fd_table[fd] = Some(inode);
-        fd as isize
+        return fd as isize;
     } else {
-        -1
+        //否则就寻找link的路径,如果能找到且可以打开
+        if let Some(real_path) = get_link(&path){
+            if let Some(inode) = open_file(
+                real_path.as_str(),
+                OpenFlags::from_bits(flags).unwrap()
+            ) {
+                //inode类型是OSInode,就是一个文件(神奇!)
+                let mut inner = task.acquire_inner_lock();
+                let fd = inner.alloc_fd();
+                inner.fd_table[fd] = Some(inode);
+                return fd as isize;
+            }
+        }
+        return -1 as isize;
     }
 }
 
@@ -93,9 +153,15 @@ pub fn sys_pipe(pipe: *mut usize) -> isize {
     inner.fd_table[write_fd] = Some(pipe_write);
     *translated_refmut(token, pipe) = read_fd;
     *translated_refmut(token, unsafe { pipe.add(1) }) = write_fd;
+    //总而言之以上两句话的含义是,把read_fd和write_fd都写给用户态啦
     0
 }
 
+/// 功能:将进程中一个已经打开的文件复制一份并分配到一个新的文件描述符中。
+/// 参数:fd 表示进程中一个已经打开的文件的文件描述符。
+/// 返回值:如果出现了错误则返回 -1,否则能够访问已打开文件的新文件描述符。
+/// 可能的错误原因是:传入的 fd 并不对应一个合法的已打开文件。
+/// syscall ID:24
 pub fn sys_dup(fd: usize) -> isize {
     let task = current_task().unwrap();
     let mut inner = task.acquire_inner_lock();
@@ -108,4 +174,151 @@ pub fn sys_dup(fd: usize) -> isize {
     let new_fd = inner.alloc_fd();
     inner.fd_table[new_fd] = Some(Arc::clone(inner.fd_table[fd].as_ref().unwrap()));
     new_fd as isize
+}
+
+//把邮箱里面的内容写到缓冲区
+pub fn sys_mail_read(buf: *mut u8, l: usize)->isize{
+    //因为这里是直接复用了pipe,所以会受到一些限制。比如说,pipe如果写超了,就会直接切换进程······
+    //然后你就会发现自己好像死锁了。
+    //除非自己模仿pipe重写一个真正的mail,否则就只能这样在进入函数之前
+    //一定要判断有没有超范围啊
+    let mut len = l;
+    //这是测试是否有报文可以读
+    if len==0{
+        warn!("[mail_read] len=0,may fail");
+        if let Some(not_empty) = mail_not_empty_me(){
+            if(not_empty){
+                return 0 as isize;
+            }
+        }else{
+
+        }
+        return -1 as isize;
+    }
+    if len>MAIL_SIZE{
+        len = MAIL_SIZE;
+    }
+    //如果地址非法,就不能读了
+    let token = current_user_token();
+    if !check_byte_buffer_valid(token, buf, len){
+        warn!("[mail_read] invalid byte buffer address,may fail");
+        return -1 as isize;
+    }
+
+    //如果是要给自己写
+    //就不能在TASK_MANAGER里面找,找不到的
+    //这个不需要返回
+    if let Some(read_fd) = mail_get_from_me(){
+        //如果返回了文件描述符,也就是可以写的意思
+        // let task = current_task().unwrap();
+        // let mut inner = task.acquire_inner_lock();
+        // inner.fd_table[read_fd] = Some(mpipe_read);
+        // drop(inner);
+        let len = sys_read(read_fd,buf,len);
+        if sys_close(read_fd)!= -1 {
+            return len as isize;
+        }else{
+            return -1 as isize;
+        }
+    }else{
+        warn!("[mail_read] can't get mail,may fail");
+        return -1 as isize;
+    }
+}
+
+//把缓冲区里面的内容写进进程pid的邮箱
+pub fn sys_mail_write(pid: usize, buf: *mut u8, l: usize)->isize{
+    //因为这里是直接复用了pipe,所以会受到一些限制。比如说,pipe如果写超了,就会直接切换进程······
+    //然后你就会发现自己好像死锁了。
+    //除非自己模仿pipe重写一个真正的mail,否则就只能这样在进入函数之前
+    //一定要判断有没有超范围啊
+    
+    //地址错误是需要先检查的。。。
+    let mut len = l;
+    let p = sys_getpid() as isize;//my pid is p
+    if len==0{
+        warn!("[mail_write] len=0,may fail");
+        if (p == pid as isize){
+            if let Some(not_full) = mail_not_full_me(){
+                if(not_full){
+                    return 0 as isize;
+                }else{
+
+                }
+            }
+            return -1 as isize;
+        }else{
+            if let Some(not_full) = mail_not_full_pid(pid){
+                if(not_full){
+                    return 0 as isize;
+                }else{
+
+                }
+            }
+            return -1 as isize;
+        }
+    }
+    if len>MAIL_SIZE{
+        warn!("[mail_write] len too long,continue");
+        len = MAIL_SIZE;
+    }
+    
+    if (p == pid as isize){
+        //先检查地址错误
+        //如果地址非法,就不能读了
+        let token = current_user_token();
+        if !check_byte_buffer_valid(token, buf, len){
+            return -1 as isize;
+        }
+        //如果是要给自己写
+        //就不能在TASK_MANAGER里面找,找不到的
+        if let Some(mpipe_write) = mail_write_to_me(){
+            //如果返回了文件描述符,也就是可以写的意思
+            let task = current_task().unwrap();
+            let mut inner = task.acquire_inner_lock();
+            let write_fd = inner.alloc_fd();
+            inner.fd_table[write_fd] = Some(mpipe_write);
+            drop(inner);
+            if sys_write(write_fd,buf,len) != -1{
+                if sys_close(write_fd)!= -1 {
+                    return len as isize;
+                }else{
+                    warn!("[mail_write] sys_close fail,fail");
+                    return -1 as isize;
+                }
+            }else{
+                warn!("[mail_write] sys_write fail,fail");
+                return -1 as isize;
+            }
+        }else{
+            warn!("[mail_write] can't get mail,fail");
+            return -1 as isize;
+        }
+    }else{
+        //先检查地址错误
+        //如果地址非法,就不能读了
+        if let Some(token) = mail_user_token_pid(pid){
+            if !check_byte_buffer_valid(token, buf, len){
+                return -1 as isize;
+            }
+        }else{
+            return -1 as isize;
+        }
+        
+        //否则就在没有正在运行的task里面找是否可以新建一封mail
+        if let Some(mpipe_write) = mail_write_to_pid(pid){
+            //如果返回了文件描述符,也就是可以写的意思
+            let task = current_task().unwrap();
+            let mut inner = task.acquire_inner_lock();
+            let write_fd = inner.alloc_fd();
+            inner.fd_table[write_fd] = Some(mpipe_write);
+            drop(inner);
+            sys_write(write_fd,buf,len);
+            sys_close(write_fd);
+            return len as isize;
+        }else{
+            warn!("[mail_write] can't get mail,fail");
+            return -1 as isize;
+        }
+    }
 }
\ No newline at end of file
diff --git a/os/src/syscall/memory.rs b/os/src/syscall/memory.rs
new file mode 100644
index 0000000000000000000000000000000000000000..090eba8c1bdf3fa0a598f80e808a55d12f5d2194
--- /dev/null
+++ b/os/src/syscall/memory.rs
@@ -0,0 +1,58 @@
+use crate::task::{
+    mmap,
+    munmap,
+};
+
+use crate::config::{
+    PAGE_SIZE,
+    MEMORY_MAP_SIZE,
+};
+
+pub fn sys_mmap(start: usize, len: usize, port: usize) -> isize{
+    debug!("sys_mmap...start = {:#x}, len = {}, port = {}...",start,len,port);
+    //需要做几件事:
+    //1.检查数据类型是否合法:
+    //- start和页对齐
+    //- len不能过大不能过小
+    //- port满足一些要求
+    if start % PAGE_SIZE != 0 {
+        return -1 as isize;
+    }
+    if len == 0 {
+        return 0 as isize;
+    }
+    if len > MEMORY_MAP_SIZE {
+        return -1 as isize;
+    }
+    if (port & !0x7 != 0)||(port & 0x7 == 0) {
+        return -1 as isize;
+    }
+    // return -1 as isize;
+    return mmap(start, len, port);
+}
+
+pub fn sys_munmap(start: usize, len: usize) -> isize{
+    debug!("sys_munmap...start = {:#x}, len = {}",start,len);
+    //需要做几件事:
+    //1.检查数据类型是否合法:
+    //- start和页对齐
+    //- len不能过大不能过小
+    if start % PAGE_SIZE != 0 {
+        return -1 as isize;
+    }
+    if len == 0 {
+        return 0 as isize;
+    }
+    if len > MEMORY_MAP_SIZE {
+        return -1 as isize;
+    }
+    //2.分配。如果还有空间分配成功就返回size,注意返回的是字节数而不是页数,分配失败就返回-1
+
+    let result = munmap(start, len);
+    if result == -1 {
+        return -1 as isize;
+    }
+    else {
+        return result * (PAGE_SIZE as isize);
+    }
+}
\ No newline at end of file
diff --git a/os/src/syscall/mod.rs b/os/src/syscall/mod.rs
index 4683d055b9656fcc84a706aff83e68190f53726a..365264635d520ae58f542ba745e6c7c4694bfd9b 100644
--- a/os/src/syscall/mod.rs
+++ b/os/src/syscall/mod.rs
@@ -11,14 +11,34 @@ const SYSCALL_GETPID: usize = 172;
 const SYSCALL_FORK: usize = 220;
 const SYSCALL_EXEC: usize = 221;
 const SYSCALL_WAITPID: usize = 260;
+// const SYSCALL_GETTIMEOFDAY: usize = 169;
+const SYSCALL_SET_PRIORITY: usize = 140;
+const SYSCALL_MMAP: usize = 222;
+const SYSCALL_MUNMAP: usize = 215;
+const SYSCALL_SPAWN: usize = 400;
+const SYSCALL_MAIL_READ: usize = 401;
+const SYSCALL_MAIL_WRITE: usize = 402;
+//=====================lab7===============================
+const SYSCALL_UNLINKAT: usize = 35;
+const SYSCALL_LINKAT: usize = 37;
+const SYSCALL_FSTAT: usize = 80;
 
 mod fs;
 mod process;
+mod memory;
+mod trap;
+mod flinker;
 
 use fs::*;
 use process::*;
+use memory::*;
+use trap::*;
+use crate::timer::TimeVal;
+use flinker::*;
+//现在的问题就是TimeVal为什么地址不能用?照理来说应该在创建的时候自动修改了才对
 
 pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize {
+    trace!("in syscall...{},{},{},{}",syscall_id,args[0],args[1],args[2]);
     match syscall_id {
         SYSCALL_DUP=> sys_dup(args[0]),
         SYSCALL_OPEN => sys_open(args[0] as *const u8, args[1] as u32),
@@ -28,12 +48,45 @@ pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize {
         SYSCALL_WRITE => sys_write(args[0], args[1] as *const u8, args[2]),
         SYSCALL_EXIT => sys_exit(args[0] as i32),
         SYSCALL_YIELD => sys_yield(),
-        SYSCALL_GET_TIME => sys_get_time(),
+
+        // SYSCALL_GET_TIME => sys_get_time(),
+        //[time as *const _ as usize, tz, 0]
+        //lab3
+        SYSCALL_GET_TIME => sys_get_time(args[0] as *mut TimeVal, args[1]),
+        SYSCALL_SET_PRIORITY => sys_set_priority(args[0]),
+
+        //lab4
+        SYSCALL_MMAP => sys_mmap(args[0],args[1],args[2]),
+        SYSCALL_MUNMAP => sys_munmap(args[0],args[1]),
+
+        //lab5
         SYSCALL_GETPID => sys_getpid(),
         SYSCALL_FORK => sys_fork(),
         SYSCALL_EXEC => sys_exec(args[0] as *const u8, args[1] as *const usize),
         SYSCALL_WAITPID => sys_waitpid(args[0] as isize, args[1] as *mut i32),
+        SYSCALL_SPAWN => sys_spawn(args[0] as *const u8, args[1] as *const usize),
+
+        //lab6
+        SYSCALL_MAIL_READ => sys_mail_read(args[0] as *mut u8, args[1]),
+        SYSCALL_MAIL_WRITE => sys_mail_write(args[0], args[1] as *mut u8, args[2]),
+
+        //lab7
+        //=====================lab7===============================
+        SYSCALL_FSTAT => sys_fstat(args[0], args[1] as *mut Stat),
+        //important: permantly
+        SYSCALL_UNLINKAT => sys_unlinkat(args[0] as isize, args[1] as *const u8, args[2] as u32),
+        // SYSCALL_LINKAT => sys_linkat(args[0] as *const u8, args[1] as *const u8, args[2]),
+
         _ => panic!("Unsupported syscall_id: {}", syscall_id),
     }
 }
 
+//问题:有些syscall传进来5个参数,我该怎么调用它啊???
+//现在暂且不会解决,所以先用着syscall的调用
+pub fn syscall5(syscall_id: usize, args: [usize; 5]) -> isize{
+    match syscall_id {
+        SYSCALL_LINKAT => sys_linkat5(args[0] as isize, args[1] as *const u8, args[2] as isize, args[3] as *const u8, args[4] as u32),
+        // _ => panic!("Unsupported syscall5_id: {}", syscall_id),
+        _ => syscall(syscall_id, [args[0], args[1], args[2]]),
+    }
+}
\ No newline at end of file
diff --git a/os/src/syscall/process.rs b/os/src/syscall/process.rs
index bfd592d6f197d0e7536746db1942e3111385936d..299b51784ce3ce06ad619a26ffb0570bdc13a073 100644
--- a/os/src/syscall/process.rs
+++ b/os/src/syscall/process.rs
@@ -4,8 +4,11 @@ use crate::task::{
     current_task,
     current_user_token,
     add_task,
+
+    TaskPriority,
+    set_priority,
+    // current_user_v2p,
 };
-use crate::timer::get_time_ms;
 use crate::mm::{
     translated_str,
     translated_refmut,
@@ -15,11 +18,18 @@ use crate::fs::{
     open_file,
     OpenFlags,
 };
+
 use alloc::sync::Arc;
 use alloc::vec::Vec;
 use alloc::string::String;
 
+use crate::config::{
+    ISIZI_MAX,
+};
+
+
 pub fn sys_exit(exit_code: i32) -> ! {
+    kernel_println!("[kernel] Application exited with code {}", exit_code);
     exit_current_and_run_next(exit_code);
     panic!("Unreachable in sys_exit!");
 }
@@ -29,18 +39,19 @@ pub fn sys_yield() -> isize {
     0
 }
 
-pub fn sys_get_time() -> isize {
-    get_time_ms() as isize
-}
-
 pub fn sys_getpid() -> isize {
     current_task().unwrap().pid.0 as isize
 }
 
 pub fn sys_fork() -> isize {
+    let token = current_user_token();
+    info!("sys_fork...current user toker is {:#x}",token);
+
     let current_task = current_task().unwrap();
     let new_task = current_task.fork();
+    //出现一个新的task之后会自动分配一个pid的
     let new_pid = new_task.pid.0;
+    info!("sys_fork...new pid is {}",new_pid);
     // modify trap context of new_task, because it returns immediately after switching
     let trap_cx = new_task.acquire_inner_lock().get_trap_cx();
     // we do not have to move to next instruction since we have done it before
@@ -48,6 +59,8 @@ pub fn sys_fork() -> isize {
     trap_cx.x[10] = 0;
     // add new task to scheduler
     add_task(new_task);
+    //soga,因为fork出来的会自动加入进程池里面
+    //简单点说就是,放在Manager那个队列里面,等着被调用哦
     new_pid as isize
 }
 
@@ -63,6 +76,7 @@ pub fn sys_exec(path: *const u8, mut args: *const usize) -> isize {
         args_vec.push(translated_str(token, arg_str_ptr as *const u8));
         unsafe { args = args.add(1); }
     }
+    info!("sys_exec...path is {}",path.as_str());
     if let Some(app_inode) = open_file(path.as_str(), OpenFlags::RDONLY) {
         let all_data = app_inode.read_all();
         let task = current_task().unwrap();
@@ -71,14 +85,71 @@ pub fn sys_exec(path: *const u8, mut args: *const usize) -> isize {
         // return argc because cx.x[10] will be covered with it later
         argc as isize
     } else {
+        warn!("[exec] app not found, fail");
         -1
     }
 }
 
+pub fn sys_spawn(path: *const u8, mut args: *const usize) -> isize{
+    //换一种思路······spawn1的内部实现的效果是把指定的函数加载进去
+    //然后再来一个函数spawn2来负责执行这个函数
+
+    // let token = current_user_token();
+    // let path = translated_str(token, path);//总之就是把*const u8翻译成String类型
+    // info!("sys_spawn...{}",path.as_str());
+    //处理要打开的应用信息
+    let token = current_user_token();
+    //path is String
+    let path = translated_str(token, path);
+    let mut args_vec: Vec<String> = Vec::new();
+    // spawn暂时不支持添加参数了吧
+    // loop {
+    //     let arg_str_ptr = *translated_ref(token, args);
+    //     if arg_str_ptr == 0 {
+    //         break;
+    //     }
+    //     args_vec.push(translated_str(token, arg_str_ptr as *const u8));
+    //     unsafe { args = args.add(1); }
+    // }
+    info!("sys_spawn...path is {}",path.as_str());
+    if let Some(app_inode) = open_file(path.as_str(), OpenFlags::RDONLY) {
+        let all_data = app_inode.read_all();
+        let current_task = current_task().unwrap();
+        let argc = args_vec.len();
+
+        let new_task = current_task.spawn_from(all_data.as_slice(), args_vec);
+        // task.exec(all_data.as_slice(), args_vec);
+        let new_pid = new_task.pid.0;
+        add_task(new_task);
+        // return argc because cx.x[10] will be covered with it later
+        // return argc as isize;
+        return new_pid as isize
+    } else {
+        warn!("[spawn] app not found, fail");
+        return -1 as isize;
+    }
+    // if let Some(data) = get_app_data_by_name(path.as_str()) {
+    //     let current_task = current_task().unwrap();
+    //     let new_task = current_task.spawn_from(data);
+    //     //出现一个新的task之后会自动分配一个pid的
+    //     let new_pid = new_task.pid.0;
+    //     info!("sys_spawn_from...new pid is {}",new_pid);
+    //     // add new task to scheduler
+    //     add_task(new_task);
+    //     //soga,因为fork出来的会自动加入进程池里面
+    //     //简单点说就是,放在Manager那个队列里面,等着被调用哦
+    //     new_pid as isize
+    // } else {
+    //     -1
+    // }
+}
+
 /// If there is not a child process whose pid is same as given, return -1.
 /// Else if there is a child process but it is still running, return -2.
+/// 否则就返回child pid的编号
 pub fn sys_waitpid(pid: isize, exit_code_ptr: *mut i32) -> isize {
     let task = current_task().unwrap();
+    // kernel_println!("finding {}' children...",task.pid.0);
     // find a child process
 
     // ---- hold current PCB lock
@@ -95,21 +166,37 @@ pub fn sys_waitpid(pid: isize, exit_code_ptr: *mut i32) -> isize {
         .enumerate()
         .find(|(_, p)| {
             // ++++ temporarily hold child PCB lock
-            p.acquire_inner_lock().is_zombie() && (pid == -1 || pid as usize == p.getpid())
+            (p.acquire_inner_lock().is_zombie()) && (pid == -1 || pid as usize == p.getpid())
             // ++++ release child PCB lock
         });
     if let Some((idx, _)) = pair {
         let child = inner.children.remove(idx);
         // confirm that child will be deallocated after being removed from children list
         assert_eq!(Arc::strong_count(&child), 1);
+        // println!("[kernel] find pid :{}",child.pid.0);
+        // confirm that child will be deallocated after removing from children list
+        // assert_eq!(Arc::strong_count(&child), 1);
         let found_pid = child.getpid();
         // ++++ temporarily hold child lock
         let exit_code = child.acquire_inner_lock().exit_code;
         // ++++ release child PCB lock
         *translated_refmut(inner.memory_set.token(), exit_code_ptr) = exit_code;
+        kernel_println!("find pid have done :{}",found_pid);
         found_pid as isize
     } else {
+        // println!("[kernel] children not found");
         -2
     }
     // ---- release current PCB lock automatically
-}
\ No newline at end of file
+}
+
+//sys_set_priority
+pub fn sys_set_priority(prio: usize) -> isize{
+    debug!("[kernel] sys_set_priority...{}",prio);
+    if prio>=2 && prio<=ISIZI_MAX as usize {
+        set_priority(TaskPriority::from(prio));
+        return prio as isize
+    }
+    return -1 as isize
+}
+
diff --git a/os/src/syscall/trap.rs b/os/src/syscall/trap.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a4cf3e44f96c00733777a327fae3b556422b537f
--- /dev/null
+++ b/os/src/syscall/trap.rs
@@ -0,0 +1,58 @@
+use crate::timer::{
+    // get_time,
+    get_time_ms,
+    TimeVal
+};
+use crate::mm::{
+    VirtAddr,
+};
+
+use crate::task::{
+    current_user_v2p,
+};
+
+// pub fn sys_get_time() -> isize {
+//     get_time_ms() as isize
+// }
+
+
+pub fn sys_get_time(ts: *mut TimeVal, _tz: usize) -> isize{
+    info!("in sys_get_time...{:#x},{}",ts as usize,_tz);
+    let t = get_time_ms() as usize;
+
+    // let pa_top = KERNEL_SPACE.lock().v2p(VirtAddr::from(kernel_stack_top)).unwrap();
+    //仿佛明白了······因为现在处理系统调用肯定是运行在内核态
+    //但是这里给的是用户态的虚拟地址。那这就确实是有问题的
+    //因此这里大概是真的需要取出物理地址,然后访问物理地址。
+    //因为OS在页表初始化的时候以及建立了物理地址到物理地址的映射,因此直接访问物理地址也是没问题的吧大概
+    let pa = current_user_v2p(VirtAddr::from(ts as usize));
+    //todo:注意一下sec和usec不在一页的情况
+    //但是似乎概率比较小,就先不管了吧
+    match pa {
+        Some(pa_t)=>{
+            // let pa_ts = usize::from(pa.unwrap()) as *mut TimeVal;
+            let pa_ts = usize::from(pa_t) as *mut TimeVal;
+            info!("in sys_get_time...physics addr is {:#x}",pa_ts as usize);
+            unsafe{
+                //为什么无法取出地址?先访问物理地址试试看啦
+                match (*pa_ts){//这里使用指针就跑飞了。为什么?
+                    //是因为虚拟地址的问题吗?
+                    TimeVal => {
+                        info!("sec = {}, usec = {}",(*pa_ts).sec,(*pa_ts).usec);
+                        (*pa_ts).sec = t/1000;
+                        (*pa_ts).usec = t*1000;
+                        info!("sec = {}, usec = {}",(*pa_ts).sec,(*pa_ts).usec);
+                    }
+                    _ => {
+                        warn!("[sys_get_time] NULL");
+                    },
+                }
+            }
+        },
+        _ =>{
+            warn!("[get_time]  NONE");
+        }
+    }
+    debug!("sys_get_time return...");
+    return 0 as isize;
+}
diff --git a/os/src/task/context.rs b/os/src/task/context.rs
index 340bc098ea8ec1e16e054c41cb1d534d6bde26ce..573bf3335a06a29771f3a896289ac5b9ece174b4 100644
--- a/os/src/task/context.rs
+++ b/os/src/task/context.rs
@@ -2,12 +2,12 @@ use crate::trap::trap_return;
 
 #[repr(C)]
 pub struct TaskContext {
-    ra: usize,
-    s: [usize; 12],
+    pub ra: usize,
+    pub s: [usize; 12],
 }
 
 impl TaskContext {
-    pub fn goto_trap_return() -> Self {
+    pub fn goto_trap_return() -> Self {//这里是trap return的上下文,把返回地址写进了ra寄存器里面
         Self {
             ra: trap_return as usize,
             s: [0; 12],
diff --git a/os/src/task/manager.rs b/os/src/task/manager.rs
index ed22391622d406508d2c15d04951169f1fd24132..fe04280f6411d705f26bc191397f2990a181629b 100644
--- a/os/src/task/manager.rs
+++ b/os/src/task/manager.rs
@@ -1,4 +1,9 @@
-use super::TaskControlBlock;
+use super::{
+    TaskControlBlock,
+};
+use crate::fs::{
+    MPipe,
+};
 use alloc::collections::VecDeque;
 use alloc::sync::Arc;
 use spin::Mutex;
@@ -19,6 +24,50 @@ impl TaskManager {
     pub fn fetch(&mut self) -> Option<Arc<TaskControlBlock>> {
         self.ready_queue.pop_front()
     }
+    //事实证明,这样写是可以的!!!
+    //但是需要注意一点。如果是自己给自己写,会发现并不能在这里找到。因为正在运行的进程会被从TaskManager里面取出来
+    //放在Processor里面运行
+    pub fn call_test(&mut self,pid: usize){
+        for item in self.ready_queue.iter_mut(){
+            debug!("TaskManager::call_test...pid is {}",item.pid.0);
+            if item.pid.0 == pid {
+                item.call_test();
+            }
+        }
+    }
+    pub fn mail_write_to_pid(&mut self, pid:usize)-> Option<Arc<MPipe>>{
+        for item in self.ready_queue.iter_mut(){
+            debug!("TaskManager::mail_write_to_pid...pid is {}",item.pid.0);
+            if item.pid.0 == pid {
+                return item.mail_create_from_pipe();
+            }
+        }
+        None
+    }
+    pub fn mail_not_full_pid(&mut self, pid:usize)-> Option<bool>{
+        for item in self.ready_queue.iter_mut(){
+            debug!("TaskManager::mail_not_full_pid...pid is {}",item.pid.0);
+            if item.pid.0 == pid {
+                return item.mail_not_full();
+            }
+        }
+        None
+    }
+    pub fn mail_user_token_pid(&mut self,pid:usize)->Option<usize>{
+        for item in self.ready_queue.iter_mut(){
+            debug!("TaskManager::mail_not_full_pid...pid is {}",item.pid.0);
+            if item.pid.0 == pid {
+                return Some(item.acquire_inner_lock().get_user_token())
+            }
+        }
+        None
+    }
+    // pub fn mail_create_from_pipe(&self)->Option<Arc<Pipe>>{
+        // pub fn current_user_token() -> usize {
+        //     let task = current_task().unwrap();
+        //     let token = task.acquire_inner_lock().get_user_token();
+        //     token
+        // }
 }
 
 lazy_static! {
@@ -31,4 +80,23 @@ pub fn add_task(task: Arc<TaskControlBlock>) {
 
 pub fn fetch_task() -> Option<Arc<TaskControlBlock>> {
     TASK_MANAGER.lock().fetch()
+}
+
+pub fn call_test(pid: usize){
+    kernel_println!("call_test...pid is {}",pid);
+    TASK_MANAGER.lock().call_test(pid);
+}
+
+pub fn mail_write_to_pid(pid:usize)-> Option<Arc<MPipe>>{
+    debug!("mail_write_to_pid...pid is {}",pid);
+    TASK_MANAGER.lock().mail_write_to_pid(pid)
+}
+
+pub fn mail_not_full_pid(pid:usize)-> Option<bool>{
+    debug!("mail_write_to_pid...pid is {}",pid);
+    TASK_MANAGER.lock().mail_not_full_pid(pid)
+}
+
+pub fn mail_user_token_pid(pid:usize) ->Option<usize>{
+    TASK_MANAGER.lock().mail_user_token_pid(pid)
 }
\ No newline at end of file
diff --git a/os/src/task/mod.rs b/os/src/task/mod.rs
index e943016c4a1111d6836025dad15c886990e09520..8cb1b5b013ea296e7a3cc088cce11f52252b2913 100644
--- a/os/src/task/mod.rs
+++ b/os/src/task/mod.rs
@@ -4,13 +4,19 @@ mod task;
 mod manager;
 mod processor;
 mod pid;
+mod stride;
+mod priority;
 
 use crate::fs::{open_file, OpenFlags};
 use switch::__switch;
 use task::{TaskControlBlock, TaskStatus};
 use alloc::sync::Arc;
-use manager::fetch_task;
 use lazy_static::*;
+
+//=====================================================================
+// 本文件中是要向其他模块提供的代码
+//=====================================================================
+
 pub use context::TaskContext;
 
 pub use processor::{
@@ -20,10 +26,33 @@ pub use processor::{
     current_trap_cx,
     take_current_task,
     schedule,
+
+    set_priority,
+    mmap,
+    munmap,
+    current_user_v2p,
+    mail_write_to_me,
+    mail_get_from_me,
+    mail_not_full_me,
+    mail_not_empty_me,
+};
+pub use manager::{
+    add_task,
+    fetch_task,
+    call_test,
+    mail_write_to_pid,
+    mail_not_full_pid,
+    mail_user_token_pid,
 };
-pub use manager::add_task;
 pub use pid::{PidHandle, pid_alloc, KernelStack};
 
+pub use priority::{
+    TaskPriority,
+};
+
+//=====================================================================
+// 以下部分的代码都和进程调度相关
+//=====================================================================
 pub fn suspend_current_and_run_next() {
     // There must be an application running.
     let task = take_current_task().unwrap();
@@ -45,6 +74,7 @@ pub fn suspend_current_and_run_next() {
 pub fn exit_current_and_run_next(exit_code: i32) {
     // take from Processor
     let task = take_current_task().unwrap();
+    kernel_println!("exit current task is {}",task.pid.0);
     // **** hold current PCB lock
     let mut inner = task.acquire_inner_lock();
     // Change status to Zombie
@@ -86,3 +116,7 @@ lazy_static! {
 pub fn add_initproc() {
     add_task(INITPROC.clone());
 }
+
+//=====================================================================
+// 以下部分的代码是为了实现系统调用。目前支持的和进程相关的系统调用,有:
+//=====================================================================
diff --git a/os/src/task/pid.rs b/os/src/task/pid.rs
index 7cfdb83d8176817d671c118a1adddbe9f7fbebb4..17fc62edc23a19fb2eb508dc1d9c8a01bd1f5d36 100644
--- a/os/src/task/pid.rs
+++ b/os/src/task/pid.rs
@@ -62,6 +62,7 @@ pub fn kernel_stack_position(app_id: usize) -> (usize, usize) {
     (bottom, top)
 }
 
+//内核栈可以根据进程编号来自动为进程分配地址哦
 pub struct KernelStack {
     pid: usize,
 }
diff --git a/os/src/task/priority.rs b/os/src/task/priority.rs
new file mode 100644
index 0000000000000000000000000000000000000000..c16bb1e9682c9b1a52dc4ff2877dd1083a819ed6
--- /dev/null
+++ b/os/src/task/priority.rs
@@ -0,0 +1,24 @@
+
+use crate::config::TASK_PRIORITY_INIT;
+
+#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
+pub struct TaskPriority(pub usize);
+
+impl From<usize> for TaskPriority{
+    fn from(v: usize) -> Self { Self(v) }
+}
+impl From<TaskPriority> for usize {
+    fn from(v: TaskPriority) -> Self { v.0 }
+}
+
+impl TaskPriority{
+    pub fn new() -> Self {
+        TaskPriority(TASK_PRIORITY_INIT)
+    }
+    pub fn get_priority(&self)-> TaskPriority {
+        TaskPriority(self.0)
+    }
+    pub fn set_priority(&mut self, prio: TaskPriority){
+        self.0 = prio.0;
+    }
+}
\ No newline at end of file
diff --git a/os/src/task/processor.rs b/os/src/task/processor.rs
index e6895c408b0c4024aea406d753221543671145a7..a0384c15c1ac51c5027c030e560801e65411b9d6 100644
--- a/os/src/task/processor.rs
+++ b/os/src/task/processor.rs
@@ -2,7 +2,19 @@ use super::TaskControlBlock;
 use alloc::sync::Arc;
 use core::cell::RefCell;
 use lazy_static::*;
-use super::{fetch_task, TaskStatus};
+use super::{
+    fetch_task, 
+    TaskStatus,
+    TaskPriority,
+    // set_priority,
+};
+use crate::mm::{
+    VirtAddr,
+    PhysAddr,
+};
+use crate::fs::{
+    MPipe,
+};
 use super::__switch;
 use crate::trap::TrapContext;
 
@@ -40,7 +52,8 @@ impl Processor {
                 task_inner.task_status = TaskStatus::Running;
                 drop(task_inner);
                 // release
-                self.inner.borrow_mut().current = Some(task);
+                // 先把各种变量变了,然后current修改,然后进入switch函数里面
+                self.inner.borrow_mut().current = Some(task);//可以认为是转移了所有权
                 unsafe {
                     __switch(
                         idle_task_cx_ptr2,
@@ -50,9 +63,11 @@ impl Processor {
             }
         }
     }
+    //问题:为什么需要把当前任务“取出来”?
     pub fn take_current(&self) -> Option<Arc<TaskControlBlock>> {
         self.inner.borrow_mut().current.take()
     }
+    //这个函数会把task复制一份,所以并不像上面那个函数一样直接所有权都变了
     pub fn current(&self) -> Option<Arc<TaskControlBlock>> {
         self.inner.borrow().current.as_ref().map(|task| Arc::clone(task))
     }
@@ -84,6 +99,54 @@ pub fn current_trap_cx() -> &'static mut TrapContext {
     current_task().unwrap().acquire_inner_lock().get_trap_cx()
 }
 
+
+//=====================================================================
+// 以下部分的代码是为了实现系统调用。目前支持的和进程相关的系统调用,有:
+//=====================================================================
+pub fn set_priority(prio:TaskPriority){
+    let task = current_task().unwrap();
+    task.set_priority(prio);
+}
+
+pub fn mmap(start: usize, len: usize, port: usize) -> isize{
+    let task = current_task().unwrap();
+    task.mmap(start, len, port)
+}//函数结束自动释放锁
+
+pub fn munmap(start: usize, len: usize) -> isize{
+    let task = current_task().unwrap();
+    task.munmap(start,len)
+}
+
+pub fn current_user_v2p(va:VirtAddr)->Option<PhysAddr>{
+    let task = current_task().unwrap();
+    task.v2p(va)
+}
+
+pub fn mail_write_to_me()-> Option<Arc<MPipe>>{
+    let task = current_task().unwrap();
+    debug!("PROCESSOR::mail_write_to_me...");
+    task.mail_create_from_pipe()
+}
+
+//注意!这里返回的是文件描述符
+pub fn mail_get_from_me()->Option<usize>{
+    let task = current_task().unwrap();
+    debug!("PROCESSOR::mail_get_from_me...");
+    task.mail_get()
+}
+
+pub fn mail_not_full_me()->Option<bool>{
+    let task = current_task().unwrap();
+    task.mail_not_full()
+}
+
+pub fn mail_not_empty_me()->Option<bool>{
+    let task = current_task().unwrap();
+    task.mail_not_empty()
+}
+
+
 pub fn schedule(switched_task_cx_ptr2: *const usize) {
     let idle_task_cx_ptr2 = PROCESSOR.get_idle_task_cx_ptr2();
     unsafe {
diff --git a/os/src/task/stride.rs b/os/src/task/stride.rs
new file mode 100644
index 0000000000000000000000000000000000000000..c5a6d9c063d670bfb90afb491390d55dd7fbacaf
--- /dev/null
+++ b/os/src/task/stride.rs
@@ -0,0 +1,42 @@
+// // for stride
+// // 代码结构还需要再优化一下
+// extern crate alloc;
+
+// use crate::config::BIG_STRIDE;
+// use super::get_task_priority;
+
+
+// /*
+// 注意:只有处于ready状态的进程会被放在这里面。
+// 因此这个调度算法只需要简单进行分装。
+// 注意!!!但是在task管理的部分,每次切换进程等进程状态改变时也要改变堆
+// */
+
+// #[derive(Copy, Clone, PartialEq)]
+// pub struct TaskStride{
+//     task: usize,//这个进程的编号
+//     stride: usize
+// }
+// impl TaskStride{
+//     pub fn new(tsk:usize)-> Self{
+//         TaskStride{
+//             task:tsk,
+//             stride:0 as usize,
+//         }
+//     }
+//     // pub fn set_task_number(&mut self,tsk:usize){
+//     //     self.task = tsk;
+//     // }
+//     pub fn get_task_number(&self)->usize{
+//         self.task
+//     }
+//     pub fn get_my_stride(&self) -> usize{
+//         self.stride
+//     }
+//     fn get_stride_pass(&self)->usize{
+//         BIG_STRIDE / get_task_priority(self.get_task_number())
+//     }
+//     pub fn run_me(&mut self){
+//         self.stride += self.get_stride_pass();
+//     }
+// }
\ No newline at end of file
diff --git a/os/src/task/switch.S b/os/src/task/switch.S
index 262511fe7aae9f561b7873a4c1c987b2936bdc41..d346a757c22749e8a017fe9350219db44af13015 100644
--- a/os/src/task/switch.S
+++ b/os/src/task/switch.S
@@ -12,11 +12,13 @@ __switch:
     #     current_task_cx_ptr2: &*const TaskContext,
     #     next_task_cx_ptr2: &*const TaskContext
     # )
+    # a0作为第一个函数参数,保存的是函数返回地址的地址
     # push TaskContext to current sp and save its address to where a0 points to
     addi sp, sp, -13*8
-    sd sp, 0(a0)
+    sd sp, 0(a0) #将地址a0存储的东西(task_cx_ptr)拿出来赋值给sp
     # fill TaskContext with ra & s0-s11
-    sd ra, 0(sp)
+    sd ra, 0(sp) #地址为a0的地方,现在也是地址为sp的地方,存储的是TaskContext的开始地址,
+    # 同时也是TaskContext中的ra,所以把地址为a0的地方存储的地址拿出来交给ra
     .set n, 0
     .rept 12
         SAVE_SN %n
diff --git a/os/src/task/task.rs b/os/src/task/task.rs
index ee5fc53e1b51174a84778c329d303b530d6469b3..f7f1adb1bfc34a48869616d518c0eec16be4b453 100644
--- a/os/src/task/task.rs
+++ b/os/src/task/task.rs
@@ -4,18 +4,31 @@ use crate::mm::{
     KERNEL_SPACE, 
     VirtAddr,
     translated_refmut,
+    PhysAddr,
 };
 use crate::trap::{TrapContext, trap_handler};
 use crate::config::{TRAP_CONTEXT};
-use super::TaskContext;
+use super::{
+    TaskContext,
+    TaskPriority,
+};
 use super::{PidHandle, pid_alloc, KernelStack};
 use alloc::sync::{Weak, Arc};
 use alloc::vec;
 use alloc::vec::Vec;
 use alloc::string::String;
 use spin::{Mutex, MutexGuard};
-use crate::fs::{File, Stdin, Stdout};
+use crate::fs::{
+    File, 
+    Stdin, 
+    Stdout,
+    Mail,
+    MailBox,
+    MPipe,
+    make_mpipe,
+};
 
+// #[derive(Copy, Clone, PartialEq)]
 pub struct TaskControlBlock {
     // immutable
     pub pid: PidHandle,
@@ -25,15 +38,17 @@ pub struct TaskControlBlock {
 }
 
 pub struct TaskControlBlockInner {
-    pub trap_cx_ppn: PhysPageNum,
-    pub base_size: usize,
-    pub task_cx_ptr: usize,
-    pub task_status: TaskStatus,
-    pub memory_set: MemorySet,
-    pub parent: Option<Weak<TaskControlBlock>>,
-    pub children: Vec<Arc<TaskControlBlock>>,
+    pub trap_cx_ppn: PhysPageNum,//指出了应用地址空间中的 Trap 上下文(详见第四章)被放在的物理页帧的物理页号。
+    pub base_size: usize,//应用数据仅有可能出现在应用地址空间低于 base_size 字节的区域中。借助它我们可以清楚的知道应用有多少数据驻留在内存中。
+    pub task_cx_ptr: usize,//指出一个暂停的任务的任务上下文在内核地址空间(更确切的说是在自身内核栈)中的位置,用于任务切换。
+    pub task_status: TaskStatus,//维护当前进程的执行状态。
+    pub memory_set: MemorySet,//
+    pub task_priority: TaskPriority,//add
+    pub parent: Option<Weak<TaskControlBlock>>,//指向当前进程的父进程(如果存在的话)。注意我们使用 Weak 而非 Arc 来包裹另一个任务控制块,因此这个智能指针将不会影响父进程的引用计数。
+    pub children: Vec<Arc<TaskControlBlock>>,//则将当前进程的所有子进程的任务控制块以 Arc 智能指针的形式保存在一个向量中,这样才能够更方便的找到它们。
     pub exit_code: i32,
     pub fd_table: Vec<Option<Arc<dyn File + Send + Sync>>>,
+    pub mailbox:MailBox,//add
 }
 
 impl TaskControlBlockInner {
@@ -89,6 +104,7 @@ impl TaskControlBlock {
                 task_cx_ptr: task_cx_ptr as usize,
                 task_status: TaskStatus::Ready,
                 memory_set,
+                task_priority: TaskPriority::new(),
                 parent: None,
                 children: Vec::new(),
                 exit_code: 0,
@@ -100,6 +116,7 @@ impl TaskControlBlock {
                     // 2 -> stderr
                     Some(Arc::new(Stdout)),
                 ],
+                mailbox: MailBox::new(),
             }),
         };
         // prepare TrapContext in user space
@@ -145,6 +162,32 @@ impl TaskControlBlock {
         // make the user_sp aligned to 8B for k210 platform
         user_sp -= user_sp % core::mem::size_of::<usize>();
 
+        // 因为现在spawn不支持参数,所以就先这样吧
+        // push arguments on user stack
+        user_sp -= (args.len() + 1) * core::mem::size_of::<usize>();
+        let argv_base = user_sp;
+        let mut argv: Vec<_> = (0..=args.len())
+            .map(|arg| {
+                translated_refmut(
+                    memory_set.token(),
+                    (argv_base + arg * core::mem::size_of::<usize>()) as *mut usize
+                )
+            })
+            .collect();
+        *argv[args.len()] = 0;
+        for i in 0..args.len() {
+            user_sp -= args[i].len() + 1;
+            *argv[i] = user_sp;
+            let mut p = user_sp;
+            for c in args[i].as_bytes() {
+                *translated_refmut(memory_set.token(), p as *mut u8) = *c;
+                p += 1;
+            }
+            *translated_refmut(memory_set.token(), p as *mut u8) = 0;
+        }
+        // make the user_sp aligned to 8B for k210 platform
+        user_sp -= user_sp % core::mem::size_of::<usize>();
+
         // **** hold current PCB lock
         let mut inner = self.acquire_inner_lock();
         // substitute memory_set
@@ -164,13 +207,20 @@ impl TaskControlBlock {
         *inner.get_trap_cx() = trap_cx;
         // **** release current PCB lock
     }
+
+    //fork在做什么呢?
     pub fn fork(self: &Arc<TaskControlBlock>) -> Arc<TaskControlBlock> {
         // ---- hold parent PCB lock
         let mut parent_inner = self.acquire_inner_lock();
         // copy user space(include trap context)
+        //但是这个实际上用不到
         let memory_set = MemorySet::from_existed_user(
+            //复制一份一模一样的用户空间
+            //确实······感觉这里复制这一份出来,就是为了变成一个数据结构存起来,好像什么作用也没有。
+            //因为真的task被执行的时候,用到的是exec里面取出来的memory_set呀
             &parent_inner.memory_set
         );
+        //取出复制出来的空间的物理页号
         let trap_cx_ppn = memory_set
             .translate(VirtAddr::from(TRAP_CONTEXT).into())
             .unwrap()
@@ -181,6 +231,7 @@ impl TaskControlBlock {
         let kernel_stack_top = kernel_stack.get_top();
         // push a goto_trap_return task_cx on the top of kernel stack
         let task_cx_ptr = kernel_stack.push_on_top(TaskContext::goto_trap_return());
+
         // copy fd table
         let mut new_fd_table: Vec<Option<Arc<dyn File + Send + Sync>>> = Vec::new();
         for fd in parent_inner.fd_table.iter() {
@@ -199,10 +250,12 @@ impl TaskControlBlock {
                 task_cx_ptr: task_cx_ptr as usize,
                 task_status: TaskStatus::Ready,
                 memory_set,
-                parent: Some(Arc::downgrade(self)),
+                task_priority: TaskPriority::new(),
+                parent: Some(Arc::downgrade(self)),//似乎这里是弱引用?
                 children: Vec::new(),
                 exit_code: 0,
                 fd_table: new_fd_table,
+                mailbox: MailBox::new(),//邮箱并不能和父进程共享,不然几个函数之间互相传递信息就是在胡扯了
             }),
         });
         // add child
@@ -216,10 +269,213 @@ impl TaskControlBlock {
         task_control_block
         // ---- release parent PCB lock
     }
+    
+    // 这个函数假设已经在调用前取出了elf_data,可以直接拿来用了哦~
+    // 会返回一个新建的进程控制块.这个新建的进程控制块,会把
+    // pub fn spawn(&self, elf_data: &[u8]) -> isize{
+    pub fn spawn_from(self: &Arc<TaskControlBlock>, elf_data: &[u8], args: Vec<String>) -> Arc<TaskControlBlock>{
+        // memory_set with elf program headers/trampoline/trap context/user stack
+        let (memory_set, mut user_sp, entry_point) = MemorySet::from_elf(elf_data);
+        let trap_cx_ppn = memory_set
+            .translate(VirtAddr::from(TRAP_CONTEXT).into())
+            .unwrap()
+            .ppn();
+
+        // [lab7]add
+        // push arguments on user stack
+        user_sp -= (args.len() + 1) * core::mem::size_of::<usize>();
+        let argv_base = user_sp;
+        let mut argv: Vec<_> = (0..=args.len())
+            .map(|arg| {
+                translated_refmut(
+                    memory_set.token(),
+                    (argv_base + arg * core::mem::size_of::<usize>()) as *mut usize
+                )
+            })
+            .collect();
+        *argv[args.len()] = 0;
+        for i in 0..args.len() {
+            user_sp -= args[i].len() + 1;
+            *argv[i] = user_sp;
+            let mut p = user_sp;
+            for c in args[i].as_bytes() {
+                *translated_refmut(memory_set.token(), p as *mut u8) = *c;
+                p += 1;
+            }
+            *translated_refmut(memory_set.token(), p as *mut u8) = 0;
+        }
+        // make the user_sp aligned to 8B for k210 platform
+        user_sp -= user_sp % core::mem::size_of::<usize>();
+
+
+        // alloc a pid and a kernel stack in kernel space
+        let pid_handle = pid_alloc();
+        let kernel_stack = KernelStack::new(&pid_handle);
+        let kernel_stack_top = kernel_stack.get_top();
+        // push a task context which goes to trap_return to the top of kernel stack
+        let task_cx_ptr = kernel_stack.push_on_top(TaskContext::goto_trap_return());
+
+        //几乎就和new函数一样,唯一的区别在于new的时候还要建立进程之间的父子关系
+        // ---- hold parent PCB lock
+        let mut parent_inner = self.acquire_inner_lock();
+        // copy fd table
+        let mut new_fd_table: Vec<Option<Arc<dyn File + Send + Sync>>> = Vec::new();
+        for fd in parent_inner.fd_table.iter() {
+            if let Some(file) = fd {
+                new_fd_table.push(Some(file.clone()));
+            } else {
+                new_fd_table.push(None);
+            }
+        }
+        let task_control_block = Arc::new(TaskControlBlock {
+            pid: pid_handle,
+            kernel_stack,
+            inner: Mutex::new(TaskControlBlockInner {
+                trap_cx_ppn,
+                base_size: user_sp,
+                task_cx_ptr: task_cx_ptr as usize,
+                task_status: TaskStatus::Ready,
+                memory_set,
+                task_priority: TaskPriority::new(),
+                parent: Some(Arc::downgrade(self)),
+                children: Vec::new(),
+                exit_code: 0,
+                fd_table: new_fd_table,
+                mailbox: MailBox::new(),//邮箱并不能和父进程共享,不然几个函数之间互相传递信息就是在胡扯了
+            }),
+        });
+        // add child
+        parent_inner.children.push(task_control_block.clone());
+        // modify kernel_sp in trap_cx
+        // prepare TrapContext in user space
+
+        //let trap_cx  = task_control_block.acquire_inner_lock().get_trap_cx();
+        let mut trap_cx = TrapContext::app_init_context(
+            entry_point,
+            user_sp,
+            KERNEL_SPACE.lock().token(),
+            kernel_stack_top,
+            trap_handler as usize,
+        );
+        trap_cx.x[10] = args.len();
+        trap_cx.x[11] = argv_base;
+        *task_control_block.acquire_inner_lock().get_trap_cx() = trap_cx;
+        // return
+        task_control_block
+        // ---- release parent PCB lock
+    }
+
+//=====================================================================
+// sys_calls
+//=====================================================================
+
     pub fn getpid(&self) -> usize {
         self.pid.0
     }
 
+    pub fn set_priority(&self,prio:TaskPriority){
+        // **** hold current PCB lock
+        let mut inner = self.acquire_inner_lock();
+        inner.task_priority.set_priority(prio);
+        // **** release current PCB lock
+    }
+    pub fn get_priority(&self) -> TaskPriority{
+        // **** hold current PCB lock
+        let inner = self.acquire_inner_lock();
+        inner.task_priority.get_priority()
+        // **** release current PCB lock
+    }
+
+    pub fn mmap(&self,start: usize, len: usize, port: usize) -> isize{
+        // **** hold current PCB lock
+        let mut inner = self.acquire_inner_lock();
+        inner.memory_set.mmap(start, len, port) 
+        // **** release current PCB lock
+    }
+    pub fn munmap(&self,start: usize, len: usize) -> isize{
+        // **** hold current PCB lock
+        let mut inner = self.acquire_inner_lock();
+        inner.memory_set.munmap(start, len)
+        // **** release current PCB lock
+    }
+
+    pub fn v2p(&self,va:VirtAddr)->Option<PhysAddr>{
+        let inner = self.acquire_inner_lock();
+        inner.memory_set.v2p(va)
+    }
+
+    // let mut inner = task.acquire_inner_lock();
+    // let (pipe_read, pipe_write) = make_pipe();
+    // let read_fd = inner.alloc_fd();
+    // inner.fd_table[read_fd] = Some(pipe_read);
+    // let write_fd = inner.alloc_fd();
+    // inner.fd_table[write_fd] = Some(pipe_write);
+
+    //mail_write,意思是不知道是谁,反正有人给我写了一条邮件,要我存起来
+    //这也就表明,事实上我这个进程即使并没有在运行,也得能调用这个函数
+    //但是事实上这个函数并不关心邮件是什么,这个函数只管创建新的pipe,用来存储自己
+    //如果返回值是None,说明创建失败了
+    //需要返回的是文件描述符,是用来写的
+    pub fn mail_create_from_pipe(&self)->Option<Arc<MPipe>>{
+        kernel_println!("TaskControlblock::mail_create_from_pipe...pid is {}",self.pid.0);
+        // **** hold current PCB lock
+        let mut inner = self.acquire_inner_lock();
+        // kernel_println!("TaskControlblock::mail_create_from_pipe...pid is {}",self.pid.0);
+        //!!!!!一定要先判断
+        //如果邮箱满了那就不能写了
+        if inner.mailbox.can_add_mail(){
+            let (mpipe_read, mpipe_write) = make_mpipe();
+            let read_fd = inner.alloc_fd();
+            inner.fd_table[read_fd] = Some(mpipe_read);
+            // 给目标进程分配read_fd就可以了
+            // 写pipe的文件描述符不需要存哦
+            // let write_fd = inner.alloc_fd();
+            // inner.fd_table[write_fd] = Some(pipe_write);
+            let mail = Mail::new(read_fd);
+            inner.mailbox.add_mail(mail);
+            drop(inner);
+            Some(mpipe_write)
+        }else{
+            drop(inner);
+            None
+        }
+        // **** release current PCB lock
+    }
+    pub fn mail_get(&self)->Option<usize>{
+        kernel_println!("TaskControlblock::mail_get...pid is {}",self.pid.0);
+        // **** hold current PCB lock
+        let mut inner = self.acquire_inner_lock();
+        // kernel_println!("TaskControlblock::mail_create_from_pipe...pid is {}",self.pid.0);
+        //!!!!!一定要先判断
+        //如果邮箱满了那就不能写了
+        //因为之前会进行判断,如果没有字符要写,就不会创建邮箱了
+        //如果还有邮件
+        if let Some(mail) = inner.mailbox.get_mail(){
+            // if let Some(mpipe_read) = inner.fd_table[mail.get_read_fd()]{//就是文件描述符确实对应了一个mail
+            //     return S
+            // }else{
+            //     warn!("no mail to read");
+            // }
+            return Some(mail.get_read_fd());//返回能读mail的文件描述符即可
+            // 给目标进程分配read_fd就可以了
+            // 写pipe的文件描述符不需要存哦
+        }else{
+            None
+        }
+        // **** release current PCB lock
+    }
+    pub fn mail_not_full(&self)->Option<bool>{
+        let inner = self.acquire_inner_lock();
+        return Some(inner.mailbox.can_add_mail());
+    }
+    pub fn mail_not_empty(&self) ->Option<bool>{
+        let inner = self.acquire_inner_lock();
+        return Some(inner.mailbox.can_read_mail());
+    }
+    pub fn call_test(&self){
+        let inner = self.acquire_inner_lock();
+        kernel_println!("TaskControlBlock::call_test");
+    }
 }
 
 #[derive(Copy, Clone, PartialEq)]
@@ -227,4 +483,5 @@ pub enum TaskStatus {
     Ready,
     Running,
     Zombie,
+    // Exited,
 }
\ No newline at end of file
diff --git a/os/src/timer.rs b/os/src/timer.rs
index 92d50e3ade2b54f5b935d44f7948bb72c12b7ff8..8fcf5b8461ff70e495dba1860c076706cce48586 100644
--- a/os/src/timer.rs
+++ b/os/src/timer.rs
@@ -5,14 +5,25 @@ use crate::config::CLOCK_FREQ;
 const TICKS_PER_SEC: usize = 100;
 const MSEC_PER_SEC: usize = 1000;
 
+#[repr(C)]
+#[derive(Debug,Clone,Copy)]
+pub struct TimeVal {
+    pub sec: usize,
+    pub usec: usize,
+}
+
+//时钟周期数
 pub fn get_time() -> usize {
     time::read()
 }
 
+//毫秒数
 pub fn get_time_ms() -> usize {
+    trace!("in get_time_ms,{},{},{},{}",get_time(),CLOCK_FREQ,MSEC_PER_SEC,time::read() / (CLOCK_FREQ / MSEC_PER_SEC));
     time::read() / (CLOCK_FREQ / MSEC_PER_SEC)
 }
 
 pub fn set_next_trigger() {
+    trace!("timer::set_next_trigger....new timer is {}",get_time() + CLOCK_FREQ / TICKS_PER_SEC);
     set_timer(get_time() + CLOCK_FREQ / TICKS_PER_SEC);
-}
\ No newline at end of file
+}
diff --git a/os/src/trap/mod.rs b/os/src/trap/mod.rs
index f83560b2948c72335c9e87e4beabfb27f71d4a51..c0a4e91469729a30af963832bc3634b83e88b102 100644
--- a/os/src/trap/mod.rs
+++ b/os/src/trap/mod.rs
@@ -1,4 +1,5 @@
 mod context;
+// use crate::sbi::shutdown;
 
 use riscv::register::{
     mtvec::TrapMode,
@@ -12,15 +13,21 @@ use riscv::register::{
     stval,
     sie,
 };
-use crate::syscall::syscall;
+use crate::syscall::{
+    syscall5,
+};
 use crate::task::{
     exit_current_and_run_next,
     suspend_current_and_run_next,
     current_user_token,
     current_trap_cx,
+    // TASK_MANAGER,
 };
 use crate::timer::set_next_trigger;
 use crate::config::{TRAP_CONTEXT, TRAMPOLINE};
+// use crate::timer::set_next_trigger;
+// use crate::timer::{get_time,get_time_ms};
+// use crate::config::MAX_RUN_TIME_MS;
 
 global_asm!(include_str!("trap.S"));
 
@@ -46,6 +53,7 @@ pub fn enable_timer_interrupt() {
 
 #[no_mangle]
 pub fn trap_handler() -> ! {
+    // debug!("in trap_handler......");
     set_kernel_trap_entry();
     let scause = scause::read();
     let stval = stval::read();
@@ -55,18 +63,27 @@ pub fn trap_handler() -> ! {
             let mut cx = current_trap_cx();
             cx.sepc += 4;
             // get system call return value
-            let result = syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]);
+            // let result = syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]);
+            let result = syscall5(cx.x[17], [cx.x[10], cx.x[11], cx.x[12], cx.x[13], cx.x[14]]);
             // cx is changed during sys_exec, so we have to call it again
             cx = current_trap_cx();
             cx.x[10] = result as usize;
         }
+        //操作系统怎么知道数据读好?
+        //OS请求device读数据,然后device读好之后发起中断
+        //但是在我们这次实验里面,是直接读的。使用中断&&DMA工作量还是很大的
+        //硬件在进入的时候进行中断屏蔽。
+        //内核在执行过程中是否允许嵌套中断,取决于内核的实现。
         Trap::Exception(Exception::StoreFault) |
         Trap::Exception(Exception::StorePageFault) |
         Trap::Exception(Exception::InstructionFault) |
         Trap::Exception(Exception::InstructionPageFault) |
         Trap::Exception(Exception::LoadFault) |
         Trap::Exception(Exception::LoadPageFault) => {
-            println!(
+        //问题:是否可以只换出进程的一部分?
+        //如果没有页机制,那么整个进程换出去很好。但是在有了页机制之后,就并不是很有必要把整个进程都换出去。
+        //各种东西都有自己适用的场景
+            kernel_println!(
                 "[kernel] {:?} in application, bad addr = {:#x}, bad instruction = {:#x}, core dumped.",
                 scause.cause(),
                 stval,
@@ -76,24 +93,30 @@ pub fn trap_handler() -> ! {
             exit_current_and_run_next(-2);
         }
         Trap::Exception(Exception::IllegalInstruction) => {
-            println!("[kernel] IllegalInstruction in application, core dumped.");
+            kernel_println!("[kernel] IllegalInstruction in application, core dumped.");
             // illegal instruction exit code
             exit_current_and_run_next(-3);
         }
-        Trap::Interrupt(Interrupt::SupervisorTimer) => {
-            set_next_trigger();
-            suspend_current_and_run_next();
+        Trap::Interrupt(Interrupt::SupervisorTimer) => {//发现时钟中断:
+            // println!("[kernel] trap_handler::Exception::SupervisorTimer");
+            set_next_trigger();//先重新设置一个 10ms 的计时器
+            suspend_current_and_run_next();//调用 suspend_current_and_run_next 函数暂停当前应用并切换到下一个
         }
         _ => {
+            // exit_current_and_run_next(-10);
+            // kernel_println!("[kernel] Upsupported trap of app {},core dumped.", get_task_current());
+            // exit_current_and_run_next();
             panic!("Unsupported trap {:?}, stval = {:#x}!", scause.cause(), stval);
         }
     }
     //println!("before trap_return");
+    // drop(tm);
     trap_return();
 }
 
 #[no_mangle]
 pub fn trap_return() -> ! {
+    //根据汇编的结果,确实是进入了trap return函数没错
     set_user_trap_entry();
     let trap_cx_ptr = TRAP_CONTEXT;
     let user_satp = current_user_token();
diff --git a/reports/lab1.md b/reports/lab1.md
new file mode 100644
index 0000000000000000000000000000000000000000..b49a2a2c6c135184744331f3b62edaaf5822b02d
--- /dev/null
+++ b/reports/lab1.md
@@ -0,0 +1,73 @@
+# RustOS-lab1
+
+陈张萌 2017013678 计74
+
+[TOC]
+
+## 一、本次实验增加了什么
+
+1. 模仿println!实现了error!、warn!等5个宏
+2. 支持命令行参数LOG=xxx,可以在运行时指定输出等级
+
+## 二、实验截图
+
+` make run LOG=info`
+
+![](./lab1/info.png)
+
+`make run LOG=trace`
+
+![](./lab1/trace.png)
+
+## 三、回答问题
+
+### 为了方便 os 处理,M态软件会将 S 态异常/中断委托给 S 态软件,请指出有哪些寄存器记录了委托信息,rustsbi 委托了哪些异常/中断?(也可以直接给出寄存器的值)
+
+把中断全部委托给了S层。mideleg和medeleg分别记录了中断和异常委托的信息。设置完中断异常委托后,两个寄存器的信息如下:
+
+| register | value  |
+| -------- | ------ |
+| medeleg  | 0xb1ab |
+| mideleg  | 0x222  |
+
+### 请学习 gdb 调试工具的使用(这对后续调试很重要),并通过 gdb 简单跟踪从机器加电到跳转到 0x80200000 的简单过程。只需要描述重要的跳转即可,只需要描述在 qemu 上的情况。
+
+#### 上电检查
+
+上电之后从0x0000开始的位置都是unimp,第一条指令位于0x1000。
+
+0x1000执行几条指令后跳转到0x80000000,也就是进入了rustsbi,代码如下。
+
+```assembly
+0x1000:      auipc   t0,0x0
+                                               │   0x1004:      addi    a1,t0,32
+                                               │   0x1008:      csrr    a0,mhartid
+                                               │   0x100c:      ld      t0,24(t0)
+                                               │   0x1010:      jr      t0
+```
+
+#### 进入rustsbi
+
+进入rustsbi后,
+
+- 先会跳转到start函数,执行和hart_id有关的初始化操作,然后会进入main函数,继续执行关于hart的初始化
+- main函数所做的主要工作是为从M态进入S态做准备,类似从S态trap进了M态之后要返回S态。
+- 调用_start_trap等函数对中断异常处理进行初始化,设置中断处理函数的入口地址,设置中断模式为直接中断模式。
+- 接下来对rustsbi进行初始化,再将M态中断和异常都委托给S层。
+- 输出rustsbi的欢迎信息。
+- 最后,修改mepc到OS的起始地址 `0x80200000`,修改MPP,再通过系统调用就进入了S态,跳转到OS的起始地址开始运行操作系统。
+
+#### 运行OS
+
+使用下面的指令可以对os.bin进行反汇编,发现os的地址确实是从0x80200000开始的。
+
+`rust-objdump -S -d target/riscv64gc-unknown-none-elf/release/os > target/riscv64gc-unknown-none-elf/release/os.s  -x --arch-name=riscv64 `
+
+## 四、你对本次实验设计及难度/工作量的看法,以及有哪些需要改进的地方
+
+作为熟悉代码框架和熟悉Rust语言的第一个实验我觉得难度比较合适。
+
+代码内容建议再改进一下,因为这次实验的内容即使完全不看指导书的任何内容,就对着println!宏魔改也能改出来,而且代码量仿佛不存在一样的小。
+
+真正的工作量在理解代码框架和rustsbi,简而言之就是希望可以有和代码框架关系更大一些的实验内容。
+
diff --git a/reports/lab1/info.png b/reports/lab1/info.png
new file mode 100644
index 0000000000000000000000000000000000000000..2f37ec914cc838822fb6dd1abee4ebaac55b48d9
Binary files /dev/null and b/reports/lab1/info.png differ
diff --git a/reports/lab1/trace.png b/reports/lab1/trace.png
new file mode 100644
index 0000000000000000000000000000000000000000..dedcb48ab661ece334a6640286000e54545f9666
Binary files /dev/null and b/reports/lab1/trace.png differ
diff --git a/reports/lab2.md b/reports/lab2.md
new file mode 100644
index 0000000000000000000000000000000000000000..036bad021196cbfdebef7730d2c84e955bbe266e
--- /dev/null
+++ b/reports/lab2.md
@@ -0,0 +1,141 @@
+# RustOS-lab2
+
+陈张萌 2017013678 计74
+
+[TOC]
+
+## 本次实验增加了什么
+
+1. 在batch.rs文件中增加public函数,允许其他模块获得当前运行进程的地址空间(left,right)以及用户栈地址空间(left2,right2)
+2. 在系统调用syswrite处进行检查,只有在以上两个空间范围内的内容允许输出。
+
+## 回答问题
+
+### 问题1
+
+<<<<<<< HEAD
+正确进入 U 态后,程序的特征还应有:使用 S 态特权指令,访问 S 态寄存器后会报错。目前由于一些其他原因,这些问题不太好测试,请同学们可以自行测试这些内容(参考 [前三个测例](https://github.com/DeathWish5/rCore_tutorial_tests/tree/master/user/src/bin) ),描述程序出错行为,同时注意注明你使用的 sbi 及其版本。
+
+修改测试程序,在U态程序中加入访问S态寄存器的代码,运行得到以下报错信息。因为U态没有访问S态寄存器的权限,因此访问S态寄存器会引入StoreFault,就会跳转到异常处理中StoreFault的位置,结束这个task,运行下一个task。
+
+```toml
+[INFO][kernel] Loading app_1
+Into Test store_fault, we will insert an invalid store operation...
+Kernel should kill this application!
+[kernel] PageFault in application, core dumped.
+```
+=======
+> 正确进入 U 态后,程序的特征还应有:使用 S 态特权指令,访问 S 态寄存器后会报错。目前由于一些其他原因,这些问题不太好测试,请同学们可以自行测试这些内容(参考 [前三个测例](https://github.com/DeathWish5/rCore_tutorial_tests/tree/master/user/src/bin) ),描述程序出错行为,同时注意注明你使用的 sbi 及其版本。
+
+修改测试程序,在U态程序中加入访问S态寄存器的代码,运行得到以下报错信息。因为U态没有访问S态寄存器的权限,因此访问S态寄存器会报ivalid instruction,从而产生rustssbi-panic,进而退出。
+
+![](./lab2/panic.png)
+
+我使用的sbi是0.1.1版。
+>>>>>>> ch2
+
+### 问题2
+
+请结合用例理解 [trap.S](https://github.com/rcore-os/rCore-Tutorial-v3/blob/ch2/os/src/trap/trap.S) 中两个函数 `__alltraps` 和 `__restore` 的作用,并回答如下几个问题:
+
+#### 问题2.1
+
+L40: 刚进入 `__restore` 时,`a0` 代表了什么值。请指出 `__restore` 的两种使用情景。
+
+<<<<<<< HEAD
+刚进入 `__restore` 时,`a0` 代表的是内核栈的栈顶地址。
+=======
+刚进入 `__restore` 时,`a0` 代表的是内核栈的栈顶地址。使用__restore的两个场景如下:
+>>>>>>> ch2
+
+- 场景1: 准备进入U态,运行用户程序
+- 场景2: 中断/异常处理结束,要返回U态
+
+#### 问题2.2
+
+L46-L51: 这几行汇编代码特殊处理了哪些寄存器?这些寄存器的的值对于进入用户态有何意义?请分别解释。
+
+把存储在栈上的sstatus、sepc和sscratch寄存器从栈上恢复出来。
+
+sstatus表示了进入trap之前的特权级;sepc存储的是用户态进入trap之前的虚拟地址(目前还没有实现,所以大概是实际地址),sscratch寄存器存储的是用户栈栈顶。(接下来要让sp指向用户栈栈顶,sscratch指向内核栈栈顶)。
+
+```
+ld t0, 32*8(sp)
+ld t1, 33*8(sp)
+ld t2, 2*8(sp)
+csrw sstatus, t0
+csrw sepc, t1
+csrw sscratch, t2
+```
+
+#### 问题2.3
+
+L53-L59: 为何跳过了 `x2` 和 `x4`?
+
+因为`x2`就是`sp`,`tp(x4)` 除非我们手动出于一些特殊用途使用它,否则一般也不会被用到,在进入中断的时候这些寄存器也没有存在内核栈里。
+
+```
+ld x1, 1*8(sp)
+ld x3, 3*8(sp)
+.set n, 5
+.rept 27
+   LOAD_GP %n
+   .set n, n+1
+.endr
+```
+
+#### 问题2.4
+
+L63: 该指令之后,`sp` 和 `sscratch` 中的值分别有什么意义?
+
+在这条指令之后,sp指向的是用户栈栈顶,sscratch指向内核栈栈顶。
+
+```
+csrrw sp, sscratch, sp
+```
+
+#### 问题2.5
+
+` __restore`:中发生状态切换在哪一条指令?为何该指令执行之后会进入用户态?
+
+状态发生切换在sret。CPU执行该指令会做:CPU从S-Mode变为U-Mode,pc指向sepc(也就是陷入内核前的指令地址),sp指向用户栈栈顶。
+
+#### 问题2.6
+
+从 U 态进入 S 态是哪一条指令发生的?
+
+U态进入S态是在sbi.rs中调用sbi_call(xxx)时内联汇编代码的ecall指令会使程序由U态进入S态。
+
+### 问题3
+
+> 描述程序陷入内核的两大原因是中断和异常,请问 riscv64 支持哪些中断/异常?如何判断进入内核是由于中断还是异常?描述陷入内核时的几个重要寄存器及其值。
+
+riscv64将中断/异常类型保存在scause寄存器中,下表中展示了中断和异常的编号。判断中断/异常只需要看最高位是1(中断)还是0(异常)。
+
+![](./lab2/scause.png)
+
+陷入内核时的重要寄存器:
+
+| 寄存器   | 功能                                                         |
+| -------- | ------------------------------------------------------------ |
+| ssstatus | 保存中断/异常屏蔽位、返回后的特权级、中断模式(direct or vector)等信息 |
+| scause   | 保存中断/异常编号                                            |
+| sepc     | 记录发生中断前的指令的虚拟地址                               |
+| sscratch | 陷入内核前保存的是内核栈地址,陷入内核后保存用户栈地址       |
+
+(以上内容参考:The RISC-V Instruction Set Manual)
+
+<<<<<<< HEAD
+=======
+### 问题4
+
+>对于任何中断, __alltraps 中都需要保存所有寄存器吗?你有没有想到一些加速 __alltraps 的方法?简单描述你的想法。
+
+一般还是需要保存寄存器的,因为中断&&异常有可能发生在任何地方,所以需要确保上下文的一致性。
+
+加速的方法有是有,但是感觉总体上还是得不偿失。比如说,可以修改CPU架构,为ecall设计特殊的上下文切换指令和寄存器,或者约定调用ecall只保存某些特定寄存器。但是问题在于多保存几个寄存器并不会有多么明显的性能损失,但是这些优化却会破坏安全性,而且这样的CPU和OS看起来一点都不优美。
+
+>>>>>>> ch2
+## 你对本次实验设计及难度/工作量的看法,以及有哪些需要改进的地方
+
+我觉得难度比较合适。
diff --git a/reports/lab2/panic.png b/reports/lab2/panic.png
new file mode 100644
index 0000000000000000000000000000000000000000..dd779139a41155f041beedc76fa15abe8883cb48
Binary files /dev/null and b/reports/lab2/panic.png differ
diff --git a/reports/lab2/q1.png b/reports/lab2/q1.png
new file mode 100644
index 0000000000000000000000000000000000000000..1c73a2529e063e19b115a0c35084bc761d1bca06
Binary files /dev/null and b/reports/lab2/q1.png differ
diff --git a/reports/lab2/result.png b/reports/lab2/result.png
new file mode 100644
index 0000000000000000000000000000000000000000..3574951af174636b93e05a55c91595b213b1bd97
Binary files /dev/null and b/reports/lab2/result.png differ
diff --git a/reports/lab2/scause.png b/reports/lab2/scause.png
new file mode 100644
index 0000000000000000000000000000000000000000..921924a0500aa4cd196855ea3ba4d3e8f8fed9dd
Binary files /dev/null and b/reports/lab2/scause.png differ
diff --git a/reports/lab3.md b/reports/lab3.md
new file mode 100644
index 0000000000000000000000000000000000000000..0fe595e49c61cb6ec1dce4e78ce435064dde113e
--- /dev/null
+++ b/reports/lab3.md
@@ -0,0 +1,125 @@
+# RustOS-lab3 实验报告
+
+陈张萌 2017013678 计74
+
+[TOC]
+
+## 本次实验增加了什么?
+
+1. 将实验2的对sys_write的检查迁移过来。为什么需要重新写呢,因为允许多程序并行之后,用户栈地址和用户程序地址都发生了变化,所以需要修改地址判断。
+2. 增加了系统调用:sys_get_time和sys_set_priority
+3. 增加了stride调度算法,验证发现stride和优先级成正比
+4. 为了使用BinaryHeap,增加了chapter4中的动态内存分配,但事实是最后还是选择了用数组形式存储每个进程的stride相关信息。
+5. 做出以上处理的原因在于:要选择执行的进程要满足处于Ready状态,但是stride最小的进程并不一定是Ready的。对此有两种解决方式:一是只把Ready状态的进程放进堆里,但是此种解决方式需要在进程状态改变时更新堆的信息,取出进程的最坏情况时间复杂度为O(NlogN),而且代码非常不简洁。另一种方法是把所有进程都放进堆里,每次取出时检查是否满足Ready状态,最坏情况下取出进程时间复杂度也要O(NlogN);两种解决方式均不如直接选择使用数组存储进程。
+
+![](./lab3/stride.png)
+
+## 简答作业
+
+### 问题1
+
+> 简要描述这一章的进程调度策略。何时进行进程切换?如何选择下一个运行的进程?如何处理新加入的进程?
+
+在每次时钟中断产生的时候进行进程切换。
+
+使用stride算法选择下一个运行的进程。
+
+暂时无法处理新加入的进程,因为本章采用的方式是在操作系统启动的时候,检查有多少个待运行程序并一次性把所有进程都加载进来,进程管理模块也是在初始化的时候就确定了大小。
+
+### 问题2
+
+> 在 C 版代码中,同样实现了类似 RR 的调度算法,但是由于没有 VecDeque 这样直接可用的数据结构(Rust很棒对不对),C 版代码的实现严格来讲存在一定问题。大致情况如下:C版代码使用一个进程池(也就是一个 struct proc 的数组)管理进程调度,当一个时间片用尽后,选择下一个进程逻辑在 [chapter3相关代码](https://github.com/DeathWish5/ucore-Tutorial/blob/ch3/kernel/proc.c#L60-L74) ,也就是当第 i 号进程结束后,会以 i -> max_num -> 0 -> i 的顺序遍历进程池,直到找到下一个就绪进程。C 版代码新进程在调度池中的位置选择见 [chapter5相关代码](https://github.com/DeathWish5/ucore-Tutorial/blob/ch5/kernel/proc.c#L90-L98) ,也就是从头到尾遍历进程池,找到第一个空位。
+
+#### (2-1) 在目前这一章(chapter3)两种调度策略有实质不同吗?
+
+> 考虑在一个完整的 os 中,随时可能有新进程产生,这两种策略是否实质相同?
+
+目前没有实质不同。由于目前的所有进程都会一次性加载完成,max_num是不变的,因此只有效率上存在一定差别,但是每个进程都有机会轮转到,也满足先产生的进程先执行的特点。
+
+但是在完整的os中,随时都会用新的进程产生,下面的问题就是先产生的进程后执行的反例。
+
+#### (2-2) 其实 C 版调度策略在公平性上存在比较大的问题
+
+> 请找到一个进程产生和结束的时间序列,使得在该调度算法下发生:先创建的进程后执行的现象。你需要给出类似下面例子的信息(有更详细的分析描述更好,但尽量精简)。同时指出该序列在你实现的 stride 调度算法下顺序是怎样的?
+
+例如进程池大小为4。
+
+###### 调度顺序举例
+
+| 时间点   | 0               | 1    | 2    | 3    | 4      | 5              | 6      | 7    | 8    |
+| -------- | --------------- | ---- | ---- | ---- | ------ | -------------- | ------ | ---- | ---- |
+| 运行进程 |                 | p1   | p2   | p3   | p4     | p1             | p2     | p6   | p5   |
+| 事件     | p1,p2,p3,p4产生 |      |      |      | p4结束 | p5产生,p3结束 | p6产生 |      |      |
+|          |                 |      |      |      |        |                |        |      |      |
+
+进程产生顺序:1,2,3,4,5,6;
+
+进程执行顺序:1,2,3,4,1,2,6,5;
+
+因为不考虑进程优先级,允许进程在其他进程执行时结束。因此可以进程池里下标编号较大位置的进程先结束,下标编号较小的进程后结束,那么新产生的进程就会放在靠前的位置,就会先执行。
+
+而在stride算法中,刚产生的进程stride=0,因此一定是在产生进程后的下一次切换进程执行状态时就会马上执行(除非一次产生很多个进程)。
+
+### 问题3
+
+#### 1.实际情况是轮到P1执行吗?
+
+并不是,当使用8个bit的无符号整数存储时,能表示的范围是0~255。在P2执行一个时间片后,会发生溢出,导致P2的stride在执行后本来应该比P1大,却由于溢出而变得比P1小了。
+
+#### 2.为何能做到 STRIDE_MAX – STRIDE_MIN <= BigStride / 2?
+
+数学归纳法:
+
+假设在第n步满足 `STRIDE_MAX(n) – STRIDE_MIN(n) <= BigStride / 2`,那么在第n+1步,会增加的一定是 `STRIDE_MIN(n)` ,增加的步长为 `PASS(n)`, `STRIDE_MIN(n+1)= min{STRIDE_MIN(n)+PASS(n),STRIDE_MIN2(n)(即:第n步时的倒数第2小的stride)}` 
+
+如果 `STRIDE(n)+PASS(n)` 是第n+1步最小的stride,
+那么 `STRIDE_MAX(n+1)=STRIDE_MAX(n)`  ,
+因此 ` STRIDE_MAX(n+1) – STRIDE_MIN(n+1) <=STRIDE_MAX(n) – STRIDE_MIN(n) <= BigStride / 2 ` 仍然成立
+
+如果 `STRIDE_MIN2(n)` 是第n+1步最小的stride,那么 `STRIDE_MAX(n+1)=max{STRIDE_MAX(n),STRIDE_MIN(n)+PASS(n)}` 。
+而 `STRIDE_MAX(n)-STRIDE_MIN2(n)<=STRIDE_MAX(n) – STRIDE_MIN(n) <= BigStride / 2 ` ,
+且 `STRIDE_MIN(n)+PASS(n)-STRIDE_MIN2(n)<=PASS(n)<= BigStride / 2 ` ,
+因此 `STRIDE_MAX(n+1)-STRIDE_MIN(n+1) <= BigStride/2`  仍然成立。
+
+综上当第n步满足时,第n+1步仍然满足。
+
+在初始条件下,所有进程的Stride都为0,自然满足条件。由数学归纳可知,每一步都可以满足 `STRIDE_MAX – STRIDE_MIN <= BigStride / 2` 。
+
+#### 3. 请补全如下 `partial_cmp` 函数(假设永远不会相等)。
+
+```rust
+use core::cmp::Ordering;
+use crate::config::BIG_STRIDE;
+
+struct Stride(u64);
+
+impl PartialOrd for Stride {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+      if self.0 < other.0{
+            if other.0 - self.0 < BigStride as u64 {
+                return Some(Ordering::Less);
+            }else{
+                return Some(Ordering::Greater);
+            }
+        }else {
+            if self.0 - other.0 < BigStride as u64 {
+                return Some(Ordering::Less);
+            }else{
+                return Some(Ordering::Greater);
+            }
+        }
+    }
+}
+
+impl PartialEq for Person {
+    fn eq(&self, other: &Self) -> bool {
+        false
+    }
+}
+```
+
+## 你对本次实验设计及难度/工作量的看法,以及有哪些需要改进的地方
+
+我做的时候觉得思路比较清楚,工作量也很合适,总体来说难度属于中等偏下吧~前两次是不是有点太简单了,只能当成熟悉Rust语法&&代码结构了······
+
+需要改进的地方······可能是由于和ch2相比架构的改变,导致需要24K纯手工merge代码(约等于重写),确实是有点惊喜Orz。
\ No newline at end of file
diff --git a/reports/lab3/stride.png b/reports/lab3/stride.png
new file mode 100644
index 0000000000000000000000000000000000000000..63f834f4ef80e8a38b9d9e75af5036498b96c7b1
Binary files /dev/null and b/reports/lab3/stride.png differ
diff --git a/reports/lab4.md b/reports/lab4.md
new file mode 100644
index 0000000000000000000000000000000000000000..656ba01a402c10397e31c096a4505b01ae5430d8
--- /dev/null
+++ b/reports/lab4.md
@@ -0,0 +1,162 @@
+# RustOS-lab4 实验报告
+
+陈张萌 2017013678 计74
+
+[TOC]
+
+## 本次实验增加了什么?
+
+1. 增加了物理内存动态分配。在OS启动时对物理内存分配器进行初始化,使得OS中的物理内存分配机制使用我们提供的stack分配策略。
+2. 增加了分页机制,为每个进程提供虚拟地址空间和页表,为每个物理地址建立到自己的一一映射以便OS在开启分页后仍然能方便地访问物理地址。
+3. 增加了mmap和munmap系统调用。事实上只要调用代码框架中已经写好的函数就可以了,需要自己完成的函数是对map/unmap的失败情况进行检查。
+4. 修改了get_time系统调用。需要修复的原因是,这个系统调用是在用户态传来了TimeVal类的地址,在OS内实现系统调用的时候要使用它的裸指针访问这个数据。但是用户程序传来的地址是在该任务使用的页表下的虚拟地址,但是执行系统调用会使得OS进入内核态, 同时也切换到内核页表,因此在内核态下访问同样的虚拟地址,自然会发生错误。要进行的修复工作是,将该虚拟地址对应的物理地址取出来,直接通过物理地址对应的裸指针修改TimeVal的值。因为虽然进入内核态之后也仍然使用页表映射,但是OS在页表机制启动的时候,就为物理页面建立了一一自映射,因此输入物理地址在页表映射下就会访问对应的物理地址。
+
+实验结果如下:
+
+![](./lab4/result2.png)
+
+运行测例,输出结果如下:
+
+![](./lab4/result.png)
+
+## 简答作业
+
+### 问题1、页表项
+
+> 请列举 SV39 页表页表项的组成,结合课堂内容,描述其中的标志位有何作用/潜在作用?
+
+页表项的高44位表示ppn,即物理页号;页表项的低8位是标志位:V(表示次页表有效),R(可读),W(可写),X(可执行),U(用户态可访问),GAD本次实验没有用到。
+
+利用标志位,可以和CPU协同工作,提供访问权限控制、地址保护等功能。例如当一个页在U态可访问时可以将标志位U置为1,这样CPU在执行时就知道如果一个用户程序的虚拟地址对应的页表标志位为U,因此就可以允许其进行地址转换,进而允许访问。
+
+还可以帮助OS进行修改页表的工作。例如在进行map时,可以用虚拟地址找到对应的页表项并检查valid位,据此判断这是新分配的页表项还是已经map过的页表项。
+
+### 问题2、缺页
+
+> 缺页指的是进程访问页面时页面不在页表中或在页表中无效的现象,此时 MMU 将会返回一个中断,告知 os 进程内存访问出了问题。os 选择填补页表并重新执行异常指令或者杀死进程。
+
+#### (2-1-1) 请问哪些异常可能是缺页导致的?
+
+![](./lab4/scause.png)
+
+RISC-V中的异常种类如上表所示。其中,有可能由于缺页产生的异常类型是:
+
+- 12,13,15三种类型的pagefault
+
+#### (2-1-2)发生缺页时,描述相关的重要寄存器的值(lab2中描述过的可以简单点)
+
+陷入内核时的重要寄存器:
+
+| 寄存器   | 功能                                                         |
+| -------- | ------------------------------------------------------------ |
+| ssstatus | 保存中断/异常屏蔽位、返回后的特权级、中断模式(direct or vector)等信息 |
+| scause   | 保存中断/异常编号,产生page fault时,scause为12、13或15      |
+| sepc     | 记录发生中断前的指令的虚拟地址                               |
+| sscratch | 陷入内核前保存的是内核栈地址,陷入内核后保存用户栈地址       |
+
+(以上内容参考:The RISC-V Instruction Set Manual)
+
+
+
+> 缺页有两个常见的原因,其一是 Lazy 策略,也就是直到内存页面被访问才实际进行页表操作。比如,一个程序被执行时,进程的代码段理论上需要从磁盘加载到内存。但是 os 并不会马上这样做,而是会保存 .text 段在磁盘的位置信息,在这些代码第一次被执行时才完成从磁盘的加载操作。
+
+#### (2-2)这样做有哪些好处?
+
+这样做可以节省开销。例如一个极端情况是,一个程序写了长达几个G的代码,但是在main函数却里面直接return 0,如果把整个函数都一次性加载进内存中,开销太大了。
+
+> 此外 COW(Copy On Write) 也是常见的容易导致缺页的 Lazy 策略,这个之后再说。其实,我们的 mmap 也可以采取 Lazy 策略,比如:一个用户进程先后申请了 10G 的内存空间,然后用了其中 1M 就直接退出了。按照现在的做法,我们显然亏大了,进行了很多没有意义的页表操作。
+
+#### (2-3-1)请问处理 10G 连续的内存页面,需要操作的页表实际大致占用多少内存(给出数量级即可)?
+
+按照实验所用的页表进行估计,每一页有4096Byte,也即每一页的大小是4KByte。$10GByte=10 * 2^{10}MByte=10 * 2^{20}KByte=2.5 * 2^{20}$条页表。每条页表占据8Byte,总共占用空间为$8*2.5*2^{20}=20MByte$。
+
+#### (2-3-2)请简单思考如何才能在现有框架基础上实现 Lazy 策略,缺页时又如何处理?描述合理即可,不需要考虑实现。
+
+现有框架将find_pte_create函数放在MapArea的map函数里面进行调用,缺页时直接退出正在运行的用户进程,或者panic退出。
+
+如果使用lazy策略,可以将find_pte_create函数放在缺页处理函数里面,当缺页时先寻找/创建pte,如果是可用的pte则可以继续执行;否则说明的确产生了非法访问,会退出正在运行的用户进程。
+
+> 缺页的另一个常见原因是 swap 策略,也就是内存页面可能被换到磁盘上了,导致对应页面失效。
+
+#### (2-4)此时页面失效如何表现在页表项(PTE)上?
+
+在swap使得页面失效之后,PTE会将Valid位置1。
+
+### 问题3、双页表与单页表
+
+> 为了防范侧信道攻击,我们的 os 使用了双页表。但是传统的设计一直是单页表的,也就是说,用户线程和对应的内核线程共用同一张页表,只不过内核对应的地址只允许在内核态访问。
+
+#### 1.如何更换页表?
+
+对CPU来说,进行虚拟地址到实际地址之间的转换都是从satp寄存器取出第一级页表的地址,再据此访问二、三级页表。因此更换页表只需要把新页表的地址写入satp寄存器即可。
+
+#### 2.单页表情况下,如何控制用户态无法访问内核页面?
+
+页表项中有U位表示是否允许用户态访问,只需要将U位置0。
+
+#### 3. 单页表有何优势?(回答合理即可)
+
+单页表最大的优势就是节省更换页表的开销。在更换页表时,需要使用sfence指令提醒CPU,之前在CPU内缓存的页表项都失效了。读取新的页表就要再次放存,以sv39为例,一个没有缓存过的页表项需要访存4次,开销非常大,而频繁更换页表就会极大地增加放存次数,影响性能。
+
+#### 4. 双页表实现下,何时需要更换页表?假设你写一个单页表操作系统,你会选择何时更换页表(回答合理即可)?
+
+单页表和双页表都需要在切换进程的时候更换页表,因为OS为每一个app都提供一个虚拟地址空间,这就对应一张页表。单页表操作系统在切换进程时更换页表即可,并提醒CPU之前缓存的TLB可能失效。
+
+对于双页表系统,在执行用户程序时要使用用户态的页表,执行内核程序时要使用内核的页表。因此,对于本次实验来说,切换进程的时候会trap进入内核态,把页表更换成kernel的页表,trap处理完毕返回时要更换成新的进程的用户态页表。也就是说切换一次进程就需要更换2次页表。两次切换分别在代码中对应:
+
+```rust
+//为每个app都保存了kernel使用的页表,并且在进入trap的时候更新页表
+impl TrapContext {
+    pub fn set_sp(&mut self, sp: usize) { self.x[2] = sp; }
+    pub fn app_init_context(
+        entry: usize,
+        sp: usize,
+        kernel_satp: usize,
+        kernel_sp: usize,
+        trap_handler: usize,
+    ) -> Self {
+        let mut sstatus = sstatus::read();
+        sstatus.set_spp(SPP::User);
+        let mut cx = Self {
+            x: [0; 32],
+            sstatus,
+            sepc: entry,
+            kernel_satp,
+          //就保存在这里,为每一个进程都保存了一份
+            kernel_sp,
+            trap_handler,
+        };
+        cx.set_sp(sp);
+        cx
+    }
+}
+```
+
+```rust
+//trap执行完毕返回用户态时
+#[no_mangle]
+pub fn trap_return() -> ! {
+    set_user_trap_entry();
+    let trap_cx_ptr = TRAP_CONTEXT;
+    let user_satp = current_user_token();
+    extern "C" {
+        fn __alltraps();
+        fn __restore();
+    }
+    let restore_va = __restore as usize - __alltraps as usize + TRAMPOLINE;
+    unsafe {
+        llvm_asm!("fence.i" :::: "volatile");
+        llvm_asm!("jr $0" :: "r"(restore_va), "{a0}"(trap_cx_ptr), "{a1}"(user_satp) :: "volatile");
+    }
+  //trap执行完毕,返回用户态,更换页表,并且刷新TLB
+    panic!("Unreachable in back_to_user!");
+}
+```
+
+## 你对本次实验设计及难度/工作量的看法,以及有哪些需要改进的地方
+
+我觉得这部分还有一些可以改进的地方。
+
+第一是可以参考去年实验,物理内存动态分配、虚拟地址1、虚拟地址2一共分了3次实验进行。这部分相对来说是不太容易理解的知识点,所以多做几次实验总是好的。今年一次性搞定感觉确实需要多点时间消化~
+
+第二是,实验内容还有改进空间。就是如果自己写的话,代码量比较少,几乎唯一做的事情就是调用了各种各样的函数,做完感觉像什么都没干一样。我觉得可以改成增加一些更加核心的功能的代码似乎会更好一些。
\ No newline at end of file
diff --git a/reports/lab4/result.png b/reports/lab4/result.png
new file mode 100644
index 0000000000000000000000000000000000000000..b6815956f2d3bd6aef0cd938d1602a393df56f19
Binary files /dev/null and b/reports/lab4/result.png differ
diff --git a/reports/lab4/result2.png b/reports/lab4/result2.png
new file mode 100644
index 0000000000000000000000000000000000000000..2857d2df1a2e78017fff171670dc4f5d901a3f3e
Binary files /dev/null and b/reports/lab4/result2.png differ
diff --git a/reports/lab4/scause.png b/reports/lab4/scause.png
new file mode 100644
index 0000000000000000000000000000000000000000..921924a0500aa4cd196855ea3ba4d3e8f8fed9dd
Binary files /dev/null and b/reports/lab4/scause.png differ
diff --git a/reports/lab5.md b/reports/lab5.md
new file mode 100644
index 0000000000000000000000000000000000000000..416223e027dc09ed1b3082af852f94f9de026600
--- /dev/null
+++ b/reports/lab5.md
@@ -0,0 +1,74 @@
+# RustOS-lab5 实验报告
+
+陈张萌 2017013678 计74
+
+[TOC]
+
+## 本次实验增加了什么?
+
+1. spawn系统调用。和新建一个进程控制块几乎一样,唯一的区别在于新建时需要:
+   1. 使用类似exec系统调用的方式,从文件中将memory_set等加载进来
+   2. 新建的进程和当前进程之间建立父子关系
+   3. 新建进程加入到调度队列中
+
+运行测例,输出结果如下:
+
+![](./lab5/result0.png)
+
+![](./lab5/result1.png)
+
+## 简答作业
+
+### 问题1
+
+> fork + exec 的一个比较大的问题是 fork 之后的内存页/文件等资源完全没有使用就废弃了,针对这一点,有什么改进策略?
+
+改进策略就是增加了spawn系统调用,新建一个进程控制块时就直接使用文件对memory_set进行初始化。
+
+### 问题2
+
+> 其实使用了题(1)的策略之后,fork + exec 所带来的无效资源的问题已经基本被解决了,但是今年来 fork 还是在被不断的批判,那么到底是什么正在”杀死”fork?可以参考 [论文](https://www.microsoft.com/en-us/research/uploads/prod/2019/04/fork-hotos19.pdf) ,**注意**:回答无明显错误就给满分,出这题只是想引发大家的思考,完全不要求看论文,球球了,别卷了。
+
+- 非线程安全。例如某一个进程正在进行地址映射,因此拿着heap的锁,此时另一个进程在执行fork操作。此时如果在子进程中分配内存,就会出现死锁。
+- 效率不高,例如问题1中说到的问题。
+- 还会存在不安全的问题,
+
+### 问题3
+
+> fork 当年被设计并称道肯定是有其好处的。请使用 **带初始参数** 的 spawn 重写如下 fork 程序,然后描述 fork 有那些好处。注意:使用”伪代码”传达意思即可,spawn 接口可以自定义。可以写多个文件。
+
+如果直接使用已经实现的spawn替换原来代码中的fork,会遇到这些问题:
+
+- 需要在写代码的时候就知道自己的文件名是什么,不够灵活
+- spawn目前只支持从文件新建进程控制块,新建出来的进程控制块会从头开始执行。也就是说,新建进程这件事会进入一个死循环,不断地新建子进程。
+
+改写思路为:
+
+- 思路1:为spawn增加默认构造选项,如果输入字符串为空则使用和将父程序的进程控制块复制一份给子程序,且执行状态也保持一致,返回值为-2。这种修改方式实际上实现的功能和fork是一样的了。
+- 思路2:将if里面的函数内容封装到另一个文件中,使用spawn执行。
+
+fork的好处就是新建出来的进程连执行状态也是和父进程一样的。
+
+### 问题4
+
+>描述进程执行的几种状态,以及 fork/exec/wait/exit 对于状态的影响。
+
+```rust
+pub enum TaskStatus {
+    Ready,//表示进程准备就绪,受到调度就可以执行
+    Running,//表示这是正在执行的进程
+    Zombie,//进程已经执行完毕,等待回收资源
+}
+```
+
+fork:将当前进程复制一份,新的进程状态为Ready,不改变当前进程执行状态
+
+exec:不改变当前进程的执行状态,但是使用其他的文件来更新当前进程的memory_set等资源
+
+wait:将当前正在Running的进程变为Ready
+
+exit:将当前正在Running的进程改变为Zombie
+
+## 你对本次实验设计及难度/工作量的看法,以及有哪些需要改进的地方
+
+我觉得这次实验代码有些过于简单,只要看懂了指导书就实在是简单得令人发指。
diff --git a/reports/lab5/fork-hotos19.pdf b/reports/lab5/fork-hotos19.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..abc89538c1ffd24fb00efc5f0212d70736b7e915
Binary files /dev/null and b/reports/lab5/fork-hotos19.pdf differ
diff --git a/reports/lab5/result0.png b/reports/lab5/result0.png
new file mode 100644
index 0000000000000000000000000000000000000000..ff7055966e0a99b7e45970f413f1251adb6a8839
Binary files /dev/null and b/reports/lab5/result0.png differ
diff --git a/reports/lab5/result1.png b/reports/lab5/result1.png
new file mode 100644
index 0000000000000000000000000000000000000000..001388896507e2d6f0cf335eb29fbd9a7dd0ceb0
Binary files /dev/null and b/reports/lab5/result1.png differ
diff --git a/user/Cargo.lock b/user/Cargo.lock
new file mode 100644
index 0000000000000000000000000000000000000000..1e1ea74e270f0f8b01a8bdf562edfb7f95752463
--- /dev/null
+++ b/user/Cargo.lock
@@ -0,0 +1,129 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "aho-corasick"
+version = "0.7.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "bare-metal"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
+dependencies = [
+ "rustc_version",
+]
+
+[[package]]
+name = "bit_field"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
+
+[[package]]
+name = "bitflags"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+
+[[package]]
+name = "buddy_system_allocator"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4e85e760e105b46ae0bd1236578793c6c147ae7463fe95c8350296b8bfcb830"
+dependencies = [
+ "spin",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "memchr"
+version = "2.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
+
+[[package]]
+name = "regex"
+version = "1.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
+
+[[package]]
+name = "riscv"
+version = "0.6.0"
+source = "git+https://github.com/rcore-os/riscv#21e32ee1dc786cc0d5006ceee0040ce4f8398575"
+dependencies = [
+ "bare-metal",
+ "bit_field",
+ "bitflags",
+ "riscv-target",
+]
+
+[[package]]
+name = "riscv-target"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88aa938cda42a0cf62a20cfe8d139ff1af20c2e681212b5b34adb5a58333f222"
+dependencies = [
+ "lazy_static",
+ "regex",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "semver"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
+dependencies = [
+ "semver-parser",
+]
+
+[[package]]
+name = "semver-parser"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
+
+[[package]]
+name = "spin"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13287b4da9d1207a4f4929ac390916d64eacfe236a487e9a9f5b3be392be5162"
+
+[[package]]
+name = "user_lib"
+version = "0.1.0"
+dependencies = [
+ "bitflags",
+ "buddy_system_allocator",
+ "riscv",
+]
diff --git a/user/Cargo.toml b/user/Cargo.toml
index d50a0ba07b1dceca3e6d7be9852bb242e3589c2f..d76ba49e4a3e62b29463a44cb41776fb4fabb338 100644
--- a/user/Cargo.toml
+++ b/user/Cargo.toml
@@ -8,4 +8,5 @@ edition = "2018"
 
 [dependencies]
 buddy_system_allocator = "0.6"
-bitflags = "1.2.1"
\ No newline at end of file
+bitflags = "1.2.1"
+riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] }
\ No newline at end of file
diff --git a/user/src/bin/ch5_getpid.rs b/user/src/bin/ch5_getpid.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b71f90570ca777198027de6e420ab786e31d5bb5
--- /dev/null
+++ b/user/src/bin/ch5_getpid.rs
@@ -0,0 +1,14 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::getpid;
+
+#[no_mangle]
+pub fn main() -> i32 {
+    let pid = getpid();
+    println!("Test getpid OK! pid = {}", pid);
+    0
+}
diff --git a/user/src/bin/ch5_spawn0.rs b/user/src/bin/ch5_spawn0.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a5c108239d755893f4750fbbc26a4e24ff16a165
--- /dev/null
+++ b/user/src/bin/ch5_spawn0.rs
@@ -0,0 +1,25 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{spawn, wait};
+const MAX_CHILD: usize = 40;
+
+#[no_mangle]
+pub fn main() -> i32 {
+    for _ in 0..MAX_CHILD {
+        let cpid = spawn("ch5_getpid\0");
+        assert!(cpid >= 0, "child pid invalid");
+        println!("new child {}", cpid);
+    }
+    let mut exit_code: i32 = 0;
+    for _ in 0..MAX_CHILD {
+        assert!(wait(&mut exit_code) > 0, "wait stopped early");
+        assert_eq!(exit_code, 0, "error exit ocde {}", exit_code);
+    }
+    assert!(wait(&mut exit_code) <= 0, "wait got too many");
+    println!("Test spawn0 OK!");
+    0
+}
\ No newline at end of file
diff --git a/user/src/bin/ch5_spawn1.rs b/user/src/bin/ch5_spawn1.rs
new file mode 100644
index 0000000000000000000000000000000000000000..d4394cf997c17df928029aacac27bba2da7e9922
--- /dev/null
+++ b/user/src/bin/ch5_spawn1.rs
@@ -0,0 +1,28 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{spawn, wait, waitpid};
+
+#[no_mangle]
+pub fn main() -> i32 {
+    let cpid = spawn("ch5_exit0\0");
+    assert!(cpid >= 0, "child pid invalid");
+    println!("new child {}", cpid);
+    let mut exit_code: i32 = 0;
+    let exit_pid = wait(&mut exit_code);
+    assert_eq!(exit_pid, cpid, "error exit pid");
+    assert_eq!(exit_code, 66778, "error exit code");
+    println!("Test wait OK!");
+    let (cpid0, cpid1) = (spawn("ch5_exit0\0"), spawn("ch5_exit1\0"));
+    let exit_pid = waitpid(cpid1 as usize, &mut exit_code);
+    assert_eq!(exit_pid, cpid1, "error exit pid");
+    assert_eq!(exit_code, -233, "error exit code");
+    let exit_pid = wait(&mut exit_code);
+    assert_eq!(exit_pid, cpid0, "error exit pid");
+    assert_eq!(exit_code, 66778, "error exit code");
+    println!("Test waitpid OK!");
+    0
+}
diff --git a/user/src/bin/ch5_usershell.rs b/user/src/bin/ch5_usershell.rs
new file mode 100644
index 0000000000000000000000000000000000000000..f016103198d6d1db240d753d2a4aae7e32c32bdd
--- /dev/null
+++ b/user/src/bin/ch5_usershell.rs
@@ -0,0 +1,65 @@
+#![no_std]
+#![no_main]
+
+extern crate alloc;
+
+#[macro_use]
+extern crate user_lib;
+
+const LF: u8 = 0x0au8;
+const CR: u8 = 0x0du8;
+const DL: u8 = 0x7fu8;
+const BS: u8 = 0x08u8;
+
+use alloc::string::String;
+use user_lib::console::getchar;
+use user_lib::{spawn, waitpid, yield_};
+
+#[no_mangle]
+pub fn main() -> i32 {
+    println!("Rust user shell");
+    let mut line: String = String::new();
+    print!(">> ");
+    loop {
+        let c = getchar();
+        match c {
+            LF | CR => {
+                println!("");
+                if !line.is_empty() {
+                    line.push('\0');
+                    let cpid = spawn(line.as_str());
+                    if cpid < 0 {
+                        println!("invalid file name");
+                        continue;
+                    }
+                    let mut xstate: i32 = 0;
+                    let mut exit_pid: isize;
+                    loop {
+                        exit_pid = waitpid(cpid as usize, &mut xstate);
+                        if exit_pid == -1 {
+                            yield_();
+                        } else {
+                            assert_eq!(cpid, exit_pid);
+                            println!("Shell: Process {} exited with code {}", cpid, xstate);
+                            break;
+                        }
+                    }
+                    line.clear();
+                }
+                print!(">> ");
+            }
+            BS | DL => {
+                if !line.is_empty() {
+                    print!("{}", BS as char);
+                    print!(" ");
+                    print!("{}", BS as char);
+                    line.pop();
+                }
+            }
+            _ => {
+                print!("{}", c as char);
+                line.push(c as char);
+            }
+        }
+    }
+}
diff --git a/user/src/bin/filetest_simple.rs b/user/src/bin/ch7_file0.rs
similarity index 50%
rename from user/src/bin/filetest_simple.rs
rename to user/src/bin/ch7_file0.rs
index 60fda6aa38bc3b1534444717001bdd708f44c13e..0b61fc929e24c184ab59f681cfddc2e13439ca2b 100644
--- a/user/src/bin/filetest_simple.rs
+++ b/user/src/bin/ch7_file0.rs
@@ -4,35 +4,28 @@
 #[macro_use]
 extern crate user_lib;
 
-use user_lib::{
-    open,
-    close,
-    read,
-    write,
-    OpenFlags,
-};
+use user_lib::{close, open, read, write, OpenFlags};
+
+/// 测试文件基本读写,输出 Test file0 OK! 就算正确。
 
 #[no_mangle]
 pub fn main() -> i32 {
     let test_str = "Hello, world!";
-    let filea = "filea\0";
-    let fd = open(filea, OpenFlags::CREATE | OpenFlags::WRONLY);
+    let fname = "fname\0";
+    let fd = open(fname, OpenFlags::CREATE | OpenFlags::WRONLY);
     assert!(fd > 0);
     let fd = fd as usize;
     write(fd, test_str.as_bytes());
     close(fd);
 
-    let fd = open(filea, OpenFlags::RDONLY);
+    let fd = open(fname, OpenFlags::RDONLY);
     assert!(fd > 0);
     let fd = fd as usize;
     let mut buffer = [0u8; 100];
     let read_len = read(fd, &mut buffer) as usize;
     close(fd);
 
-    assert_eq!(
-        test_str,
-        core::str::from_utf8(&buffer[..read_len]).unwrap(),
-    );
-    println!("file_test passed!");
+    assert_eq!(test_str, core::str::from_utf8(&buffer[..read_len]).unwrap(),);
+    println!("Test file0 OK!");
     0
-}
\ No newline at end of file
+}
diff --git a/user/src/bin/ch7_file1.rs b/user/src/bin/ch7_file1.rs
new file mode 100644
index 0000000000000000000000000000000000000000..4b2b08a73bf905e2fe1478dce6692142af8715ba
--- /dev/null
+++ b/user/src/bin/ch7_file1.rs
@@ -0,0 +1,24 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+use user_lib::{close, fstat, open, OpenFlags, Stat, StatMode};
+
+/// 测试 fstat,输出 Test fstat OK! 就算正确。
+
+#[no_mangle]
+pub fn main() -> i32 {
+    let fname = "fname1\0";
+    let fd = open(fname, OpenFlags::CREATE | OpenFlags::WRONLY);
+    assert!(fd > 0);
+    let fd = fd as usize;
+    let stat: Stat = Stat::new();
+    let ret = fstat(fd, &stat);
+    assert_eq!(ret, 0);
+    assert_eq!(stat.mode, StatMode::FILE);
+    assert_eq!(stat.nlink, 1);
+    close(fd);
+    println!("Test fstat OK!");
+    0
+}
diff --git a/user/src/bin/ch7_file2.rs b/user/src/bin/ch7_file2.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ad20dea89e3cf2192c9588d9619b7cd40fbdc6e5
--- /dev/null
+++ b/user/src/bin/ch7_file2.rs
@@ -0,0 +1,44 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+use user_lib::{close, fstat, link, open, read, unlink, write, OpenFlags, Stat};
+
+/// 测试 link/unlink,输出 Test link OK! 就算正确。
+
+#[no_mangle]
+pub fn main() -> i32 {
+    let test_str = "Hello, world!";
+    let fname = "fname2\0";
+    let (lname0, lname1, lname2) = ("linkname0\0", "linkname1\0", "linkname2\0");
+    let fd = open(fname, OpenFlags::CREATE | OpenFlags::WRONLY) as usize;
+    link(fname, lname0);
+    let stat = Stat::new();
+    fstat(fd, &stat);
+    assert_eq!(stat.nlink, 2);
+    link(fname, lname1);
+    link(fname, lname2);
+    fstat(fd, &stat);
+    assert_eq!(stat.nlink, 4);
+    write(fd, test_str.as_bytes());
+    close(fd);
+
+    unlink(fname);
+    let fd = open(lname0, OpenFlags::RDONLY) as usize;
+    let stat2 = Stat::new();
+    let mut buf = [0u8; 100];
+    let read_len = read(fd, &mut buf) as usize;
+    assert_eq!(test_str, core::str::from_utf8(&buf[..read_len]).unwrap(),);
+    fstat(fd, &stat2);
+    assert_eq!(stat2.dev, stat.dev);
+    assert_eq!(stat2.ino, stat.ino);
+    assert_eq!(stat2.nlink, 3);
+    unlink(lname1);
+    unlink(lname2);
+    fstat(fd, &stat2);
+    assert_eq!(stat2.nlink, 1);
+    close(fd);
+    println!("Test link OK!");
+    0
+}
diff --git a/user/src/bin/ch7_usertest.rs b/user/src/bin/ch7_usertest.rs
new file mode 100644
index 0000000000000000000000000000000000000000..418dff3f652ce36c4a09c3162f6ffcc7999cea87
--- /dev/null
+++ b/user/src/bin/ch7_usertest.rs
@@ -0,0 +1,51 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+/// 辅助测例,运行所有其他测例。
+
+static TESTS: &[&str] = &[
+    "ch2_hello_world\0",
+    "ch2_power\0",
+    "ch2_write1\0",
+    "ch3_0_setprio\0",
+    "ch3_0_sleep\0",
+    "ch3_0_sleep1\0",
+    "ch4_mmap0\0",
+    "ch4_mmap1\0",
+    "ch4_mmap2\0",
+    "ch4_mmap3\0",
+    "ch4_unmap\0",
+    "ch4_unmap2\0",
+    "ch5_getpid\0",
+    "ch5_spawn0\0",
+    "ch5_spawn1\0",
+    "ch6_mail0\0",
+    "ch6_mail1\0",
+    "ch6_mail2\0",
+    "ch6_mail3\0",
+    "ch7_file0\0",
+    "ch7_file1\0",
+    "ch7_file2\0",
+];
+
+use user_lib::{spawn, waitpid};
+
+#[no_mangle]
+pub fn main() -> i32 {
+    for test in TESTS {
+        println!("Usertests: Running {}", test);
+        let pid = spawn(*test);
+        let mut xstate: i32 = Default::default();
+        let wait_pid = waitpid(pid as usize, &mut xstate);
+        assert_eq!(pid, wait_pid);
+        println!(
+            "\x1b[32mUsertests: Test {} in Process {} exited with code {}\x1b[0m",
+            test, pid, xstate
+        );
+    }
+    println!("ch7 Usertests passed!");
+    0
+}
diff --git a/user/src/bin/fantastic_text.rs b/user/src/bin/fantastic_text.rs
deleted file mode 100644
index bb51db305f0a8a19860e80ae67554d1b01dc77e0..0000000000000000000000000000000000000000
--- a/user/src/bin/fantastic_text.rs
+++ /dev/null
@@ -1,44 +0,0 @@
-#![no_std]
-#![no_main]
-
-#[macro_use]
-extern crate user_lib;
-
-macro_rules! color_text {
-    ($text:expr, $color:expr) => {{
-        format_args!("\x1b[{}m{}\x1b[0m", $color, $text)
-    }};
-}
-
-#[no_mangle]
-pub fn main() -> i32 {
-    println!(
-        "{}{}{}{}{} {}{}{}{} {}{}{}{}{}{}",
-        color_text!("H", 31),
-        color_text!("e", 32),
-        color_text!("l", 33),
-        color_text!("l", 34),
-        color_text!("o", 35),
-        color_text!("R", 36),
-        color_text!("u", 37),
-        color_text!("s", 90),
-        color_text!("t", 91),
-        color_text!("u", 92),
-        color_text!("C", 93),
-        color_text!("o", 94),
-        color_text!("r", 95),
-        color_text!("e", 96),
-        color_text!("!", 97),
-    );
-
-    let text =
-        "reguler \x1b[4munderline\x1b[24m \x1b[7mreverse\x1b[27m \x1b[9mstrikethrough\x1b[29m";
-    println!("\x1b[47m{}\x1b[0m", color_text!(text, 30));
-    for i in 31..38 {
-        println!("{}", color_text!(text, i));
-    }
-    for i in 90..98 {
-        println!("{}", color_text!(text, i));
-    }
-    0
-}
\ No newline at end of file
diff --git a/user/src/bin/forktest.rs b/user/src/bin/forktest.rs
deleted file mode 100644
index fea6967c412316a5dd6995306ad420e045f56f74..0000000000000000000000000000000000000000
--- a/user/src/bin/forktest.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-#![no_std]
-#![no_main]
-
-#[macro_use]
-extern crate user_lib;
-
-use user_lib::{fork, wait, exit};
-
-const MAX_CHILD: usize = 40;
-
-#[no_mangle]
-pub fn main() -> i32 {
-    for i in 0..MAX_CHILD {
-        let pid = fork();
-        if pid == 0 {
-            println!("I am child {}", i);
-            exit(0);
-        } else {
-            println!("forked child pid = {}", pid);
-        }
-        assert!(pid > 0);
-    }
-    let mut exit_code: i32 = 0;
-    for _ in 0..MAX_CHILD {
-        if wait(&mut exit_code) <= 0 {
-            panic!("wait stopped early");
-        }
-    }
-    if wait(&mut exit_code) > 0 {
-        panic!("wait got too many");
-    }
-    println!("forktest pass.");
-    0
-}
\ No newline at end of file
diff --git a/user/src/bin/forktest2.rs b/user/src/bin/forktest2.rs
deleted file mode 100644
index d08a412063d87238be7a2ff0120af12236d2a8ab..0000000000000000000000000000000000000000
--- a/user/src/bin/forktest2.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-#![no_std]
-#![no_main]
-
-#[macro_use]
-extern crate user_lib;
-
-use user_lib::{fork, wait, getpid, exit, sleep, get_time};
-
-static NUM: usize = 30;
-
-#[no_mangle]
-pub fn main() -> i32 {
-    for _ in 0..NUM {
-        let pid = fork();
-        if pid == 0 {
-            let current_time = get_time();
-            let sleep_length = (current_time as i32 as isize) * (current_time as i32 as isize) % 1000 + 1000;
-            println!("pid {} sleep for {} ms", getpid(), sleep_length);
-            sleep(sleep_length as usize);
-            println!("pid {} OK!", getpid());
-            exit(0);
-        }
-    }
-
-    let mut exit_code: i32 = 0;
-    for _ in 0..NUM {
-        assert!(wait(&mut exit_code) > 0);
-        assert_eq!(exit_code, 0);
-    }
-    assert!(wait(&mut exit_code) < 0);
-    println!("forktest2 test passed!");
-    0
-}
\ No newline at end of file
diff --git a/user/src/bin/forktest_simple.rs b/user/src/bin/forktest_simple.rs
deleted file mode 100644
index 821fba642fb22926df365187453fdea1cb761264..0000000000000000000000000000000000000000
--- a/user/src/bin/forktest_simple.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-#![no_std]
-#![no_main]
-
-#[macro_use]
-extern crate user_lib;
-
-use user_lib::{fork, getpid, wait};
-
-#[no_mangle]
-pub fn main() -> i32 {
-    assert_eq!(wait(&mut 0i32), -1);
-    println!("sys_wait without child process test passed!");
-    println!("parent start, pid = {}!", getpid());
-    let pid = fork();
-    if pid == 0 {
-        // child process
-        println!("hello child process!");
-        100
-    } else {
-        // parent process
-        let mut exit_code: i32 = 0;
-        println!("ready waiting on parent process!");
-        assert_eq!(pid, wait(&mut exit_code));
-        assert_eq!(exit_code, 100);
-        println!("child process pid = {}, exit code = {}", pid, exit_code);
-        0
-    }
-}
\ No newline at end of file
diff --git a/user/src/bin/forktree.rs b/user/src/bin/forktree.rs
deleted file mode 100644
index 26954b7a6512980b3aee6357d3edfee1ad5ef0f0..0000000000000000000000000000000000000000
--- a/user/src/bin/forktree.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-#![no_std]
-#![no_main]
-
-#[macro_use]
-extern crate user_lib;
-
-use user_lib::{sleep, getpid, fork, exit, yield_};
-
-const DEPTH: usize = 4;
-
-fn fork_child(cur: &str, branch: char) {
-    let mut next = [0u8; DEPTH + 1];
-    let l = cur.len();
-    if l >= DEPTH {
-        return;
-    }
-    &mut next[..l].copy_from_slice(cur.as_bytes());
-    next[l] = branch as u8;
-    if fork() == 0 {
-        fork_tree(core::str::from_utf8(&next[..l + 1]).unwrap());
-        yield_();
-        exit(0);
-    }
-}
-
-fn fork_tree(cur: &str) {
-    println!("pid{}: {}", getpid(), cur);
-    fork_child(cur, '0');
-    fork_child(cur, '1');
-}
-
-#[no_mangle]
-pub fn main() -> i32 {
-    fork_tree("");
-    sleep(3000);
-    0
-}
diff --git a/user/src/bin/matrix.rs b/user/src/bin/matrix.rs
deleted file mode 100644
index 8ef2c044a2f83ceed1d49ff58888081e8e640920..0000000000000000000000000000000000000000
--- a/user/src/bin/matrix.rs
+++ /dev/null
@@ -1,68 +0,0 @@
-#![no_std]
-#![no_main]
-
-#[macro_use]
-extern crate user_lib;
-
-use user_lib::{fork, wait, yield_, exit, getpid, get_time};
-
-static NUM: usize = 35;
-const N: usize = 10;
-static P: i32 = 10007;
-type Arr = [[i32; N]; N];
-
-fn work(times: isize) {
-    let mut a: Arr = Default::default();
-    let mut b: Arr = Default::default();
-    let mut c: Arr = Default::default();
-    for i in 0..N {
-        for j in 0..N {
-            a[i][j] = 1;
-            b[i][j] = 1;
-        }
-    }
-    yield_();
-    println!("pid {} is running ({} times)!.", getpid(), times);
-    for _ in 0..times {
-        for i in 0..N {
-            for j in 0..N {
-                c[i][j] = 0;
-                for k in 0..N {
-                    c[i][j] = (c[i][j] + a[i][k] * b[k][j]) % P;
-                }
-            }
-        }
-        for i in 0..N {
-            for j in 0..N {
-                a[i][j] = c[i][j];
-                b[i][j] = c[i][j];
-            }
-        }
-    }
-    println!("pid {} done!.", getpid());
-    exit(0);
-}
-
-#[no_mangle]
-pub fn main() -> i32 {
-    for _ in 0..NUM {
-        let pid = fork();
-        if pid == 0 {
-            let current_time = get_time();
-            let times = (current_time as i32 as isize) * (current_time as i32 as isize) % 1000;
-            work(times * 10);
-        }
-    }
-
-    println!("fork ok.");
-
-    let mut exit_code: i32 = 0;
-    for _ in 0..NUM {
-        if wait(&mut exit_code) < 0 {
-            panic!("wait failed.");
-        }
-    }
-    assert!(wait(&mut exit_code) < 0);
-    println!("matrix passed.");
-    0
-}
\ No newline at end of file
diff --git a/user/src/bin/pipe_large_test.rs b/user/src/bin/pipe_large_test.rs
deleted file mode 100644
index 121987be346c50c1efec3a5f500f4c40f6405359..0000000000000000000000000000000000000000
--- a/user/src/bin/pipe_large_test.rs
+++ /dev/null
@@ -1,69 +0,0 @@
-#![no_std]
-#![no_main]
-
-#[macro_use]
-extern crate user_lib;
-
-extern crate alloc;
-
-use user_lib::{fork, close, pipe, read, write, wait, get_time};
-use alloc::format;
-
-const LENGTH: usize = 3000;
-#[no_mangle]
-pub fn main() -> i32 {
-    // create pipes
-    // parent write to child
-    let mut down_pipe_fd = [0usize; 2];
-    // child write to parent
-    let mut up_pipe_fd = [0usize; 2];
-    pipe(&mut down_pipe_fd);
-    pipe(&mut up_pipe_fd);
-    let mut random_str = [0u8; LENGTH];
-    if fork() == 0 {
-        // close write end of down pipe
-        close(down_pipe_fd[1]);
-        // close read end of up pipe
-        close(up_pipe_fd[0]);
-        assert_eq!(read(down_pipe_fd[0], &mut random_str) as usize, LENGTH);
-        close(down_pipe_fd[0]);
-        let sum: usize = random_str.iter().map(|v| *v as usize).sum::<usize>();
-        println!("sum = {}(child)", sum);
-        let sum_str = format!("{}", sum);
-        write(up_pipe_fd[1], sum_str.as_bytes());
-        close(up_pipe_fd[1]);
-        println!("Child process exited!");
-        0
-    } else {
-        // close read end of down pipe
-        close(down_pipe_fd[0]);
-        // close write end of up pipe
-        close(up_pipe_fd[1]);
-        // generate a long random string
-        for i in 0..LENGTH {
-            random_str[i] = get_time() as u8;
-        }
-        // send it
-        assert_eq!(write(down_pipe_fd[1], &random_str) as usize, random_str.len());
-        // close write end of down pipe
-        close(down_pipe_fd[1]);
-        // calculate sum(parent)
-        let sum: usize = random_str.iter().map(|v| *v as usize).sum::<usize>();
-        println!("sum = {}(parent)", sum);
-        // recv sum(child)
-        let mut child_result = [0u8; 32];
-        let result_len = read(up_pipe_fd[0], &mut child_result) as usize;
-        close(up_pipe_fd[0]);
-        // check
-        assert_eq!(
-            sum,
-            str::parse::<usize>(
-                core::str::from_utf8(&child_result[..result_len]).unwrap()
-            ).unwrap()
-        );
-        let mut _unused: i32 = 0;
-        wait(&mut _unused);
-        println!("pipe_large_test passed!");
-        0
-    }
-}
\ No newline at end of file
diff --git a/user/src/bin/sleep_simple.rs b/user/src/bin/sleep_simple.rs
deleted file mode 100644
index 4c058f879d14214e059152f22f698a4eaa032746..0000000000000000000000000000000000000000
--- a/user/src/bin/sleep_simple.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-#![no_std]
-#![no_main]
-
-#[macro_use]
-extern crate user_lib;
-
-use user_lib::{get_time, sleep};
-
-#[no_mangle]
-pub fn main() -> i32 {
-    println!("into sleep test!");
-    let start = get_time();
-    println!("current time_msec = {}", start);
-    sleep(100);
-    let end = get_time();
-    println!("time_msec = {} after sleeping 100 ticks, delta = {}ms!", end, end - start);
-    println!("r_sleep passed!");
-    0
-}
\ No newline at end of file
diff --git a/user/src/bin/stack_overflow.rs b/user/src/bin/stack_overflow.rs
deleted file mode 100644
index e0ea471d536ac0e53be65d112087c6c9ac2f9fd0..0000000000000000000000000000000000000000
--- a/user/src/bin/stack_overflow.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-#![no_std]
-#![no_main]
-
-#[macro_use]
-extern crate user_lib;
-
-fn f(d: usize) {
-    println!("d = {}",d);
-    f(d + 1);
-}
-
-#[no_mangle]
-pub fn main() -> i32 {
-    println!("It should trigger segmentation fault!");
-    f(0);
-    0
-}
\ No newline at end of file
diff --git a/user/src/console.rs b/user/src/console.rs
index 810ebba13a5ea2e4c6bf20f280e18fb5dc28b425..2257557baa3d47384fbc580e527d0a549f22bc53 100644
--- a/user/src/console.rs
+++ b/user/src/console.rs
@@ -1,7 +1,7 @@
 use core::fmt::{self, Write};
 
-const STDIN: usize = 0;
-const STDOUT: usize = 1;
+pub const STDIN: usize = 0;
+pub const STDOUT: usize = 1;
 
 use super::{read, write};
 
diff --git a/user/src/lib.rs b/user/src/lib.rs
index 0ef03bfb78e91bdc70238f086ef02039a4ff7364..4cb9e9027fcc9ba80d7f86d101fb122fbe296160 100644
--- a/user/src/lib.rs
+++ b/user/src/lib.rs
@@ -15,8 +15,61 @@ extern crate bitflags;
 
 use syscall::*;
 use buddy_system_allocator::LockedHeap;
+pub use console::{STDIN, STDOUT};
 use alloc::vec::Vec;
 
+#[repr(C)]
+#[derive(Debug)]
+pub struct TimeVal {
+    pub sec: usize,
+    pub usec: usize,
+}
+
+impl TimeVal {
+    pub fn new() -> Self {
+        TimeVal { sec: 0, usec: 0 }
+    }
+}
+
+#[repr(C)]
+#[derive(Debug)]
+pub struct Stat {
+    /// ID of device containing file
+    pub dev: u64,
+    /// inode number
+    pub ino: u64,
+    /// file type and mode
+    pub mode: StatMode,
+    /// number of hard links
+    pub nlink: u32,
+    /// unused pad
+    pad: [u64; 7],
+}
+
+impl Stat {
+    pub fn new() -> Self {
+        Stat {
+            dev: 0,
+            ino: 0,
+            mode: StatMode::NULL,
+            nlink: 0,
+            pad: [0; 7],
+        }
+    }
+}
+
+bitflags! {
+    pub struct StatMode: u32 {
+        const NULL  = 0;
+        /// directory
+        const DIR   = 0o040000;
+        /// ordinary regular file
+        const FILE  = 0o100000;
+    }
+}
+
+const AT_FDCWD: isize = -100;
+
 const USER_HEAP_SIZE: usize = 32768;
 
 static mut HEAP_SPACE: [u8; USER_HEAP_SIZE] = [0; USER_HEAP_SIZE];
@@ -77,10 +130,21 @@ pub fn read(fd: usize, buf: &mut [u8]) -> isize { sys_read(fd, buf) }
 pub fn write(fd: usize, buf: &[u8]) -> isize { sys_write(fd, buf) }
 pub fn exit(exit_code: i32) -> ! { sys_exit(exit_code); }
 pub fn yield_() -> isize { sys_yield() }
-pub fn get_time() -> isize { sys_get_time() }
+// pub fn get_time() -> isize { sys_get_time() }
+pub fn get_time() -> isize {
+    let time = TimeVal::new();
+    match sys_get_time(&time, 0) {
+        0 => {
+            return ((time.sec & 0xffff) * 1000 + time.usec / 1000) as isize;
+        },
+        _ => -1
+    }
+}
 pub fn getpid() -> isize { sys_getpid() }
 pub fn fork() -> isize { sys_fork() }
-pub fn exec(path: &str, args: &[*const u8]) -> isize { sys_exec(path, args) }
+pub fn exec(path: &str, args: &[*const u8]) -> isize { 
+    sys_exec(path, args) 
+}
 pub fn wait(exit_code: &mut i32) -> isize {
     loop {
         match sys_waitpid(-1, exit_code as *mut _) {
@@ -101,8 +165,53 @@ pub fn waitpid(pid: usize, exit_code: &mut i32) -> isize {
     }
 }
 pub fn sleep(period_ms: usize) {
-    let start = sys_get_time();
-    while sys_get_time() < start + period_ms as isize {
+    let start = get_time();
+    while get_time() < start + period_ms as isize {
         sys_yield();
     }
+}
+//=====================lab3===============================
+pub fn set_priority(prio: isize) -> isize {
+    sys_set_priority(prio)
+}
+
+
+//=====================lab4===============================
+pub fn mmap(start: usize, len: usize, prot: usize) -> isize {
+    sys_mmap(start, len, prot)
+}
+
+pub fn munmap(start: usize, len: usize) -> isize {
+    sys_munmap(start, len)
+}
+
+
+//=====================lab5===============================
+pub fn spawn(path: &str) -> isize {
+    sys_spawn(path)
+}
+// pub fn exec(path: &str, args: &[*const u8]) -> isize { 
+//     sys_exec(path, args) 
+// }
+
+//=====================lab6===============================
+pub fn mail_read(buf: &mut [u8]) -> isize {
+    sys_mail_read(buf)
+}
+
+pub fn mail_write(pid: usize, buf: &[u8]) -> isize {
+    sys_mail_write(pid, buf)
+}
+
+//=====================lab6===============================
+pub fn link(old_path: &str, new_path: &str) -> isize {
+    sys_linkat(AT_FDCWD as usize, old_path, AT_FDCWD as usize, new_path, 0)
+}
+
+pub fn unlink(path: &str) -> isize {
+    sys_unlinkat(AT_FDCWD as usize, path, 0)
+}
+
+pub fn fstat(fd: usize, st: &Stat) -> isize {
+    sys_fstat(fd, st)
 }
\ No newline at end of file
diff --git a/user/src/linker.ld b/user/src/linker.ld
index e05a98ba75dd800b4afb37f737ff648d9070ad89..7273618c9f6b2cb497b34e1ffae692b79fcce40a 100644
--- a/user/src/linker.ld
+++ b/user/src/linker.ld
@@ -14,13 +14,16 @@ SECTIONS
     . = ALIGN(4K);
     .rodata : {
         *(.rodata .rodata.*)
+        *(.srodata .srodata.*)
     }
     . = ALIGN(4K);
     .data : {
         *(.data .data.*)
+        *(.sdata .sdata.*)
     }
     .bss : {
         *(.bss .bss.*)
+        *(.sbss .sbss.*)
     }
     /DISCARD/ : {
         *(.eh_frame)
diff --git a/user/src/syscall.rs b/user/src/syscall.rs
index 1cd30f8459fe6bfceff0e858628d909da0e192d7..9ae58561644f0b2220f5d62f0d2bdab1ce5d6e4b 100644
--- a/user/src/syscall.rs
+++ b/user/src/syscall.rs
@@ -1,9 +1,11 @@
+use super::{Stat, TimeVal};
 const SYSCALL_DUP: usize = 24;
-const SYSCALL_OPEN: usize = 56;
+const SYSCALL_OPENAT: usize = 56;
 const SYSCALL_CLOSE: usize = 57;
 const SYSCALL_PIPE: usize = 59;
 const SYSCALL_READ: usize = 63;
 const SYSCALL_WRITE: usize = 64;
+
 const SYSCALL_EXIT: usize = 93;
 const SYSCALL_YIELD: usize = 124;
 const SYSCALL_GET_TIME: usize = 169;
@@ -11,6 +13,17 @@ const SYSCALL_GETPID: usize = 172;
 const SYSCALL_FORK: usize = 220;
 const SYSCALL_EXEC: usize = 221;
 const SYSCALL_WAITPID: usize = 260;
+const SYSCALL_SET_PRIORITY: usize = 140;
+const SYSCALL_MUNMAP: usize = 215;
+const SYSCALL_MMAP: usize = 222;
+const SYSCALL_SPAWN: usize = 400;
+//=====================lab6===============================
+const SYSCALL_MAIL_READ: usize = 401;
+const SYSCALL_MAIL_WRITE: usize = 402;
+//=====================lab7===============================
+const SYSCALL_UNLINKAT: usize = 35;
+const SYSCALL_LINKAT: usize = 37;
+const SYSCALL_FSTAT: usize = 80;
 
 fn syscall(id: usize, args: [usize; 3]) -> isize {
     let mut ret: isize;
@@ -25,12 +38,26 @@ fn syscall(id: usize, args: [usize; 3]) -> isize {
     ret
 }
 
+fn syscall5(id: usize, args: [usize; 5]) -> isize {
+    let mut ret: isize;
+    unsafe {
+        llvm_asm!("ecall"
+            : "={x10}" (ret)
+            : "{x10}" (args[0]), "{x11}" (args[1]), "{x12}" (args[2]), "{x13}" (args[3]),
+                "{x14}" (args[4]), "{x17}" (id)
+            : "memory"
+            : "volatile"
+        );
+    }
+    ret
+}
+
 pub fn sys_dup(fd: usize) -> isize {
     syscall(SYSCALL_DUP, [fd, 0, 0])
 }
 
 pub fn sys_open(path: &str, flags: u32) -> isize {
-    syscall(SYSCALL_OPEN, [path.as_ptr() as usize, flags as usize, 0])
+    syscall(SYSCALL_OPENAT, [path.as_ptr() as usize, flags as usize, 0])
 }
 
 pub fn sys_close(fd: usize) -> isize {
@@ -58,8 +85,11 @@ pub fn sys_yield() -> isize {
     syscall(SYSCALL_YIELD, [0, 0, 0])
 }
 
-pub fn sys_get_time() -> isize {
-    syscall(SYSCALL_GET_TIME, [0, 0, 0])
+// pub fn sys_get_time() -> isize {
+//     syscall(SYSCALL_GET_TIME, [0, 0, 0])
+// }
+pub fn sys_get_time(time: &TimeVal, tz: usize) -> isize {
+    syscall(SYSCALL_GET_TIME, [time as *const _ as usize, tz, 0])
 }
 
 pub fn sys_getpid() -> isize {
@@ -76,4 +106,67 @@ pub fn sys_exec(path: &str, args: &[*const u8]) -> isize {
 
 pub fn sys_waitpid(pid: isize, exit_code: *mut i32) -> isize {
     syscall(SYSCALL_WAITPID, [pid as usize, exit_code as usize, 0])
+}
+//=====================lab3===============================
+pub fn sys_set_priority(prio: isize) -> isize {
+    syscall(SYSCALL_SET_PRIORITY, [prio as usize, 0, 0])
+}
+
+//=====================lab4===============================
+pub fn sys_mmap(start: usize, len: usize, prot: usize) -> isize {
+    syscall(SYSCALL_MMAP, [start, len, prot])
+}
+
+pub fn sys_munmap(start: usize, len: usize) -> isize {
+    syscall(SYSCALL_MUNMAP, [start, len, 0])
+}
+
+//=====================lab5===============================
+
+pub fn sys_spawn(path: &str) -> isize {
+    syscall(SYSCALL_SPAWN, [path.as_ptr() as usize, 0, 0])
+}
+
+
+//=====================lab6===============================
+pub fn sys_mail_read(buffer: &mut [u8]) -> isize {
+    syscall(
+        SYSCALL_MAIL_READ,
+        [buffer.as_ptr() as usize, buffer.len(), 0],
+    )
+}
+
+pub fn sys_mail_write(pid: usize, buffer: &[u8]) -> isize {
+    syscall(
+        SYSCALL_MAIL_WRITE,
+        [pid, buffer.as_ptr() as usize, buffer.len()],
+    )
+}
+
+//=====================lab7===============================
+pub fn sys_linkat(
+    old_dirfd: usize,
+    old_path: &str,
+    new_dirfd: usize,
+    new_path: &str,
+    flags: usize,
+) -> isize {
+    syscall5(
+        SYSCALL_LINKAT,
+        [
+            old_dirfd,
+            old_path.as_ptr() as usize,
+            new_dirfd,
+            new_path.as_ptr() as usize,
+            flags,
+        ],
+    )
+}
+
+pub fn sys_unlinkat(dirfd: usize, path: &str, flags: usize) -> isize {
+    syscall(SYSCALL_UNLINKAT, [dirfd, path.as_ptr() as usize, flags])
+}
+
+pub fn sys_fstat(fd: usize, st: &Stat) -> isize {
+    syscall(SYSCALL_FSTAT, [fd, st as *const _ as usize, 0])
 }
\ No newline at end of file
diff --git a/user/target/.rustc_info.json b/user/target/.rustc_info.json
new file mode 100644
index 0000000000000000000000000000000000000000..2b9684e525a718993f55e9a0f8ccbe3b5ff6e22f
--- /dev/null
+++ b/user/target/.rustc_info.json
@@ -0,0 +1 @@
+{"rustc_fingerprint":11314787443920216368,"outputs":{"17103856195547239926":["___\nlib___.rlib\nlib___.a\n/home/user/.rustup/toolchains/nightly-2021-01-30-x86_64-unknown-linux-gnu\ndebug_assertions\npanic=\"abort\"\nproc_macro\ntarget_arch=\"riscv64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_feature=\"a\"\ntarget_feature=\"c\"\ntarget_feature=\"d\"\ntarget_feature=\"f\"\ntarget_feature=\"m\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_os=\"none\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\n","warning: dropping unsupported crate type `dylib` for target `riscv64gc-unknown-none-elf`\n\nwarning: dropping unsupported crate type `cdylib` for target `riscv64gc-unknown-none-elf`\n\nwarning: dropping unsupported crate type `proc-macro` for target `riscv64gc-unknown-none-elf`\n\nwarning: 3 warnings emitted\n\n"],"1164083562126845933":["rustc 1.51.0-nightly (b12290861 2021-01-29)\nbinary: rustc\ncommit-hash: b122908617436af187252572ed5db96850551380\ncommit-date: 2021-01-29\nhost: x86_64-unknown-linux-gnu\nrelease: 1.51.0-nightly\nLLVM version: 11.0.1\n",""],"4476964694761187371":["___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/user/.rustup/toolchains/nightly-2021-01-30-x86_64-unknown-linux-gnu\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"unknown\"\nunix\n",""]},"successes":{}}
\ No newline at end of file
diff --git a/user/target/CACHEDIR.TAG b/user/target/CACHEDIR.TAG
new file mode 100644
index 0000000000000000000000000000000000000000..20d7c319cda945dc07729797750a49b232206ab5
--- /dev/null
+++ b/user/target/CACHEDIR.TAG
@@ -0,0 +1,3 @@
+Signature: 8a477f597d28d172789f06886806bc55
+# This file is a cache directory tag created by cargo.
+# For information about cache directory tags see https://bford.info/cachedir/
diff --git a/user/target/release/.cargo-lock b/user/target/release/.cargo-lock
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/user/target/riscv64gc-unknown-none-elf/CACHEDIR.TAG b/user/target/riscv64gc-unknown-none-elf/CACHEDIR.TAG
new file mode 100644
index 0000000000000000000000000000000000000000..20d7c319cda945dc07729797750a49b232206ab5
--- /dev/null
+++ b/user/target/riscv64gc-unknown-none-elf/CACHEDIR.TAG
@@ -0,0 +1,3 @@
+Signature: 8a477f597d28d172789f06886806bc55
+# This file is a cache directory tag created by cargo.
+# For information about cache directory tags see https://bford.info/cachedir/
diff --git a/user/target/riscv64gc-unknown-none-elf/release/.cargo-lock b/user/target/riscv64gc-unknown-none-elf/release/.cargo-lock
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/user/target/riscv64gc-unknown-none-elf/release/libuser_lib.d b/user/target/riscv64gc-unknown-none-elf/release/libuser_lib.d
new file mode 100644
index 0000000000000000000000000000000000000000..feaeb4ff4bf61346bba1e6e570c3f7fefe3e3005
--- /dev/null
+++ b/user/target/riscv64gc-unknown-none-elf/release/libuser_lib.d
@@ -0,0 +1 @@
+/home/user/OS/rCore-Tutorial-v3/user/target/riscv64gc-unknown-none-elf/release/libuser_lib.rlib: /home/user/OS/rCore-Tutorial-v3/user/src/console.rs /home/user/OS/rCore-Tutorial-v3/user/src/lang_items.rs /home/user/OS/rCore-Tutorial-v3/user/src/lib.rs /home/user/OS/rCore-Tutorial-v3/user/src/syscall.rs
diff --git a/user/target/riscv64gc-unknown-none-elf/release/libuser_lib.rlib b/user/target/riscv64gc-unknown-none-elf/release/libuser_lib.rlib
new file mode 100644
index 0000000000000000000000000000000000000000..8747d477cced1834b5bf0eb7b4c3b1befa9aaf9f
Binary files /dev/null and b/user/target/riscv64gc-unknown-none-elf/release/libuser_lib.rlib differ