diff --git a/frame.c b/frame.c
new file mode 100644
index 0000000000000000000000000000000000000000..5ee340f3b49232c5f1fe0e8a2c1471c5ce6c20a9
--- /dev/null
+++ b/frame.c
@@ -0,0 +1,101 @@
+#include <system.h>
+
+/* 帧位图,用于记录每个帧的使用状态(1=已分配,0=空闲) */
+unsigned int* frame_bitmap;
+unsigned int total_frames;
+
+/**
+ * 设置帧位图中的指定位
+ * @param frame_addr 要设置的帧地址
+ */
+static void set_frame(addr frame_addr) {
+    addr frame_index = frame_addr / 0x1000;            // 计算帧索引
+    addr bitmap_index = INDEX_FROM_BIT(frame_index);  // 计算位图数组中的索引
+    addr bit_offset = OFFSET_FROM_BIT(frame_index);   // 计算位图索引内的偏移量
+    frame_bitmap[bitmap_index] |= (0x1 << bit_offset); // 设置该位为 1
+}
+
+/**
+ * 清除帧位图中的指定位
+ * @param frame_addr 要清除的帧地址
+ */
+static void clear_frame(addr frame_addr) {
+    addr frame_index = frame_addr / 0x1000;            // 计算帧索引
+    addr bitmap_index = INDEX_FROM_BIT(frame_index);  // 计算位图数组中的索引
+    addr bit_offset = OFFSET_FROM_BIT(frame_index);   // 计算位图索引内的偏移量
+    frame_bitmap[bitmap_index] &= ~(0x1 << bit_offset); // 清除该位
+}
+
+/**
+ * 检查帧位图中的指定位是否已设置
+ * @param frame_addr 要检查的帧地址
+ * @return 返回 1 表示帧已分配,0 表示帧空闲
+ */
+static unsigned int test_frame(addr frame_addr) {
+    addr frame_index = frame_addr / 0x1000;            // 计算帧索引
+    addr bitmap_index = INDEX_FROM_BIT(frame_index);  // 计算位图数组中的索引
+    addr bit_offset = OFFSET_FROM_BIT(frame_index);   // 计算位图索引内的偏移量
+    return (frame_bitmap[bitmap_index] & (0x1 << bit_offset)); // 返回该位的状态
+}
+
+/**
+ * 查找第一个空闲帧
+ * @return 返回空闲帧的索引,如果没有空闲帧,返回 -1
+ */
+static addr find_first_free_frame() {
+    // 遍历帧位图
+    for (unsigned int i = 0; i < INDEX_FROM_BIT(total_frames); i++) {
+        if (frame_bitmap[i] != 0xFFFFFFFF) { // 如果当前位图块未完全占用
+            for (unsigned int j = 0; j < 32; j++) {
+                unsigned int test_bit = 0x1 << j;
+                if (!(frame_bitmap[i] & test_bit)) {
+                    return i * 32 + j; // 计算帧索引并返回
+                }
+            }
+        }
+    }
+    // 未找到空闲帧
+    return (addr)-1;
+}
+
+/**
+ * 分配一个物理帧
+ * @param p 要分配帧的页面
+ * @param is_kernel 是否为内核模式页面
+ * @param is_writable 页面是否可写
+ */
+void frame_alloc(struct page* p, int is_kernel, int is_writable) {
+    if (p->frame != 0) {
+        // 页面已分配物理帧,直接返回
+        return;
+    }
+
+    // 查找第一个空闲帧
+    addr frame_index = find_first_free_frame();
+    if (frame_index == (addr)-1) {
+        PANIC("没有空闲帧!"); // 如果没有空闲帧,抛出异常
+    }
+
+    // 设置帧位图并更新页面属性
+    set_frame(frame_index * 0x1000);   // 将帧标记为已分配
+    p->present = 1;                   // 设置页面为存在
+    p->rw = is_writable ? 1 : 0;      // 设置页面是否可写
+    p->user = is_kernel ? 0 : 1;      // 设置页面是否为用户模式
+    p->frame = frame_index;           // 记录分配的帧索引
+}
+
+/**
+ * 释放物理帧
+ * @param p 要释放帧的页面
+ */
+void frame_free(struct page* p) {
+    if (p->frame == 0) {
+        // 页面未分配物理帧,直接返回
+        return;
+    }
+
+    // 清除帧位图并更新页面属性
+    clear_frame(p->frame * 0x1000);   // 将帧标记为空闲
+    p->frame = 0x0;                   // 清除页面的帧索引
+}
+
diff --git a/gdt.c b/gdt.c
index c4d0d9c4878531a1be529ddae71fc66b8eab0881..26588df31a294856e2084d277c604e3ef1861455 100755
--- a/gdt.c
+++ b/gdt.c
@@ -1,8 +1,6 @@
-/* bkerndev - Bran's Kernel Development Tutorial
-*  作者:Brandon F. (friesenb@gmail.com)
+/*
 *  描述:全局描述符表(GDT)管理
-*
-*  注意:无明示或暗示的保证。使用风险自负。 */
+*/
 
 /* 定义一个GDT条目 */
 struct gdt_entry
@@ -68,6 +66,13 @@ void gdt_install()
     *  但这个条目的访问字节中的描述符类型说它是一个数据段 */
     gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF);
 
+    /* 安装用户模式段到全局描述符表(GDT)中。 */
+    gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF);
+	gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF);
+
+    tss_install(5, 0x10, 0x0); // 安装TSS到GDT中
+
     /* 刷新旧的GDT并安装新的更改! */
     gdt_flush();
+    tss_flush();
 }
diff --git a/idt.c b/idt.c
index fcbc50cd02a237c02fab7eabb20c375bc8e9af80..bdbb3d3ea28353510862bfcc95531884d72f6bf2 100755
--- a/idt.c
+++ b/idt.c
@@ -1,8 +1,6 @@
-/* bkerndev - Bran's Kernel Development Tutorial
-*  作者:Brandon F. (friesenb@gmail.com)
+/* 
 *  描述:中断描述符表(IDT)管理
-*
-*  注意:无明示或暗示的保证。使用风险自负。 */
+*/
 #include <system.h>
 
 /* 定义一个IDT条目 */
diff --git a/include/system.h b/include/system.h
index 5456667c4c5c9dae95abe446de9f3128f22e7d83..114f30d58b3b8f0c62a420b270be19b82c5e9332 100755
--- a/include/system.h
+++ b/include/system.h
@@ -7,6 +7,7 @@
 #define __SYSTEM_H
 
 typedef int size_t;
+typedef unsigned long int addr;
 
 /* This defines what the stack looks like after an ISR was running */
 struct regs
@@ -18,12 +19,11 @@ struct regs
 };
 
 /* MAIN.C */
-extern void *memcpy(void *dest, const void *src, size_t count);
-extern void *memset(void *dest, char val, size_t count);
-extern unsigned short *memsetw(unsigned short *dest, unsigned short val, size_t count);
-extern size_t strlen(const char *str);
-extern unsigned char inportb (unsigned short _port);
-extern void outportb (unsigned short _port, unsigned char _data);
+extern void* memcpy(void* dest, const void* src, int count);
+extern void* memset(void* dest, unsigned char val, int count);
+extern unsigned short* memsetw(unsigned short* dest, unsigned short val, int count);
+extern unsigned char inportb(unsigned short _port);
+extern void outportb(unsigned short _port, unsigned char _data);
 
 /* CONSOLE.C */
 extern void init_video(void);
@@ -32,13 +32,23 @@ extern void putch(unsigned char c);
 extern void cls();
 
 /* GDT.C */
+extern void gdt_flush();
 extern void gdt_set_gate(int num, unsigned long base, unsigned long limit, unsigned char access, unsigned char gran);
 extern void gdt_install();
 
 /* IDT.C */
+extern void idt_load();
 extern void idt_set_gate(unsigned char num, unsigned long base, unsigned short sel, unsigned char flags);
 extern void idt_install();
 
+struct regs
+{
+	unsigned int ds;					/* data segment selector */
+	unsigned int edi, esi, ebp, esp, ebx, edx, ecx, eax;	/* pushed by 'pusha' */
+	unsigned int int_no, err_code;				/* our 'push byte #' and ecodes do this */
+	unsigned int eip, cs, eflags, useresp, ss;              /* pushed by the processor automatically */
+};
+
 /* ISRS.C */
 extern void isrs_install();
 
@@ -54,4 +64,313 @@ extern void timer_install();
 /* KEYBOARD.C */
 extern void keyboard_install();
 
+/* Macros used in the bitset algorithms */
+#define INDEX_FROM_BIT(a) (a/(8*4))
+#define OFFSET_FROM_BIT(a) (a%(8*4))
+
+/* FRAME.C */
+extern void frame_alloc(struct page* p, int is_kernel, int is_writable);
+extern void frame_free(struct page* p);
+
+/*kmem*/
+extern addr kmalloc_a(addr size);
+extern addr kmalloc_p(addr size, addr* phys);
+extern addr kmalloc_ap(addr size, addr* phys);
+extern addr kmalloc(addr size);
+extern void kfree(addr pos);
+
+extern addr kmem_total();
+extern void kmem_install(struct multiboot_info* mbt);
+
+/* Multiboot structure definitions for information passed
+ * by GRUB to the main() function */
+struct multiboot_header
+{
+	unsigned long magic, flags, checksum;
+	unsigned long header_addr;
+	unsigned long load_addr, load_end_addr;
+	unsigned long bss_end_addr;
+	unsigned long entry_addr;
+} __attribute__((packed));
+
+struct aout_symbol_table
+{
+	unsigned long tabsize;
+	unsigned long strsize;
+	unsigned long addr;
+	unsigned long reserved;
+} __attribute__((packed));
+
+struct multiboot_info
+{
+	unsigned long flags;
+	unsigned long mem_lower, mem_upper;
+	unsigned long boot_device;
+	unsigned long cmdline;
+	unsigned long mods_count, mods_addr;
+	struct aout_symbol_table symbols;
+	unsigned long mmap_length;
+	unsigned long mmap_addr;
+} __attribute__((packed));
+
+struct multiboot_memory_map
+{
+	unsigned int size;
+	unsigned long long int base_addr;
+	unsigned long long int length;
+	unsigned int type;
+} __attribute__((packed));
+
+/*page*/
+/* Structures */
+typedef struct page
+{
+	unsigned int present	: 1;	/* Page present in memory */
+	unsigned int rw		: 1;	/* Read-only if clear, read-write if set */
+	unsigned int user	: 1;	/* Supervisor level if clear */
+	unsigned int accessed	: 1;	/* Has the page been accessed since last refresh? */
+	unsigned int dirty	: 1;	/* Has the page been written since last refresh? */
+	unsigned int unused	: 7;	/* Amalgamation of unused and reserved bits */
+	unsigned int frame	: 20;	/* Frame address (shifted 12 bits */
+} page_t;
+
+struct page_table
+{
+	struct page pages[1024];
+} __attribute__((packed));
+
+
+struct page_directory
+{
+	/* Array of pointers to page tables */
+	struct page_table* tables[1024];
+
+	/* Array of pointers to the page tables above, but
+	 * gives their physical location, for loading into
+	 * the CR3 register */
+	addr tables_phys[1024];
+
+	/* The physical address of tables_phys.  This comes
+	 * into play when we get our kernel heap allocated
+	 * and the directory may be in a different location
+	 * in virtual memory */
+	addr phys_addr;
+} __attribute__((packed));
+
+
+
+extern struct page_directory* current_directory;
+extern struct page_directory* kernel_directory;
+
+/* PAGE.C */
+extern void page_install();
+extern void page_switch(struct page_directory* dir);
+extern page_t page_get(addr address, int make, struct page_directory* dir);
+
+/* SCRN.C */
+extern void cls();
+extern void putch(unsigned char c);
+extern void puts(unsigned char* str);
+extern void putp(addr pointer);
+extern void settextcolor(unsigned char forecolor, unsigned char backcolor);
+extern void init_video();
+
+/* STRING.C */
+extern int strlen(const unsigned char* str);
+extern int strcpy(unsigned char* dest, const unsigned char* src, unsigned int size);
+extern int strcmp(const unsigned char* a, const unsigned char* b);
+extern unsigned char* strrev(unsigned char* str);
+extern unsigned char* itoa(unsigned long num, unsigned char* str, int base);
+
+/* Macro definitions */
+#define __QUOTEME_(x) #x
+#define __QUOTEME(x) __QUOTEME_(x)
+#define ASSERT(expr) \
+	if (!(expr)) \
+		panic("kernel panic (" __FILE__ ":" __QUOTEME(__LINE__) ") - assert \"" __QUOTEME(expr) "\" failed");
+#define PANIC(msg) \
+	panic("kernel panic (" __FILE__ ":" __QUOTEME(__LINE__) ") - " msg);
+
+/* Type definitions */
+typedef unsigned long int addr;
+
+/* SYSTEM.C */
+extern void panic(unsigned char* msg);
+
+#define KERNEL_STACK_SIZE 2048			/* Use a 2KB stack */
+
+/* Structure for a process */
+struct task
+{
+	int id;					/* Process ID */
+	addr esp, ebp;				/* Stack and base pointers */
+	addr eip;				/* Instruction pointer */
+	struct page_directory* page_directory;	/* Page directory */
+	addr kernel_stack;			/* Kernel stack location */
+	struct task* next;			/* Next task in a linked list */
+};
+
+/* TASK.C */
+extern void task_install();
+extern void task_switch();
+extern void move_stack(void* new_stack_start, addr size);
+
+extern int fork();
+extern int getpid();
+
+typedef volatile struct tss
+{
+	unsigned short link;
+	unsigned short link_h;
+
+	unsigned long esp0;
+	unsigned short ss0;
+	unsigned short ss0_h;
+
+	unsigned long esp1;
+	unsigned short ss1;
+	unsigned short ss1_h;
+
+	unsigned long esp2;
+	unsigned short ss2;
+	unsigned short ss2_h;
+
+	unsigned long cr3;
+	unsigned long eip;
+	unsigned long eflags;
+
+	unsigned long eax;
+	unsigned long ecx;
+	unsigned long edx;
+	unsigned long ebx;
+
+	unsigned long esp;
+	unsigned long ebp;
+	
+	unsigned long esi;
+	unsigned long edi;
+
+	unsigned short es;
+	unsigned short es_h;
+
+	unsigned short cs;
+	unsigned short cs_h;
+
+	unsigned short ss;
+	unsigned short ss_h;
+
+	unsigned short ds;
+	unsigned short ds_h;
+
+	unsigned short fs;
+	unsigned short fs_h;
+
+	unsigned short gs;
+	unsigned short gs_h;
+
+	unsigned short ldt;
+	unsigned short ldt_h;
+
+	unsigned short trap;
+	unsigned short iomap;
+} __attribute__((packed)) tss_t;
+
+/* TSS.C */
+extern void tss_set_kernel_stack(unsigned int stack);
+extern void tss_install(signed int num, unsigned short ss0, unsigned short esp0);
+extern void tss_switch();
+
+#define FS_FILE		0x01
+#define FS_DIRECTORY	0x02
+#define FS_CHARDEVICE	0x03
+#define FS_BLOCKDEVICE	0x04
+#define FS_PIPE		0x05
+#define FS_SYMLINK	0x06
+#define FS_MOUNTPOINT	0x08
+
+struct fs_node;
+
+/* Function definitions for virtual filesystems to implement */
+typedef unsigned int (*read_type_t)(struct fs_node*, unsigned int, unsigned int, unsigned char*);
+typedef unsigned int (*write_type_t)(struct fs_node*, unsigned int, unsigned int, unsigned char*);
+typedef void (*open_type_t)(struct fs_node*);
+typedef void (*close_type_t)(struct fs_node*);
+typedef struct dirent* (*readdir_type_t)(struct fs_node*, unsigned int);
+typedef struct fs_node* (*finddir_type_t)(struct fs_node*, char* name);
+
+/* The definition of a filesystem inode */
+typedef struct fs_node
+{
+	signed char name[128];	/* The filename */
+	unsigned int inode;	/* The node ID */
+	unsigned int flags;	/* The node type */
+	unsigned int mask;	/* The permissions mask */
+	unsigned int uid;	/* The owner ID */
+	unsigned int gid;	/* The group ID */
+	unsigned int length;	/* The length */
+	unsigned int impl;	/* Implementation-specific number */
+	read_type_t read;
+	write_type_t write;
+	open_type_t open;
+	close_type_t close;
+	readdir_type_t readdir;
+	finddir_type_t finddir;
+	struct fs_node* ptr;	/* Used by mountpoints and symlinks */
+} fs_node_t;
+
+/* The structure of a directory entry */
+struct dirent
+{
+	char name[128];
+	unsigned int inode;	/* The node ID */
+};
+
+/* Variable declaring the root filesystem */
+extern struct fs_node* fs_root;
+
+/* Functions called by the kernel to read and write to
+ * the filesystem */
+unsigned int read_fs(struct fs_node* node, unsigned int offset, unsigned int size, unsigned char* buffer);
+unsigned int write_fs(struct fs_node* node, unsigned int offset, unsigned int size, unsigned char* buffer);
+void open_fs(struct fs_node* node, unsigned char read, unsigned char write);
+void close_fs(struct fs_node* node);
+struct dirent* readdir_fs(struct fs_node* node, unsigned int index);
+struct fs_node* finddir_fs(struct fs_node* node, char* name);
+
+#define VMEM_START		0xC0000000
+#define VMEM_INITIAL_SIZE	0x100000
+#define VMEM_INDEX_SIZE		0x20000
+#define VMEM_MAGIC		0x123890AB
+#define VMEM_MIN_SIZE		0x70000
+
+/* Structure definitions */
+struct vmem_header
+{
+	unsigned int magic;	/* Magic number, used for error checking and identification */
+	unsigned char is_hole;	/* 1 if this is a hole, 0 if this is a block */
+	unsigned int size;	/* Size of the block, including this and the footer */
+};
+
+struct vmem_footer
+{
+	unsigned int magic;		/* Magic number, same as in header */
+	struct vmem_header* header;	/* Pointer to the block header */
+};
+
+typedef struct vmem_heap
+{
+	struct ordered_array index;	
+	addr start_address;		/* The start of our allocated space */
+	addr end_address;		/* The end of our allocated space.  May be expanded up to max_address */
+	addr max_address;		/* The maximum address the heap can be expanded to */
+	unsigned char supervisor;	/* Should extra pages requested by us be mapped as supervisor-only? */
+	unsigned char readonly;		/* Should extra pages requested by us be mapped as read-only? */
+} vmem_heap_t;
+
+/* VMEM.C */
+extern vmem_heap_t* create_heap(addr start, addr end, addr max, unsigned char supervisor, unsigned char readonly);
+extern void* vmalloc(addr size, unsigned char page_align, struct vmem_heap* heap);
+extern void vfree(void* p, struct vmem_heap* heap);
+
+
 #endif
diff --git a/irq.c b/irq.c
index b764b1eada6e3cab8e7b47f2e298d85b7062a29c..48901ebadb1b6b505b3e47418ae32a99ed96805f 100755
--- a/irq.c
+++ b/irq.c
@@ -1,8 +1,6 @@
-/* bkerndev - Bran's Kernel Development Tutorial
-*  作者:Brandon F. (friesenb@gmail.com)
+/* 
 *  描述:中断请求管理
-*
-*  注意:无明示或暗示的保证。使用风险自负。 */
+*/
 #include <system.h>
 
 /* 这些是我们自己的ISR,它们指向我们的特殊IRQ处理程序
@@ -112,4 +110,10 @@ void irq_handler(struct regs *r)
 
     /* 在任何情况下,我们都需要向主中断控制器发送一个EOI */
     outportb(0x20, 0x20);
+
+    /*在定时器中断事件中,我们可能正在进行任务切换,
+    *因此我们只能在向中断控制器发送EOI(中断结束)之后执行任务切换,
+    *因此我们在这里调用定时器中断的处理程序。*/
+   if (handler && r.int_no - 32 == 0)
+		handler(&r);
 }
diff --git a/isrs.c b/isrs.c
index 082882c6e6b89c51d9cab2a6349e3c8b40ec92ab..c60d97f8517b60abd2987e2d24402c51cc372b83 100755
--- a/isrs.c
+++ b/isrs.c
@@ -1,8 +1,6 @@
-/* bkerndev - Bran's Kernel Development Tutorial
-*  作者:Brandon F. (friesenb@gmail.com)
+/* 
 *  描述:中断服务程序安装程序和异常
-*
-*  注意:无明示或暗示的保证。使用风险自负。 */
+*/
 #include <system.h>
 
 /* 这些是所有异常处理程序的原型:IDT中的前32个条目由Intel保留,
@@ -39,9 +37,9 @@ extern void isr28();
 extern void isr29();
 extern void isr30();
 extern void isr31();
+extern void isr80();
 
-/* 这是一个非常重复的函数...它不难,只是烦人。正如你所看到的,
-*  我们将IDT中的前32个条目设置为前32个ISR。我们不能为此使用for循环,
+/* 我们将IDT中的前32个条目设置为前32个ISR。我们不能为此使用for循环,
 *  因为没有办法获取与给定条目对应的函数名。我们将访问标志设置为0x8E。
 *  这意味着条目存在,运行在环0(内核级别),并且将所需的'14'设置为
 *  低5位,这在十六进制中表示为'E'。 */
@@ -82,6 +80,7 @@ void isrs_install()
     idt_set_gate(29, (unsigned)isr29, 0x08, 0x8E);
     idt_set_gate(30, (unsigned)isr30, 0x08, 0x8E);
     idt_set_gate(31, (unsigned)isr31, 0x08, 0x8E);
+    idt_set_gate(0x80, (unsigned)isr80, 0x08, 0x8E | 0x60);
 }
 
 /* 这是一个简单的字符串数组。它包含每个异常对应的消息。
@@ -126,16 +125,45 @@ unsigned char *exception_messages[] =
     "Reserved"
 };
 
+/* 所有的外部定义的故障处理程序,我们不想让整个内核都能看到它们。
+ * 可以将这些放在头文件中 */
+extern void _page_fault(struct regs* r);
+
 /* 我们所有的异常处理中断服务程序都将指向这个函数。
 *  这将告诉我们发生了什么异常!现在,我们只是通过进入一个
 *  无尽循环来停止系统。所有ISR在服务时都会禁用中断,作为一种
 *  '锁定'机制,以防止IRQ发生并破坏内核数据结构 */
-void fault_handler(struct regs *r)
+void _fault_handler(struct regs r)
 {
-    if (r->int_no < 32)
-    {
-        puts(exception_messages[r->int_no]);
-        puts(" Exception. System Halted!\n");
-        for (;;);
-    }
+	/* 是否是我们要处理的故障? */
+	switch (r.int_no)
+	{
+		case 13:
+			return;
+		case 14:
+			/* 页错误;发送到page.c */
+			page_fault(&r);
+			return;
+		case 80:
+			/* 系统调用;发送到syscall.c */
+			syscall_handler(&r);
+			return;
+		default:
+			/* 不是我们要特别处理的故障,
+			 * 所以让它通过下面的if语句 */
+			break;
+	}
+
+	/* 是否是编号为0到31的故障? */
+	if (r.int_no < 32)
+	{
+		/* 显示发生的异常的描述。
+		 * 在本教程中,我们将简单地使用无限循环来停止系统。 */
+		putch('\n');
+		settextcolor(4, 0);
+		puts(exception_messages[r.int_no]);
+		puts(" 异常。\n系统已停止!\n\0");
+		for (;;);
+	}
 }
+
diff --git a/kb.c b/kb.c
index bbe59628fd358889972ddd44f278ca942e26d3e3..b425e0b4c9598548488589e884631709abd68453 100755
--- a/kb.c
+++ b/kb.c
@@ -1,16 +1,15 @@
-/* bkerndev - Bran's Kernel Development Tutorial
-*  作者:Brandon F. (friesenb@gmail.com)
-*  描述:键盘驱动程序
-*
-*  注意:无明示或暗示的保证。使用风险自负。 */
 #include <system.h>
 
-/* KBDUS 表示美国键盘布局。这是一个扫描码表
-*  用于布局标准美国键盘。我留下了一些注释
-*  给你一个关于什么键是什么的想法,即使我将它的数组索引设置为0。
-*  你可以使用宏将其更改为你想要的任何内容,如果你愿意! */
-unsigned char kbdus[128] =
-{
+/* 键盘修饰键定义 */
+#define KB_SHIFT 0x11
+#define KB_ALT 0x12
+#define KB_CTRL 0x13
+
+/* 键盘状态位,用于记录 Shift、Ctrl 和 Alt 键的状态 */
+unsigned short key_status = 0x0000;
+
+/* 美国键盘布局的扫描码表 */
+unsigned char us_keyboard_layout[128] = {
     0,  27, '1', '2', '3', '4', '5', '6', '7', '8',   /* 9 */
   '9', '0', '-', '=', '\b',   /* 退格键 */
   '\t',   /* 制表符 */
@@ -49,38 +48,50 @@ unsigned char kbdus[128] =
     0,  /* 所有其他键都是未定义的 */
 };
 
-/* 处理键盘中断 */
-void keyboard_handler(struct regs *r)
-{
+/**
+ * 键盘中断处理函数
+ * @param r 保存的寄存器上下文
+ */
+void keyboard_handler(struct regs* r) {
     unsigned char scancode;
 
-    /* 从键盘的数据缓冲区读取 */
+    // 从键盘数据端口读取扫描码
     scancode = inportb(0x60);
 
-    /* 如果我们从键盘读取的字节的最高位被设置,
-    *  那意味着一个键刚刚被释放 */
-    if (scancode & 0x80)
-    {
-        /* 你可以使用这个来查看用户是否释放了
-        *  shift,alt或control键... */
-    }
-    else
-    {
-        /* 这里,一个键刚刚被按下。请注意,如果你
-        *  按住一个键,你会得到重复的按键中断。
+    // 检查扫描码的最高位,判断是按键按下还是释放
+    if (scancode & 0x80) {
+        // 按键释放处理
+        unsigned char released_key = us_keyboard_layout[scancode & 0x7F]; // 去掉最高位得到对应按键
+        if (released_key == KB_SHIFT) {
+            key_status &= ~0x0100; // 清除 Shift 状态
+        } else if (released_key == KB_CTRL) {
+            key_status &= ~0x0010; // 清除 Ctrl 状态
+        } else if (released_key == KB_ALT) {
+            key_status &= ~0x0001; // 清除 Alt 状态
+        }
+    } else {
+        // 按键按下处理
+        unsigned char pressed_key = us_keyboard_layout[scancode];
+        if (pressed_key == KB_SHIFT) {
+            key_status |= 0x0100; // 设置 Shift 状态
+        } else if (pressed_key == KB_CTRL) {
+            key_status |= 0x0010; // 设置 Ctrl 状态
+        } else if (pressed_key == KB_ALT) {
+            key_status |= 0x0001; // 设置 Alt 状态
+        }
 
-        /* 为了向你展示这是如何工作的,我们简单地将
-        *  键盘扫描码转换为ASCII值,然后将其显示到屏幕上。
-        *  你可以发挥创意,使用一些标志来查看是否按下了shift,
-        *  并使用不同的布局,或者你可以向上述布局添加另一个128个条目
-        *  以对应于按住'shift'。如果使用较大的查找表按住shift,
-        *  你会在查找时将scancode增加128 */
-        putch(kbdus[scancode]);
+        // 检查是否按下 Shift 键,使用对应的字符表输出
+        if (key_status & 0x0100) { // 如果 Shift 被按下
+            putch(us_keyboard_layout[scancode + 128]); // 使用大写字符或特殊符号
+        } else {
+            putch(us_keyboard_layout[scancode]); // 输出普通字符
+        }
     }
 }
 
-/* 将键盘处理程序安装到IRQ1 */
-void keyboard_install()
-{
-    irq_install_handler(1, keyboard_handler);
+/**
+ * 安装键盘中断处理程序
+ */
+void keyboard_install() {
+    irq_install_handler(1, keyboard_handler); // 将键盘中断处理程序挂载到 IRQ1
 }
diff --git a/kmem.c b/kmem.c
new file mode 100644
index 0000000000000000000000000000000000000000..07925a114b27bf13ba0e2fb2db765b355f1bca25
--- /dev/null
+++ b/kmem.c
@@ -0,0 +1,122 @@
+#include <system.h>
+
+/* 外部变量,表示内核结束地址 */
+extern unsigned int end;
+
+/* 内核内存分配器的全局变量 */
+addr kmem_addr = (addr)&end;          /* 内核当前可用的地址 */
+addr kmem_memtotal = 0;               /* 系统总内存大小 */
+struct vmem_heap* kmem_heap = 0;      /* 内核堆的指针 */
+
+/**
+ * 内部内存分配函数
+ * @param size 要分配的内存大小
+ * @param align 是否需要对齐到页面边界
+ * @param phys 返回物理地址的指针(如果需要)
+ * @return 返回分配的虚拟地址
+ */
+static addr kmalloc_internal(addr size, int align, addr* phys) {
+    // 如果内核堆已初始化,使用堆分配
+    if (kmem_heap != 0) {
+        void* address = vmalloc(size, (unsigned char)align, kmem_heap);
+        if (phys != 0) {
+            struct page* page = (struct page*)get_page((addr)address, 0, kernel_directory);
+            *phys = page->frame * 0x1000 + ((addr)address & 0xFFF); // 计算物理地址
+        }
+        return (addr)address;
+    }
+
+    // 如果需要对齐,并且当前地址未对齐到页面边界
+    if (align == 1 && (kmem_addr & 0xFFFFF000)) {
+        kmem_addr &= 0xFFFFF000;   // 对齐到页面边界
+        kmem_addr += 0x1000;       // 移动到下一页
+    }
+
+    // 如果需要返回物理地址
+    if (phys) {
+        *phys = kmem_addr;
+    }
+
+    // 返回当前地址,并将地址向前移动分配的大小
+    addr allocated_addr = kmem_addr;
+    kmem_addr += size;
+    return allocated_addr;
+}
+
+/**
+ * 分配对齐的内存
+ * @param size 要分配的内存大小
+ * @return 返回分配的虚拟地址(对齐到页面边界)
+ */
+addr kmalloc_a(addr size) {
+    return kmalloc_internal(size, 1, 0);
+}
+
+/**
+ * 分配内存,并返回物理地址
+ * @param size 要分配的内存大小
+ * @param phys 返回物理地址的指针
+ * @return 返回分配的虚拟地址
+ */
+addr kmalloc_p(addr size, addr* phys) {
+    return kmalloc_internal(size, 0, phys);
+}
+
+/**
+ * 分配对齐的内存,并返回物理地址
+ * @param size 要分配的内存大小
+ * @param phys 返回物理地址的指针
+ * @return 返回分配的虚拟地址(对齐到页面边界)
+ */
+addr kmalloc_ap(addr size, addr* phys) {
+    return kmalloc_internal(size, 1, phys);
+}
+
+/**
+ * 分配未对齐的内存
+ * @param size 要分配的内存大小
+ * @return 返回分配的虚拟地址
+ */
+addr kmalloc(addr size) {
+    return kmalloc_internal(size, 0, 0);
+}
+
+/**
+ * 释放内存
+ * @param pos 要释放的内存地址
+ */
+void kfree(addr pos) {
+    if (kmem_heap != 0) {
+        vfree((void*)pos, kmem_heap);
+    }
+}
+
+/**
+ * 获取系统的总内存大小
+ * @return 系统的总内存大小(字节)
+ */
+addr kmem_total() {
+    return kmem_memtotal;
+}
+
+/**
+ * 安装内存管理器并计算系统总内存
+ * @param mbt Multiboot 信息结构指针
+ */
+void kmem_install(struct multiboot_info* mbt) {
+    struct multiboot_memory_map* mmap = (struct multiboot_memory_map*)mbt->mmap_addr;
+
+    // 遍历 Multiboot 提供的内存映射表,计算系统总内存大小
+    while ((addr)mmap < mbt->mmap_addr + mbt->mmap_length) {
+        addr region_end = (addr)mmap->base_addr + (addr)mmap->length;
+        if (region_end > kmem_memtotal) {
+            kmem_memtotal = region_end; // 更新总内存大小
+        }
+
+        // 移动到下一个内存映射表项
+        mmap = (struct multiboot_memory_map*)((addr)mmap + mmap->size + sizeof(addr));
+    }
+
+    // 调整内核地址,以适应 Multiboot 模块信息
+    kmem_addr += mbt->mods_count * sizeof(unsigned int);
+}
diff --git a/main.c b/main.c
index 8548a5b44c58af160e1975c23b1027ef2d171317..e05d1198ace829de049ca8b1334117e55b066bb6 100755
--- a/main.c
+++ b/main.c
@@ -1,8 +1,4 @@
-/* bkerndev - Bran's Kernel Development Tutorial
-*  By:   Brandon F. (friesenb@gmail.com)
-*  Desc: Main.c: C code entry.
-*
-*  Notes: No warranty expressed or implied. Use at own risk. */
+
 #include <system.h>
 
 
@@ -54,24 +50,106 @@ void outportb (unsigned short _port, unsigned char _data)
 }
 
 
-void main()
+
+/* 这是一个非常简单的main()函数。它所做的就是坐在一个无限循环中。
+ * 这将是我们的'idle'循环 */
+void _main(struct multiboot_info* mbt, addr stack)
 {
-    int i;
+	/* 将堆栈位置存储在全局变量中 */
+	initial_esp = stack;
+
+	/* 设置内核/ CPU 操作的核心组件 */
+	unsigned char itoa_buffer[256];
+	gdt_install();
+	idt_install();
+	isrs_install();
+	irq_install();
+	init_video();
+
+	/* 启用IRQs */
+	asm volatile("sti");
+
+	/* 在内存管理和任务管理之前安装initrd文件系统,
+	 * 以防止我们在读取它之前覆盖它 */
+	ASSERT(mbt->mods_count > 0);
+	puts("初始化initrd... ");
+	fs_root = initrd_install(*((addr*)(mbt->mods_addr)));
+	puts("完成。\n");
+
+	/* 安装内存和任务管理 */
+	kmem_install(mbt);
+	page_install();
+	task_install();
+
+	/* 安装并处理系统中的各种设备 */
+	puts("启用设备... ");
+	timer_install();
+	kb_install();
+	puts("完成。\n");
+
+	/* 测试用户模式 */
+	puts("进入用户模式... \n");
+	int a;
+	for (a = 0; a < 16; a += 1)
+		puts("=====");
+	puts("\n");
+	tss_switch();
+	entry();
 
-    gdt_install();
-    idt_install();
-    isrs_install();
-    irq_install();
-    init_video();
-    timer_install();
-    keyboard_install();
+	for (;;);
 
-    __asm__ __volatile__ ("sti");
+	/* 测试任务管理系统 */
+	puts("派生内核...\n");
+	int ret = fork();
+	puts("fork() 返回 ");
+	puts(itoa(ret, itoa_buffer, 10));
+	puts(", 并且 getpid() 返回 ");
+	puts(itoa(getpid(), itoa_buffer, 10));
+	puts("\n==========================================\n");
 
-    puts("Hello World!\n");
+	/* 下面的代码部分不是可重入的(因为initrd VFS使用全局变量,
+	 * 这些变量将在两个进程之间共享),所以确保在列出/的
+	 * 内容时没有中断 */
+	asm volatile("cli");
 
-//    i = 10 / 0;
-//    putch(i);
+	/* 列出initrd的内容 */
+	int i = 0;
+	struct dirent* node = 0;
+	while ((node = readdir_fs(fs_root, i)) != 0)
+	{
+		puts("找到文件 ");
+		puts(node->name);
+		struct fs_node* fsnode = finddir_fs(fs_root, node->name);
 
-    for (;;);
+		if ((fsnode->flags & 0x7) == FS_DIRECTORY)
+			puts("\n\t(目录)\n");
+		else
+		{
+			puts("\n\t内容: \"");
+			char buf[256];
+			unsigned int sz = read_fs(fsnode, 0, 256, buf);
+			int j;
+			for (j = 0; j < sz; j++)
+				putch(buf[j]);
+			puts("\"\n");
+		}
+		i++;
+	}
+	puts("\n");
+
+	/* 重新启用中断 */
+	asm volatile("sti");
+
+	ret = fork();
+	puts("这条消息应该被重复4次!\n");
+	
+	/* ...并且留下这个循环。在'start.asm'中也有一个无限循环,
+	 * 如果你意外地删除了下一行 */
+	for (;;);
 }
+
+
+
+
+
+
diff --git a/page.c b/page.c
new file mode 100644
index 0000000000000000000000000000000000000000..ff2fe0ce0ad33d550f8332b8de363c2537617c0b
--- /dev/null
+++ b/page.c
@@ -0,0 +1,210 @@
+#include <system.h>
+
+/* 当前正在使用的页面目录 */
+struct page_directory* current_directory = 0;
+
+/* 内核的页面目录 */
+struct page_directory* kernel_directory = 0;
+
+/* 外部定义,用于访问内核内存和虚拟内存的相关全局变量 */
+extern addr kmem_addr;
+extern struct vmem_heap* kmem_heap;
+extern unsigned int* frames;
+extern unsigned int nframes;
+extern unsigned int end;
+
+/**
+ * 获取指定的页面
+ * @param address 虚拟地址
+ * @param make 是否需要创建新的页面表(1 表示需要)
+ * @param dir 页面目录
+ * @return 返回对应页面的指针,如果失败返回 0
+ */
+page_t* get_page(addr address, int make, struct page_directory* dir) {
+    // 将地址转换为页面索引
+    address /= 0x1000;
+
+    // 计算页面表的索引
+    unsigned int table_index = address / 1024;
+
+    // 检查该页面表是否已存在
+    if (dir->tables[table_index]) {
+        // 返回页面指针
+        return &dir->tables[table_index]->pages[address % 1024];
+    } else if (make) {
+        // 如果页面表不存在且需要创建,分配一个新的页面表
+        unsigned int temp_phys_addr;
+        dir->tables[table_index] = (struct page_table*)kmalloc_ap(sizeof(struct page_table), &temp_phys_addr);
+        memset(dir->tables[table_index], 0, sizeof(struct page_table));
+
+        // 设置页面表的物理地址
+        dir->tables_phys[table_index] = temp_phys_addr | 0x7; // 设置为 present、rw 和用户模式
+        return &dir->tables[table_index]->pages[address % 1024];
+    } else {
+        // 页面表不存在,且不需要创建
+        return 0;
+    }
+}
+
+/**
+ * 切换到指定的页面目录
+ * @param dir 要切换的页面目录
+ */
+void page_switch(struct page_directory* dir) {
+    current_directory = dir;
+
+    // 加载新的页面目录地址到 CR3
+    asm volatile("mov %0, %%cr3" :: "r"(dir->phys_addr));
+
+    // 启用分页(设置 CR0 的最高位)
+    unsigned int cr0;
+    asm volatile("mov %%cr0, %0" : "=r"(cr0));
+    cr0 |= 0x80000000; // 设置分页标志位
+    asm volatile("mov %0, %%cr0" :: "r"(cr0));
+}
+
+/* 外部定义,用于物理复制页面 */
+extern void copy_page_physical(addr src, addr dest);
+
+/**
+ * 克隆一个页面表
+ * @param src 源页面表
+ * @param phys_addr 新页面表的物理地址
+ * @return 返回克隆的页面表
+ */
+struct page_table* clone_table(struct page_table* src, addr* phys_addr) {
+    // 分配一个新的页面表
+    struct page_table* new_table = (struct page_table*)kmalloc_ap(sizeof(struct page_table), phys_addr);
+    memset(new_table, 0, sizeof(struct page_table));
+
+    // 遍历源页面表中的每个页面
+    for (int i = 0; i < 1024; i++) {
+        if (!src->pages[i].frame)
+            continue; // 如果页面没有物理帧,跳过
+
+        // 为新页面分配一个物理帧
+        frame_alloc(&new_table->pages[i], 0, 0);
+
+        // 复制页面的标志位
+        new_table->pages[i].present = src->pages[i].present;
+        new_table->pages[i].rw = src->pages[i].rw;
+        new_table->pages[i].user = src->pages[i].user;
+        new_table->pages[i].accessed = src->pages[i].accessed;
+        new_table->pages[i].dirty = src->pages[i].dirty;
+
+        // 物理复制页面内容
+        copy_page_physical(src->pages[i].frame * 0x1000, new_table->pages[i].frame * 0x1000);
+    }
+
+    return new_table;
+}
+
+/**
+ * 克隆页面目录
+ * @param src 源页面目录
+ * @return 返回克隆的页面目录
+ */
+struct page_directory* clone_directory(struct page_directory* src) {
+    addr phys;
+    struct page_directory* new_dir = (struct page_directory*)kmalloc_ap(sizeof(struct page_directory), &phys);
+    memset(new_dir, 0, sizeof(struct page_directory));
+
+    // 计算物理地址的偏移量
+    addr offset = (addr)new_dir->tables_phys - (addr)new_dir;
+    new_dir->phys_addr = phys + offset;
+
+    // 遍历源页面目录中的每个页面表
+    for (int i = 0; i < 1024; i++) {
+        if (!src->tables[i])
+            continue;
+
+        if (kernel_directory->tables[i] == src->tables[i]) {
+            // 如果是内核页面表,直接使用相同的指针
+            new_dir->tables[i] = src->tables[i];
+            new_dir->tables_phys[i] = src->tables_phys[i];
+        } else {
+            // 否则,克隆页面表
+            addr new_phys;
+            new_dir->tables[i] = clone_table(src->tables[i], &new_phys);
+            new_dir->tables_phys[i] = new_phys | 0x07; // 设置为 present、rw 和用户模式
+        }
+    }
+
+    return new_dir;
+}
+
+/**
+ * 页面故障处理函数
+ * @param r 保存寄存器的上下文
+ */
+void _page_fault(struct regs* r) {
+    addr fault_addr;
+    asm volatile("mov %%cr2, %0" : "=r"(fault_addr)); // 获取引发页面错误的地址
+
+    unsigned char buffer[256];
+
+    // 解析错误代码
+    int present = !(r->err_code & 0x1); // 页面不存在
+    int rw = r->err_code & 0x2;         // 写操作
+    int user = r->err_code & 0x4;       // 用户模式访问
+    int reserved = r->err_code & 0x8;   // CPU 保留位被覆盖
+    int id = r->err_code & 0x10;        // 由指令引起
+
+    // 打印错误信息
+    settextcolor(4, 0);
+    puts("\n页面错误 ( ");
+    if (present) puts("不存在 ");
+    if (rw) puts("写操作 ");
+    if (user) puts("用户模式 ");
+    if (reserved) puts("保留 ");
+    puts(") 在地址 0x");
+    puts(itoa(fault_addr, buffer, 16));
+    puts(".\n系统已停止!\n");
+
+    for (;;); // 死循环,停止系统
+}
+
+/**
+ * 安装分页系统
+ * @param upper 系统支持的最大内存地址
+ */
+void page_install(addr upper) {
+    unsigned char buffer[256];
+    addr memory_end = kmem_total();
+
+    puts("初始化物理帧... ");
+    nframes = memory_end / 0x1000; // 计算帧的数量
+    frames = (unsigned int*)kmalloc(INDEX_FROM_BIT(nframes));
+    memset(frames, 0, INDEX_FROM_BIT(nframes));
+    puts("完成 (");
+    puts(itoa(nframes, buffer, 10));
+    puts(" 帧).\n");
+
+    // 初始化内核页面目录
+    puts("初始化页面目录... ");
+    addr phys;
+    kernel_directory = (struct page_directory*)kmalloc_a(sizeof(struct page_directory));
+    memset(kernel_directory, 0, sizeof(struct page_directory));
+    kernel_directory->phys_addr = (addr)kernel_directory->tables_phys;
+    puts("完成。\n");
+
+    // 设置分页的内核部分
+    for (addr i = 0; i < kmem_addr + 0x1000; i += 0x1000) {
+        frame_alloc(get_page(i, 1, kernel_directory), 0, 0);
+    }
+
+    puts("启用分页... ");
+    page_switch(kernel_directory);
+    puts("完成。\n");
+
+    // 初始化内核堆
+    puts("初始化内核虚拟内存堆... ");
+    kmem_heap = create_heap(VMEM_START, VMEM_START + VMEM_INITIAL_SIZE, 0xCFFFF000, 0, 0);
+    puts("完成。\n");
+
+    // 克隆内核页面目录
+    puts("克隆内核页面目录... ");
+    current_directory = clone_directory(kernel_directory);
+    page_switch(current_directory);
+    puts("分页系统安装完成。\n");
+}
diff --git a/scrn.c b/scrn.c
index 8a37bd4352ec774f101948a38903cff357742595..ad849e7ab4600b69b3e39ca3bbb44fecc331971a 100755
--- a/scrn.c
+++ b/scrn.c
@@ -1,8 +1,6 @@
-/* bkerndev - Bran's Kernel Development Tutorial
-*  作者:Brandon F. (friesenb@gmail.com)
+/*
 *  描述:控制台输入/输出的屏幕输出函数
-*
-*  注意:无明示或暗示的保证。使用风险自负。 */
+*/
 #include <system.h>
 
 /* 这些定义了我们的文本指针,背景和前景颜色(属性),以及 x 和 y 光标坐标 */
diff --git a/start.asm b/start.asm
index f17cc8fa68b619023ca3de50f70839b11d975fe0..d85bd81f4fe43f280a19556c8d81645182efcee9 100755
--- a/start.asm
+++ b/start.asm
@@ -1,10 +1,6 @@
 
-; bkerndev - Bran's Kernel Development Tutorial
-; 作者:Brandon F. (friesenb@gmail.com)
 ; 描述:内核入口点,堆栈,以及中断服务程序。
 ;
-; 注意:无明示或暗示的保证。使用风险自负。
-;
 ; 这是内核的入口点。我们可以在这里调用main,
 ; 或者我们可以使用它来设置堆栈或其他一些好的东西,
 ; 比如设置GDT和段。请注意,此时中断是禁用的:关于中断的更多信息稍后讨论!
@@ -105,6 +101,7 @@ global isr28
 global isr29
 global isr30
 global isr31
+global isr80
 
 ;  0: 除以零异常_isr0:
 isr0:
@@ -324,6 +321,13 @@ isr31:
     push byte 31
     jmp isr_common_stub
 
+; 80: 系统调用
+isr80:
+	cli
+	push byte 0
+	push byte 80
+	jmp isr_common_stub
+
 
 ; 我们在这里调用一个C函数。我们需要让汇编器知道
 ; '_fault_handler'存在于另一个文件中
@@ -332,28 +336,32 @@ extern fault_handler
 ; 这是我们的通用ISR存根。它保存处理器状态,设置
 ; 内核模式段,调用C级故障处理程序,最后恢复堆栈帧。isr_common_stub:
 isr_common_stub:
-    pusha
-    push ds
-    push es
-    push fs
-    push gs
-    mov ax, 0x10
-    mov ds, ax
-    mov es, ax
-    mov fs, ax
-    mov gs, ax
-    mov eax, esp
-    push eax
-    mov eax, fault_handler
-    call eax
-    pop eax
-    pop gs
-    pop fs
-    pop es
-    pop ds
-    popa
-    add esp, 8
-    iret
+	pusha		; 将edi, esi, ebp, esp, ebx, edx, ecx, eax压入栈中
+
+	mov ax, ds	; 将ds寄存器的低16位存入eax
+	push eax	; 保存数据段描述符
+
+	mov ax, 0x10	; 加载内核数据段描述符
+	mov ds, ax
+	mov es, ax
+	mov fs, ax
+	mov gs, ax
+
+	call fault_handler
+
+	pop eax		; 恢复原始的数据段描述符
+	mov ds, ax
+        mov es, ax
+        mov fs, ax
+        mov gs, ax
+
+	popa		; 弹出edi, esi, ebp...
+	add esp, 8	; 清理压入的错误代码和ISR编号
+			; 的栈空间
+
+	sti
+	iret		; 一次性弹出5个东西:CS, EIP, EFLAGS, SS和ESP!
+
 
 global irq0
 global irq1
@@ -487,31 +495,32 @@ irq15:
 extern irq_handler
 
 irq_common_stub:
-    pusha
-    push ds
-    push es
-    push fs
-    push gs
+        pusha           ; 将edi, esi, ebp, esp, ebx, edx, ecx, eax压入栈中
+
+        mov ax, ds      ; 将ds寄存器的低16位存入eax
+        push eax        ; 保存数据段描述符
+
+        mov ax, 0x10    ; 加载内核数据段描述符
+        mov ds, ax
+        mov es, ax
+        mov fs, ax
+        mov gs, ax
+
+        call irq_handler
+
+        pop eax         ; 恢复原始的数据段描述符
+        mov ds, ax
+        mov es, ax
+        mov fs, ax
+        mov gs, ax
+
+        popa            ; 弹出edi, esi, ebp...
+        add esp, 8      ; 清理压入的错误代码和ISR编号
+                        ; 的栈空间
+
+        sti
+        iret            ; 一次性弹出5个东西:CS, EIP, EFLAGS, SS和ESP!
 
-    mov ax, 0x10
-    mov ds, ax
-    mov es, ax
-    mov fs, ax
-    mov gs, ax
-    mov eax, esp
-
-    push eax
-    mov eax, irq_handler
-    call eax
-    pop eax
-
-    pop gs
-    pop fs
-    pop es
-    pop ds
-    popa
-    add esp, 8
-    iret
 
 ; 这是我们的BSS段定义。目前,我们只使用它来存储堆栈。
 ; 记住,堆栈实际上是向下增长的,所以我们先声明数据大小,
diff --git a/string.c b/string.c
new file mode 100644
index 0000000000000000000000000000000000000000..b86cfedde3c5f38afd4a68cf1e2bc100140839c7
--- /dev/null
+++ b/string.c
@@ -0,0 +1,113 @@
+#include <system.h>
+
+/**
+ * 计算字符串长度
+ * @param str 指向字符串的指针
+ * @return 返回字符串的长度(不包括结尾的 '\0')
+ */
+int strlen(const unsigned char* str) {
+    // 遍历字符串直到遇到 '\0',返回字符个数
+    const unsigned char* current_char;
+    for (current_char = str; *current_char; ++current_char);
+    return (current_char - str);
+}
+
+/**
+ * 复制字符串
+ * @param dest 目标地址
+ * @param src 源地址
+ * @param size 要复制的字节数
+ * @return 返回 0 表示成功
+ */
+int strcpy(unsigned char* dest, const unsigned char* src, unsigned int size) {
+    // 使用内存复制函数 memcpy 实现字符串复制
+    memcpy(dest, src, size);
+    return 0; // 返回 0 表示成功
+}
+
+/**
+ * 比较两个字符串
+ * @param a 第一个字符串
+ * @param b 第二个字符串
+ * @return 如果 a > b 返回 1,如果 a < b 返回 -1,如果相等返回 0
+ */
+int strcmp(const unsigned char* a, const unsigned char* b) {
+    unsigned int len_a = strlen(a);
+    unsigned int len_b = strlen(b);
+
+    // 比较字符串长度
+    if (len_a > len_b) return 1;
+    if (len_a < len_b) return -1;
+
+    // 按字符逐一比较
+    for (unsigned int i = 0; i < len_a; i++) {
+        if (a[i] != b[i]) {
+            return (a[i] > b[i]) ? 1 : -1;
+        }
+    }
+
+    // 两个字符串相等
+    return 0;
+}
+
+/**
+ * 反转字符串
+ * @param str 要反转的字符串
+ * @return 返回反转后的字符串
+ */
+unsigned char* strrev(unsigned char* str) {
+    if (!str || !*str) {
+        // 如果字符串为空或长度为 0,直接返回
+        return str;
+    }
+
+    unsigned char* start = str;                // 指向字符串开头
+    unsigned char* end = str + strlen(str) - 1; // 指向字符串末尾
+
+    // 交换首尾字符,直到两个指针相遇
+    while (end > start) {
+        unsigned char temp = *start;
+        *start = *end;
+        *end = temp;
+        start++;
+        end--;
+    }
+
+    return str;
+}
+
+/**
+ * 整数转换为字符串
+ * @param num 要转换的整数
+ * @param str 用于存储转换结果的字符串缓冲区
+ * @param base 转换的进制(例如 10 表示十进制,16 表示十六进制)
+ * @return 返回转换后的字符串
+ */
+unsigned char* itoa(unsigned long num, unsigned char* str, int base) {
+    static unsigned char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; // 支持的数字和字母
+    unsigned long index = 0;
+    int is_negative = 0; // 记录是否是负数(仅适用于十进制)
+
+    // 如果是负数且是十进制,记录符号并取绝对值
+    if ((int)num < 0 && base == 10) {
+        is_negative = 1;
+        num = -num;
+    }
+
+    // 按位计算每一位对应的字符
+    do {
+        str[index++] = digits[num % base];
+        num /= base;
+    } while (num > 0);
+
+    // 如果是负数,添加负号
+    if (is_negative) {
+        str[index++] = '-';
+    }
+
+    // 添加字符串结束符
+    str[index] = '\0';
+
+    // 反转字符串,返回结果
+    return strrev(str);
+}
diff --git a/syscall.c b/syscall.c
new file mode 100644
index 0000000000000000000000000000000000000000..f831103a9ee78c76c833e9236f7ed24f26c3a43f
--- /dev/null
+++ b/syscall.c
@@ -0,0 +1,44 @@
+
+#include <system.h>
+
+/* 定义可调用的内核函数 */
+static void *syscalls[1] =
+{
+	&puts
+};
+unsigned int num_syscalls = 1;
+
+/* 处理用户空间代码发出的系统调用 */
+void _syscall_handler(struct regs* r)
+{
+	/* 检查系统调用号是否有效 */
+	if (r->eax > num_syscalls)
+		return;
+
+	/* 获取所需的系统调用位置 */
+	void* location = syscalls[r->eax];
+
+	/* 我们不知道函数需要多少个参数,
+	 * 所以我们只是按照正确的顺序将它们
+	 * 都压入栈中。函数将使用它需要的所有
+	 * 参数,然后我们可以将它们全部弹出 */
+	int ret;
+	asm volatile (" \
+push %1; \
+push %2; \
+push %3; \
+push %4; \
+push %5; \
+call *%6; \
+pop %%ebx; \
+pop %%ebx; \
+pop %%ebx; \
+pop %%ebx; \
+pop %%ebx; \
+" : "=a" (ret) : "r" (r->edi), "r" (r->esi), "r" (r->edx), "r" (r->ecx), "r" (r->ebx), "r" (location));
+	r->eax = ret;
+}
+
+
+
+
diff --git a/system.c b/system.c
new file mode 100644
index 0000000000000000000000000000000000000000..1da3fd0d875fa9c32bee8d876ac2ac234b900e74
--- /dev/null
+++ b/system.c
@@ -0,0 +1,12 @@
+#include <system.h>
+
+
+void panic(unsigned char* msg)
+{
+	putch('\n');
+	settextcolor(4, 0);
+	puts(msg);
+	putch('\n');
+	for(;;);
+}
+
diff --git a/task.c b/task.c
new file mode 100644
index 0000000000000000000000000000000000000000..179ef719f4c9d738b0f5014bc62ad4c9b0455a7e
--- /dev/null
+++ b/task.c
@@ -0,0 +1,198 @@
+#include <system.h>
+
+/* 当前运行的任务 */
+volatile struct task* current_task;
+
+/* 任务链表的起始节点 */
+volatile struct task* task_queue;
+
+/* 引入外部定义以访问 page.c / process.asm 的成员 */
+extern addr initial_stack_ptr;
+extern addr read_eip();
+
+/* 下一个可用的进程 ID */
+unsigned int process_id = 1;
+
+/**
+ * 将当前堆栈移动到新的内存位置
+ * @param new_stack_start 新堆栈的起始地址
+ * @param size 堆栈的大小
+ */
+void move_stack(void* new_stack_start, addr size) {
+    addr addr_iter;
+
+    // 为新堆栈分配内存(从新堆栈顶部开始向下分配)
+    for (addr_iter = (addr)new_stack_start; addr_iter >= ((addr)new_stack_start - size); addr_iter -= 0x1000) {
+        frame_alloc((struct page*)get_page(addr_iter, 1, current_directory), 0 /* 用户模式 */, 1 /* 可写 */);
+    }
+
+    // 刷新 TLB(通过重新加载页面目录地址)
+    addr page_dir_address;
+    asm volatile("mov %%cr3, %0" : "=r"(page_dir_address));
+    asm volatile("mov %0, %%cr3" : : "r"(page_dir_address));
+
+    // 保存旧的堆栈指针和基指针
+    addr old_esp, old_ebp;
+    asm volatile("mov %%esp, %0" : "=r"(old_esp));
+    asm volatile("mov %%ebp, %0" : "=r"(old_ebp));
+
+    // 计算堆栈偏移量
+    addr stack_offset = (addr)new_stack_start - initial_stack_ptr;
+    addr new_esp = old_esp + stack_offset;
+    addr new_ebp = old_ebp + stack_offset;
+
+    // 复制旧堆栈内容到新位置
+    memcpy((void*)new_esp, (void*)old_esp, initial_stack_ptr - old_esp);
+
+    // 修复新堆栈中的指针(如基指针)
+    for (addr_iter = (addr)new_stack_start; addr_iter > (addr)new_stack_start - size; addr_iter -= 4) {
+        addr temp_value = *(addr*)addr_iter;
+        if ((old_esp < temp_value) && (temp_value < initial_stack_ptr)) {
+            temp_value += stack_offset;
+            *(addr*)addr_iter = temp_value;
+        }
+    }
+
+    // 切换到新的堆栈指针和基指针
+    asm volatile("mov %0, %%esp" : : "r"(new_esp));
+    asm volatile("mov %0, %%ebp" : : "r"(new_ebp));
+}
+
+/**
+ * 分叉当前进程
+ * @return 返回子进程的 PID(在父进程中),在子进程中返回 0
+ */
+int fork() {
+    // 禁用中断以确保进程分叉的安全性
+    asm volatile("cli");
+
+    // 获取当前任务的引用
+    struct task* parent_task = (struct task*)current_task;
+
+    // 克隆当前进程的地址空间
+    struct page_directory* new_page_dir = (struct page_directory*)clone_directory(current_directory);
+
+    // 创建新任务(子进程)
+    struct task* child_task = (struct task*)kmalloc(sizeof(struct task));
+    child_task->id = process_id++;
+    child_task->esp = child_task->ebp = 0;
+    child_task->eip = 0;
+    child_task->page_directory = new_page_dir;
+    child_task->kernel_stack = kmalloc_a(KERNEL_STACK_SIZE);
+    child_task->next = 0;
+
+    // 将新任务添加到任务队列末尾
+    struct task* queue_iter = (struct task*)task_queue;
+    while (queue_iter->next)
+        queue_iter = queue_iter->next;
+    queue_iter->next = child_task;
+
+    // 获取当前的 EIP(执行指令指针)
+    addr instruction_ptr = read_eip();
+
+    // 如果当前任务是父进程,设置子任务的寄存器状态
+    if (current_task == parent_task) {
+        addr current_esp, current_ebp;
+        asm volatile("mov %%esp, %0" : "=r"(current_esp));
+        asm volatile("mov %%ebp, %0" : "=r"(current_ebp));
+
+        child_task->esp = current_esp;
+        child_task->ebp = current_ebp;
+        child_task->eip = instruction_ptr;
+
+        // 重新启用中断
+        asm volatile("sti");
+
+        // 返回子进程的 PID
+        return child_task->id;
+    } else {
+        // 子进程的返回值:0
+        return 0;
+    }
+}
+
+/**
+ * 获取当前进程的 PID
+ * @return 当前任务的 PID
+ */
+int getpid() {
+    return current_task->id;
+}
+
+/**
+ * 在任务之间切换;由定时器中断自动调用
+ */
+void switch_task() {
+    // 如果当前任务未初始化,则直接返回
+    if (!current_task)
+        return;
+
+    // 保存当前任务的状态
+    addr current_esp, current_ebp, current_eip;
+    asm volatile("mov %%esp, %0" : "=r"(current_esp));
+    asm volatile("mov %%ebp, %0" : "=r"(current_ebp));
+
+    current_eip = read_eip();
+
+    // 检查是否发生了任务切换
+    if (current_eip == 0x12345)
+        return;
+
+    current_task->eip = current_eip;
+    current_task->esp = current_esp;
+    current_task->ebp = current_ebp;
+
+    // 切换到下一个任务
+    current_task = current_task->next;
+    if (!current_task)
+        current_task = task_queue;
+
+    // 恢复新任务的状态
+    current_eip = current_task->eip;
+    current_esp = current_task->esp;
+    current_ebp = current_task->ebp;
+    current_directory = current_task->page_directory;
+
+    // 设置新任务的内核堆栈
+    tss_set_kernel_stack(current_task->kernel_stack + KERNEL_STACK_SIZE);
+
+    // 执行任务切换(修改寄存器和段寄存器)
+    asm volatile(
+        "cli; "
+        "mov %0, %%ecx; "
+        "mov %1, %%esp; "
+        "mov %2, %%ebp; "
+        "mov %3, %%cr3; "
+        "mov $0x12345, %%eax; "
+        "sti; "
+        "jmp *%%ecx"
+        :
+        : "r"(current_eip), "r"(current_esp), "r"(current_ebp), "r"(current_directory->phys_addr));
+}
+
+/**
+ * 初始化任务管理器并启用多任务
+ */
+void task_install() {
+    puts("启用多任务... ");
+
+    // 禁用中断
+    asm volatile("cli");
+
+    // 将堆栈重新定位到指定位置
+    move_stack((void*)0xE0000000, 0x2000);
+
+    // 初始化第一个任务(内核任务)
+    current_task = task_queue = (struct task*)kmalloc(sizeof(struct task));
+    current_task->id = process_id++;
+    current_task->esp = current_task->ebp = 0;
+    current_task->eip = 0;
+    current_task->page_directory = current_directory;
+    current_task->next = 0;
+    current_task->kernel_stack = kmalloc_a(KERNEL_STACK_SIZE);
+
+    // 重新启用中断
+    asm volatile("sti");
+
+    puts("完成。\n");
+}
diff --git a/timer.c b/timer.c
index e42f4361ccf9e8fb100ed61b3b7420af49db017c..7a134a44ddd308a75a8ba409b27fc59ceb7cf5f2 100755
--- a/timer.c
+++ b/timer.c
@@ -1,8 +1,6 @@
-/* bkerndev - Bran's Kernel Development Tutorial
-*  作者:Brandon F. (friesenb@gmail.com)
+/* 
 *  描述:定时器驱动程序
-*
-*  注意:无明示或暗示的保证。使用风险自负。 */
+*/
 #include <system.h>
 
 /* 这将跟踪系统运行了多少滴答(ticks) */
@@ -11,30 +9,39 @@ int timer_ticks = 0;
 /* 处理定时器。在这种情况下,它非常简单:每次定时器触发时,
 *  我们都会增加 'timer_ticks' 变量。默认情况下,定时器每秒触发
 *  18.222次。为什么是18.222Hz?IBM的某个工程师一定抽了些奇怪的东西 */
-void timer_handler(struct regs *r)
+void timer_handler(struct regs* r)
 {
-    /* 增加我们的 '滴答计数' */
-    timer_ticks++;
-
-    /* 每18个滴答(大约1秒),我们将在屏幕上显示一条消息 */
-    if (timer_ticks % 18 == 0)
-    {
-        puts("One second has passed\n");
-    }
+	/* 增加我们的'tick count' */
+	timer_ticks++;
+
+	/* 指示任务管理系统可能切换任务 */
+	switch_task();
 }
 
-/* 这将持续循环,直到达到给定的时间 */
+/* 设置硬件定时器的相位 */
+void timer_phase(int hz)
+{
+	int divisor = 1193180 / hz;	/* 计算我们的除数 */
+	outportb(0x43, 0x36);		/* 设置我们的命令字节为0x36 */
+	outportb(0x40, divisor & 0xFF);	/* 设置除数的低字节 */
+	outportb(0x40, divisor >> 8);	/* 设置除数的高字节 */
+}
+
+/* 这将不断循环,直到达到给定的时间 */
 void timer_wait(int ticks)
 {
-    unsigned long eticks;
+	unsigned long eticks;
 
-    eticks = timer_ticks + ticks;
-    while(timer_ticks < eticks);
+	eticks = timer_ticks + ticks;
+	while (timer_ticks < eticks);
 }
 
 /* 通过将定时器处理程序安装到IRQ0来设置系统时钟 */
 void timer_install()
 {
-    /* 将 'timer_handler' 安装到IRQ0 */
-    irq_install_handler(0, timer_handler);
+	/* 设置相位,使定时器每100ms触发一次 */
+	timer_phase(100);
+
+	/* 将'timer_handler'安装到IRQ0 */
+	irq_install_handler(0, timer_handler);
 }
diff --git a/tss.c b/tss.c
new file mode 100644
index 0000000000000000000000000000000000000000..c9700d236d1f6e04026467c0fa8b9469f647e8b6
--- /dev/null
+++ b/tss.c
@@ -0,0 +1,93 @@
+#include <system.h>
+
+/* 定义全局 TSS 结构 */
+tss_t sys_tss;
+
+/* 外部汇编函数,用于刷新 GDT 中的 TSS 位置 */
+extern void _tss_flush();
+
+/**
+ * 设置内核堆栈
+ * @param stack 内核堆栈指针地址
+ * 
+ * 将内核堆栈地址写入 TSS 的 esp0 字段,这是在发生用户模式到内核模式切换时
+ * 使用的堆栈。
+ */
+void tss_set_kernel_stack(unsigned int stack) {
+    sys_tss.esp0 = stack;
+}
+
+/**
+ * 在 GDT 中安装 TSS
+ * @param num GDT 中的 TSS 描述符索引
+ * @param ss0 内核数据段选择子
+ * @param esp0 内核堆栈指针地址
+ * 
+ * 将 TSS 的描述符添加到 GDT 中,初始化 TSS 结构,并设置默认的内核段和堆栈。
+ */
+void tss_install(signed int num, unsigned short ss0, unsigned short esp0) {
+    // 计算 TSS 的基地址和大小
+    addr base = (addr)&sys_tss;
+    addr size = base + sizeof(tss_t);
+
+    // 将 TSS 描述符添加到 GDT 中
+    gdt_set_gate(num, base, size, 0xE9, 0x00); // 0xE9 表示 TSS 的类型和权限
+
+    // 确保 TSS 结构初始化为 0
+    memset(&sys_tss, 0, sizeof(sys_tss));
+
+    // 设置内核堆栈段选择子和堆栈指针
+    sys_tss.ss0 = ss0;
+    sys_tss.esp0 = esp0;
+
+    /* 
+     * 设置 TSS 中的段选择子:
+     * cs: 内核代码段 (RPL = 3)
+     * ss, ds, es, fs, gs: 内核数据段 (RPL = 3)
+     * RPL (请求特权级别) 为 3 表示用户模式可以访问这些段。
+     */
+    sys_tss.cs = 0x0B; // 内核代码段 (RPL = 3)
+    sys_tss.ss = sys_tss.ds = sys_tss.es = sys_tss.fs = sys_tss.gs = 0x13; // 内核数据段 (RPL = 3)
+}
+
+/* 外部定义,用于访问当前任务 */
+extern volatile struct task* current_task;
+
+/**
+ * 切换到用户模式
+ * 
+ * 将处理器从内核模式切换到用户模式,并设置相关段寄存器和堆栈。
+ */
+void tss_switch() {
+    // 设置当前任务的内核堆栈
+    tss_set_kernel_stack(current_task->kernel_stack + KERNEL_STACK_SIZE);
+
+    /* 
+     * 汇编代码将处理器从内核模式切换到用户模式。
+     * - 设置数据段寄存器(ds, es, fs, gs)为用户模式段选择子 (0x23)。
+     * - 设置堆栈指针和代码段寄存器(cs)为用户模式段选择子 (0x1B)。
+     * - 使用 iret 指令跳转到用户模式代码段。
+     */
+    asm volatile(
+        "cli; "                           /* 禁用中断 */
+        "mov $0x23, %%ax; "               /* 加载用户模式数据段选择子 (RPL = 3) */
+        "mov %%ax, %%ds; "                /* 设置 ds 段寄存器 */
+        "mov %%ax, %%es; "                /* 设置 es 段寄存器 */
+        "mov %%ax, %%fs; "                /* 设置 fs 段寄存器 */
+        "mov %%ax, %%gs; "                /* 设置 gs 段寄存器 */
+        "mov %%esp, %%eax; "              /* 保存当前堆栈指针到 eax */
+        "pushl $0x23; "                   /* 压入用户模式数据段选择子到堆栈 */
+        "pushl %%eax; "                   /* 压入用户模式的堆栈指针 */
+        "pushf; "                         /* 压入当前 EFLAGS 寄存器 */
+        "pop %%eax; "                     /* 弹出 EFLAGS 到 eax */
+        "or $0x200, %%eax; "              /* 设置 IF 标志 (启用中断) */
+        "push %%eax; "                    /* 压回修改后的 EFLAGS */
+        "pushl $0x1B; "                   /* 压入用户模式代码段选择子 */
+        "push $1f; "                      /* 压入返回地址 (跳转到标签 1) */
+        "iret; "                          /* 执行中断返回,切换到用户模式 */
+        "1: "
+        :
+        :
+        : "eax"
+    );
+}
diff --git a/tutortial.txt b/tutortial.txt
deleted file mode 100644
index 81ee46f34274c1891195b362f0141274de35bfc7..0000000000000000000000000000000000000000
--- a/tutortial.txt
+++ /dev/null
@@ -1,49 +0,0 @@
-需要qemu、gcc、nasm,然后执行以下命令(linux):
-
-
-#!/bin/bash
-
-echo "Now assembling, compiling, and linking your kernel:"
-
-# Assemble the start.asm file for x86_64 architecture
-nasm -f elf32 -o start.o start.asm
-
-# Compile C source files with the appropriate flags
-gcc -m32 -fno-pic -mno-red-zone -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions \
-    -nostdinc -fno-builtin -I./include -c -o main.o main.c
-
-gcc -m32 -fno-pic -mno-red-zone -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions \
-    -nostdinc -fno-builtin -I./include -c -o scrn.o scrn.c
-
-gcc -m32 -fno-pic -mno-red-zone -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions \
-    -nostdinc -fno-builtin -I./include -c -o gdt.o gdt.c
-
-gcc -m32 -fno-pic -mno-red-zone -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions \
-    -nostdinc -fno-builtin -I./include -c -o idt.o idt.c
-
-gcc -m32 -fno-pic -mno-red-zone -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions \
-    -nostdinc -fno-builtin -I./include -c -o isrs.o isrs.c
-
-gcc -m32 -fno-pic -mno-red-zone -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions \
-    -nostdinc -fno-builtin -I./include -c -o irq.o irq.c
-
-gcc -m32 -fno-pic -mno-red-zone -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions \
-    -nostdinc -fno-builtin -I./include -c -o timer.o timer.c
-
-gcc -m32 -fno-pic -mno-red-zone -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions \
-    -nostdinc -fno-builtin -I./include -c -o kb.o kb.c
-
-# Link all object files into the kernel binary
-ld -m elf_i386 -T link.ld -o kernel.bin start.o main.o scrn.o gdt.o idt.o isrs.o irq.o timer.o kb.o
-
-echo "Cleaning up object files..."
-rm -f *.o
-
-echo "Done!"
-
-
-然后执行:
-sudo apt update
-sudo apt install qemu qemu-system-i386
-下载对应模拟器,然后执行:
-qemu-system-i386 -kernel kernel.bin
diff --git a/vfs,c b/vfs,c
new file mode 100644
index 0000000000000000000000000000000000000000..79f76bb44babe26bba6ed319f2fa4818cc60a279
--- /dev/null
+++ b/vfs,c
@@ -0,0 +1,96 @@
+#include <system.h>
+
+/* 定义全局变量,表示根文件系统 */
+struct fs_node* fs_root = 0; /* 根文件系统节点 */
+
+/**
+ * 从文件系统中的 inode 读取数据
+ * @param node 要读取的文件节点
+ * @param offset 偏移量,从文件的哪个位置开始读取
+ * @param size 要读取的大小(字节)
+ * @param buffer 用于存储读取内容的缓冲区
+ * @return 成功读取的字节数,如果节点不支持读取,则返回 0
+ */
+unsigned int read_fs(struct fs_node* node, unsigned int offset, unsigned int size, unsigned char* buffer) {
+    if (node->read != 0) {
+        // 如果节点提供了读取函数,则调用该函数
+        return node->read(node, offset, size, buffer);
+    } else {
+        // 节点不支持读取
+        return 0;
+    }
+}
+
+/**
+ * 向文件系统中的 inode 写入数据
+ * @param node 要写入的文件节点
+ * @param offset 偏移量,从文件的哪个位置开始写入
+ * @param size 要写入的大小(字节)
+ * @param buffer 包含要写入数据的缓冲区
+ * @return 成功写入的字节数,如果节点不支持写入,则返回 0
+ */
+unsigned int write_fs(struct fs_node* node, unsigned int offset, unsigned int size, unsigned char* buffer) {
+    if (node->write != 0) {
+        // 如果节点提供了写入函数,则调用该函数
+        return node->write(node, offset, size, buffer);
+    } else {
+        // 节点不支持写入
+        return 0;
+    }
+}
+
+/**
+ * 打开文件系统中的 inode
+ * @param node 要打开的文件节点
+ * @param read 是否以只读模式打开
+ * @param write 是否以写入模式打开
+ */
+void open_fs(struct fs_node* node, unsigned char read, unsigned char write) {
+    if (node->open != 0) {
+        // 如果节点提供了打开函数,则调用该函数
+        node->open(node);
+    }
+    // 如果没有提供打开函数,则不执行任何操作
+}
+
+/**
+ * 关闭文件系统中的 inode
+ * @param node 要关闭的文件节点
+ */
+void close_fs(struct fs_node* node) {
+    if (node->close != 0) {
+        // 如果节点提供了关闭函数,则调用该函数
+        node->close(node);
+    }
+    // 如果没有提供关闭函数,则不执行任何操作
+}
+
+/**
+ * 从文件系统中的目录读取目录项
+ * @param node 要读取的目录节点
+ * @param index 要读取的目录项索引(从 0 开始)
+ * @return 返回目录项结构的指针,如果节点不是目录或不支持读取目录,则返回 0
+ */
+struct dirent* readdir_fs(struct fs_node* node, unsigned int index) {
+    // 检查节点是否是目录,并且是否提供 readdir 函数
+    if ((node->flags & 0x7) == FS_DIRECTORY && node->readdir != 0) {
+        return node->readdir(node, index); // 调用 readdir 函数
+    } else {
+        return 0; // 节点不是目录或不支持读取目录
+    }
+}
+
+/**
+ * 在文件系统中的目录中查找子目录
+ * @param node 要查找的目录节点
+ * @param name 子目录或文件的名称
+ * @return 返回对应的节点指针,如果未找到或节点不是目录,则返回 0
+ */
+struct fs_node* finddir_fs(struct fs_node* node, char* name) {
+    // 检查节点是否是目录,并且是否提供 finddir 函数
+    if ((node->flags & 0x7) == FS_DIRECTORY && node->finddir != 0) {
+        return node->finddir(node, name); // 调用 finddir 函数
+    } else {
+        return 0; // 节点不是目录或不支持查找
+    }
+}
diff --git a/vmem.c b/vmem.c
new file mode 100644
index 0000000000000000000000000000000000000000..21a0be9e661be95fd0f9a50ad96c7f9c22ede3de
--- /dev/null
+++ b/vmem.c
@@ -0,0 +1,215 @@
+#include <system.h>
+
+/**
+ * 找到适合的内存 hole(空闲块)。
+ * @param size 需要分配的大小
+ * @param page_align 是否需要页面对齐
+ * @param heap 要操作的堆
+ * @return 返回找到的 hole 在堆索引中的位置,-1 表示未找到
+ */
+static signed int find_smallest_hole(addr size, unsigned char page_align, struct vmem_heap* heap) {
+    for (unsigned int i = 0; i < heap->index.size; i++) {
+        struct vmem_header* header = (struct vmem_header*)lookup_ordered_array(i, &heap->index);
+        addr location = (addr)header;
+
+        // 如果需要页面对齐,计算偏移量
+        signed int offset = (page_align && ((location + sizeof(struct vmem_header)) % 0x1000 != 0)) 
+                            ? 0x1000 - (location + sizeof(struct vmem_header)) % 0x1000 
+                            : 0;
+
+        // 如果该 hole 的大小足够分配内存(包括偏移),返回它的位置
+        if ((header->size - offset) >= size) {
+            return i;
+        }
+    }
+    return -1; // 没有找到合适的 hole
+}
+
+/**
+ * 比较两个内存 header 的大小,用于堆的有序数组。
+ * @param a 第一个 header
+ * @param b 第二个 header
+ * @return 返回 1 表示 a 的大小小于 b
+ */
+static signed char vmem_header_less_than(void* a, void* b) {
+    return ((struct vmem_header*)a)->size < ((struct vmem_header*)b)->size;
+}
+
+/**
+ * 创建堆
+ * @param start 堆的起始地址
+ * @param end_addr 堆的结束地址
+ * @param max 堆的最大地址
+ * @param supervisor 是否为内核模式
+ * @param readonly 是否只读
+ * @return 返回新创建的堆指针
+ */
+vmem_heap_t* create_heap(addr start, addr end_addr, addr max, unsigned char supervisor, unsigned char readonly) {
+    struct vmem_heap* heap = (struct vmem_heap*)kmalloc(sizeof(struct vmem_heap));
+
+    // 确保起始和结束地址是页面对齐的
+    ASSERT((start % 0x1000 == 0) && (end_addr % 0x1000 == 0));
+
+    // 初始化堆索引数组,存储内存块的信息
+    heap->index = place_ordered_array((void*)start, VMEM_INDEX_SIZE, &vmem_header_less_than);
+    start += sizeof(type_t) * VMEM_INDEX_SIZE; // 调整 start 位置,避开索引数组所占空间
+
+    // 确保起始地址对齐到页面边界
+    if (start % 0x1000 != 0) start = (start & 0xFFFFF000) + 0x1000;
+
+    // 初始化堆结构
+    heap->start_address = start;
+    heap->end_address = end_addr;
+    heap->max_address = max;
+    heap->supervisor = supervisor;
+    heap->readonly = readonly;
+
+    // 初始化第一个大的 hole,覆盖整个堆空间
+    struct vmem_header* hole = (struct vmem_header*)start;
+    hole->size = end_addr - start;
+    hole->magic = VMEM_MAGIC;
+    hole->is_hole = 1;
+
+    // 插入第一个 hole 到索引数组中
+    insert_ordered_array((void*)hole, &heap->index);
+
+    return heap;
+}
+
+/**
+ * 扩展堆的大小
+ * @param new_size 新的堆大小
+ * @param heap 操作的堆
+ */
+static void expand(addr new_size, struct vmem_heap* heap) {
+    ASSERT(new_size > (heap->end_address - heap->start_address)); // 新大小必须大于当前堆大小
+
+    // 对齐到页面边界
+    new_size = (new_size + 0xFFF) & 0xFFFFF000;
+    ASSERT(heap->start_address + new_size <= heap->max_address); // 确保不会超过堆的最大限制
+
+    addr old_size = heap->end_address - heap->start_address;
+
+    // 分配新页面以扩展堆
+    for (addr i = old_size; i < new_size; i += 0x1000) {
+        frame_alloc((struct page*)get_page(heap->start_address + i, 1, kernel_directory),
+                    heap->supervisor, !heap->readonly);
+    }
+    heap->end_address = heap->start_address + new_size; // 更新堆的结束地址
+}
+
+/**
+ * 收缩堆的大小
+ * @param new_size 新的堆大小
+ * @param heap 操作的堆
+ * @return 返回调整后的堆大小
+ */
+static addr contract(addr new_size, struct vmem_heap* heap) {
+    ASSERT(new_size < (heap->end_address - heap->start_address)); // 新大小必须小于当前堆大小
+
+    // 最小堆大小限制
+    if (new_size < VMEM_MIN_SIZE) new_size = VMEM_MIN_SIZE;
+
+    // 对齐到页面边界
+    new_size = (new_size + 0xFFF) & 0xFFFFF000;
+
+    addr old_size = heap->end_address - heap->start_address;
+
+    // 释放页面,减少堆大小
+    for (addr i = old_size; i > new_size; i -= 0x1000) {
+        frame_free((struct page*)get_page(heap->start_address + i - 0x1000, 0, kernel_directory));
+    }
+    heap->end_address = heap->start_address + new_size; // 更新堆的结束地址
+    return new_size;
+}
+
+/**
+ * 分配内存
+ * @param size 要分配的大小
+ * @param page_align 是否需要页面对齐
+ * @param heap 操作的堆
+ * @return 返回分配的内存地址
+ */
+void* vmalloc(addr size, unsigned char page_align, struct vmem_heap* heap) {
+    addr total_size = size + sizeof(struct vmem_header) + sizeof(struct vmem_footer);
+    signed int iterator = find_smallest_hole(total_size, page_align, heap);
+
+    // 如果没有找到合适的 hole,需要扩展堆
+    if (iterator == -1) {
+        addr old_size = heap->end_address - heap->start_address;
+        expand(old_size + total_size, heap); // 扩展堆大小
+        iterator = find_smallest_hole(total_size, page_align, heap);
+        ASSERT(iterator != -1); // 必须能找到 hole,否则出错
+    }
+
+    struct vmem_header* hole = (struct vmem_header*)lookup_ordered_array(iterator, &heap->index);
+    addr hole_size = hole->size;
+
+    // 检查是否需要拆分 hole,如果剩余空间足够创建新 hole,则拆分
+    if (hole_size - total_size > (sizeof(struct vmem_header) + sizeof(struct vmem_footer))) {
+        struct vmem_header* new_hole = (struct vmem_header*)((addr)hole + total_size);
+        new_hole->magic = VMEM_MAGIC;
+        new_hole->is_hole = 1;
+        new_hole->size = hole_size - total_size;
+
+        struct vmem_footer* new_footer = (struct vmem_footer*)((addr)new_hole + new_hole->size - sizeof(struct vmem_footer));
+        new_footer->magic = VMEM_MAGIC;
+        new_footer->header = new_hole;
+
+        insert_ordered_array((void*)new_hole, &heap->index); // 插入新 hole 到索引中
+        hole->size = total_size; // 调整原 hole 的大小
+    }
+
+    remove_ordered_array(iterator, &heap->index); // 从索引中移除已分配的 hole
+
+    hole->is_hole = 0; // 标记为已分配
+    hole->magic = VMEM_MAGIC;
+
+    struct vmem_footer* footer = (struct vmem_footer*)((addr)hole + hole->size - sizeof(struct vmem_footer));
+    footer->magic = VMEM_MAGIC;
+    footer->header = hole;
+
+    return (void*)((addr)hole + sizeof(struct vmem_header));
+}
+
+/**
+ * 释放内存
+ * @param ptr 要释放的内存指针
+ * @param heap 操作的堆
+ */
+void vfree(void* ptr, struct vmem_heap* heap) {
+    if (!ptr) return; // 如果指针为空,直接返回
+
+    struct vmem_header* header = (struct vmem_header*)((addr)ptr - sizeof(struct vmem_header));
+    struct vmem_footer* footer = (struct vmem_footer*)((addr)header + header->size - sizeof(struct vmem_footer));
+
+    // 检查 header 和 footer 的完整性
+    ASSERT(header->magic == VMEM_MAGIC && footer->magic == VMEM_MAGIC);
+
+    header->is_hole = 1; // 标记为 hole
+
+    // 合并左侧的 hole
+    struct vmem_footer* prev_footer = (struct vmem_footer*)((addr)header - sizeof(struct vmem_footer));
+    if ((addr)prev_footer >= heap->start_address && prev_footer->magic == VMEM_MAGIC && prev_footer->header->is_hole) {
+        header = prev_footer->header;
+        header->size += footer->header->size;
+        footer = (struct vmem_footer*)((addr)header + header->size - sizeof(struct vmem_footer));
+    }
+
+    // 合并右侧的 hole
+    struct vmem_header* next_header = (struct vmem_header*)((addr)footer + sizeof(struct vmem_footer));
+    if ((addr)next_header < heap->end_address && next_header->magic == VMEM_MAGIC && next_header->is_hole) {
+        header->size += next_header->size;
+        footer = (struct vmem_footer*)((addr)header + header->size - sizeof(struct vmem_footer));
+    }
+
+    footer->magic = VMEM_MAGIC;
+    footer->header = header;
+
+    // 如果释放的 block 在堆的末尾,收缩堆
+    if ((addr)footer + sizeof(struct vmem_footer) == heap->end_address) {
+        contract((addr)header - heap->start_address, heap);
+    } else {
+        insert_ordered_array((void*)header, &heap->index); // 将 hole 插入到索引中
+    }
+}