From 1dd9195d6903c5376a7626867c96e833e26be913 Mon Sep 17 00:00:00 2001 From: fslongjin Date: Mon, 15 Aug 2022 01:42:34 +0800 Subject: [PATCH] =?UTF-8?q?new:=20vma=E5=8F=8D=E5=90=91=E6=98=A0=E5=B0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 5 +- Makefile | 2 +- kernel/arch/x86_64/current.h | 20 +++++ kernel/common/Makefile | 5 +- kernel/common/semaphore.c | 43 +++++++++ kernel/common/semaphore.h | 48 ++-------- kernel/common/spinlock.h | 45 +++++----- kernel/driver/disk/ahci/ahci.c | 1 + kernel/lib/libUI/textui-render.c | 2 + kernel/lib/libUI/textui.c | 1 + kernel/mm/internal.h | 47 ++++++++-- kernel/mm/mm-types.h | 9 ++ kernel/mm/mm.c | 7 +- kernel/mm/mm.h | 16 +++- kernel/mm/mmap.c | 57 +++++++++--- kernel/mm/vma.c | 107 +++++++++++++++++++++- kernel/process/preempt.h | 3 +- kernel/process/proc-types.h | 131 +++++++++++++++++++++++++++ kernel/process/process.c | 35 ++++++-- kernel/process/process.h | 147 +------------------------------ 20 files changed, 486 insertions(+), 245 deletions(-) create mode 100644 kernel/arch/x86_64/current.h create mode 100644 kernel/common/semaphore.c create mode 100644 kernel/process/proc-types.h diff --git a/.vscode/settings.json b/.vscode/settings.json index 5d7267b1..56386d57 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -122,7 +122,10 @@ "atomic.h": "c", "uart.h": "c", "fat_ent.h": "c", - "semaphore.h": "c" + "semaphore.h": "c", + "mm-types.h": "c", + "vfs.h": "c", + "current.h": "c" }, "C_Cpp.errorSquiggles": "Enabled", "esbonio.sphinx.confDir": "" diff --git a/Makefile b/Makefile index 1a87b4c1..f3592417 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ export ARCH=__x86_64__ export ROOT_PATH=$(shell pwd) export DEBUG=DEBUG -export GLOBAL_CFLAGS := -mcmodel=large -fno-builtin -m64 -fno-stack-protector -D $(ARCH) -O1 +export GLOBAL_CFLAGS := -mcmodel=large -fno-builtin -m64 -fno-stack-protector -D $(ARCH) -O0 ifeq ($(DEBUG), DEBUG) GLOBAL_CFLAGS += -g diff --git a/kernel/arch/x86_64/current.h b/kernel/arch/x86_64/current.h new file mode 100644 index 00000000..da26753d --- /dev/null +++ b/kernel/arch/x86_64/current.h @@ -0,0 +1,20 @@ +#pragma once +#include + +#pragma GCC push_options +#pragma GCC optimize("O0") +struct process_control_block; +// 获取当前的pcb +static __always_inline struct process_control_block *get_current_pcb() +{ + struct process_control_block *current = NULL; + // 利用了当前pcb和栈空间总大小为32k大小对齐,将rsp低15位清空,即可获得pcb的起始地址 + barrier(); + __asm__ __volatile__("andq %%rsp, %0 \n\t" + : "=r"(current) + : "0"(~32767UL)); + barrier(); + return current; +}; +#define current_pcb get_current_pcb() +#pragma GCC pop_options \ No newline at end of file diff --git a/kernel/common/Makefile b/kernel/common/Makefile index 63c0e225..f8ae7f19 100644 --- a/kernel/common/Makefile +++ b/kernel/common/Makefile @@ -10,7 +10,7 @@ $(kernel_common_subdirs): ECHO $(MAKE) -C $@ all CFLAGS="$(CFLAGS)" ASFLAGS="$(ASFLAGS)" PIC="$(PIC)" -all: glib.o printk.o cpu.o bitree.o kfifo.o wait_queue.o mutex.o wait.o unistd.o string.o $(kernel_common_subdirs) +all: glib.o printk.o cpu.o bitree.o kfifo.o wait_queue.o mutex.o wait.o unistd.o string.o semaphore.o $(kernel_common_subdirs) glib.o: glib.c @@ -42,3 +42,6 @@ unistd.o: unistd.c string.o: string.c gcc $(CFLAGS) -c string.c -o string.o + +semaphore.o: semaphore.c + gcc $(CFLAGS) -c semaphore.c -o semaphore.o \ No newline at end of file diff --git a/kernel/common/semaphore.c b/kernel/common/semaphore.c new file mode 100644 index 00000000..f2f68d64 --- /dev/null +++ b/kernel/common/semaphore.c @@ -0,0 +1,43 @@ +#include "semaphore.h" +#include +#include + + +void semaphore_down(semaphore_t *sema) +{ + if (atomic_read(&sema->counter) > 0) // 信号量大于0,资源充足 + atomic_dec(&sema->counter); + else // 资源不足,进程休眠 + { + // 将当前进程加入信号量的等待队列 + wait_queue_node_t wait; + wait_queue_init(&wait, current_pcb); + + current_pcb->state = PROC_UNINTERRUPTIBLE; + + list_append(&sema->wait_queue.wait_list, &wait.wait_list); + + // 执行调度 + sched_cfs(); + } +} + +void semaphore_up(semaphore_t *sema) +{ + if (list_empty(&sema->wait_queue.wait_list)) // 没有进程在等待资源 + { + atomic_inc(&sema->counter); + } + else // 有进程在等待资源,唤醒进程 + { + + wait_queue_node_t *wq = container_of(list_next(&sema->wait_queue.wait_list), wait_queue_node_t, wait_list); + list_del(&wq->wait_list); + + wq->pcb->state = PROC_RUNNING; + sched_cfs_enqueue(wq->pcb); + + // 当前进程缺少需要的资源,立即标为需要被调度 + current_pcb->flags |= PF_NEED_SCHED; + } +}; \ No newline at end of file diff --git a/kernel/common/semaphore.h b/kernel/common/semaphore.h index b29fc084..bb4aa857 100644 --- a/kernel/common/semaphore.h +++ b/kernel/common/semaphore.h @@ -12,10 +12,7 @@ #pragma once #include -#include -#include -#include "wait_queue.h" - +#include /** * @brief 信号量的结构体 @@ -27,13 +24,16 @@ typedef struct wait_queue_node_t wait_queue; } semaphore_t; +void __semaphore_invoke_sched(); +void __semaphore_sched_enqueue(struct process_control_block *pcb); + /** * @brief 初始化信号量 * * @param sema 信号量对象 * @param count 信号量的初始值 */ -void semaphore_init(semaphore_t *sema, ul count) +static __always_inline void semaphore_init(semaphore_t *sema, ul count) { atomic_set(&sema->counter, count); wait_queue_init(&sema->wait_queue, NULL); @@ -44,40 +44,6 @@ void semaphore_init(semaphore_t *sema, ul count) * * @param sema */ -void semaphore_down(semaphore_t *sema) -{ - if (atomic_read(&sema->counter) > 0) // 信号量大于0,资源充足 - atomic_dec(&sema->counter); - else // 资源不足,进程休眠 - { - // 将当前进程加入信号量的等待队列 - wait_queue_node_t wait; - wait_queue_init(&wait, current_pcb); +void semaphore_down(semaphore_t *sema); - current_pcb->state = PROC_UNINTERRUPTIBLE; - - list_append(&sema->wait_queue.wait_list, &wait.wait_list); - - // 执行调度 - sched_cfs(); - } -} - -void semaphore_up(semaphore_t *sema) -{ - if (list_empty(&sema->wait_queue.wait_list)) // 没有进程在等待资源 - { - atomic_inc(&sema->counter); - } - else // 有进程在等待资源,唤醒进程 - { - - wait_queue_node_t *wq = container_of(list_next(&sema->wait_queue.wait_list), wait_queue_node_t, wait_list); - list_del(&wq->wait_list); - - wq->pcb->state = PROC_RUNNING; - sched_cfs_enqueue(wq->pcb); - // 当前进程缺少需要的资源,立即标为需要被调度 - current_pcb->flags |= PF_NEED_SCHED; - } -} \ No newline at end of file +void semaphore_up(semaphore_t *sema); \ No newline at end of file diff --git a/kernel/common/spinlock.h b/kernel/common/spinlock.h index 36653b5d..5b1d54ea 100644 --- a/kernel/common/spinlock.h +++ b/kernel/common/spinlock.h @@ -18,18 +18,9 @@ */ typedef struct { - __volatile__ char lock; // 1:unlocked 0:locked + int8_t lock; // 1:unlocked 0:locked } spinlock_t; -/** - * @brief 初始化自旋锁 - * - * @param lock - */ -void spin_init(spinlock_t *lock) -{ - lock->lock = 1; -} /** * @brief 自旋锁加锁 @@ -53,8 +44,8 @@ void spin_lock(spinlock_t *lock) /** * @brief 自旋锁解锁 - * - * @param lock + * + * @param lock */ void spin_unlock(spinlock_t *lock) { @@ -63,14 +54,26 @@ void spin_unlock(spinlock_t *lock) : "=m"(lock->lock)::"memory"); } +/** + * @brief 初始化自旋锁 + * + * @param lock + */ +void spin_init(spinlock_t *lock) +{ + barrier(); + lock->lock = 1; + barrier(); +} + /** * @brief 自旋锁加锁(不改变自旋锁持有计数) - * + * * @warning 慎用此函数,除非你有十足的把握不会产生自旋锁计数错误 */ void spin_lock_no_preempt(spinlock_t *lock) { - __asm__ __volatile__("1: \n\t" + __asm__ __volatile__("1: \n\t" "lock decq %0 \n\t" // 尝试-1 "jns 3f \n\t" // 加锁成功,跳转到步骤3 "2: \n\t" // 加锁失败,稍后再试 @@ -83,17 +86,15 @@ void spin_lock_no_preempt(spinlock_t *lock) } /** * @brief 自旋锁解锁(不改变自旋锁持有计数) - * + * * @warning 慎用此函数,除非你有十足的把握不会产生自旋锁计数错误 */ -void spin_unlock_no_preempt(spinlock_t * lock) +void spin_unlock_no_preempt(spinlock_t *lock) { __asm__ __volatile__("movq $1, %0 \n\t" : "=m"(lock->lock)::"memory"); } - - /** * @brief 尝试加锁 * @@ -125,7 +126,7 @@ long spin_trylock(spinlock_t *lock) /** * @brief 保存中断状态,关闭中断,并自旋锁加锁 - * + * */ #define spin_lock_irqsave(lock, flags) \ do \ @@ -136,7 +137,7 @@ long spin_trylock(spinlock_t *lock) /** * @brief 恢复rflags以及中断状态并解锁自旋锁 - * + * */ #define spin_unlock_irqrestore(lock, flags) \ do \ @@ -147,7 +148,7 @@ long spin_trylock(spinlock_t *lock) /** * @brief 关闭中断并加锁 - * + * */ #define spin_lock_irq(lock) \ do \ @@ -158,7 +159,7 @@ long spin_trylock(spinlock_t *lock) /** * @brief 解锁并开启中断 - * + * */ #define spin_unlock_irq(lock) \ do \ diff --git a/kernel/driver/disk/ahci/ahci.c b/kernel/driver/disk/ahci/ahci.c index 46b23b55..b4f8e100 100644 --- a/kernel/driver/disk/ahci/ahci.c +++ b/kernel/driver/disk/ahci/ahci.c @@ -3,6 +3,7 @@ #include #include #include +#include struct pci_device_structure_header_t *ahci_devs[MAX_AHCI_DEVICES]; diff --git a/kernel/lib/libUI/textui-render.c b/kernel/lib/libUI/textui-render.c index 734e3c30..5fc985f0 100644 --- a/kernel/lib/libUI/textui-render.c +++ b/kernel/lib/libUI/textui-render.c @@ -1,5 +1,7 @@ #include "textui.h" #include +#include +#include "screen_manager.h" #define WHITE 0x00ffffff //白 #define BLACK 0x00000000 //黑 diff --git a/kernel/lib/libUI/textui.c b/kernel/lib/libUI/textui.c index 64175dab..e8a047dc 100644 --- a/kernel/lib/libUI/textui.c +++ b/kernel/lib/libUI/textui.c @@ -5,6 +5,7 @@ #include #include #include +#include struct scm_ui_framework_t textui_framework; static spinlock_t __window_id_lock = {1}; diff --git a/kernel/mm/internal.h b/kernel/mm/internal.h index 20fc1020..67b3f9e6 100644 --- a/kernel/mm/internal.h +++ b/kernel/mm/internal.h @@ -4,26 +4,61 @@ /** * @brief 将vma结构体插入mm_struct的链表之中 - * + * * @param mm 内存空间分布结构体 * @param vma 待插入的VMA结构体 * @param prev 链表的前一个结点 */ -void __vma_link_list(struct mm_struct * mm, struct vm_area_struct * vma, struct vm_area_struct * prev); +void __vma_link_list(struct mm_struct *mm, struct vm_area_struct *vma, struct vm_area_struct *prev); /** * @brief 将vma给定结构体从vma链表的结点之中删除 - * + * * @param mm 内存空间分布结构体 * @param vma 待插入的VMA结构体 */ -void __vma_unlink_list(struct mm_struct * mm, struct vm_area_struct * vma); +void __vma_unlink_list(struct mm_struct *mm, struct vm_area_struct *vma); /** * @brief 获取指定虚拟地址处映射的物理地址 - * + * * @param mm 内存空间分布结构体 * @param vaddr 虚拟地址 * @return uint64_t 已映射的物理地址 */ -uint64_t __mm_get_paddr(struct mm_struct * mm, uint64_t vaddr); +uint64_t __mm_get_paddr(struct mm_struct *mm, uint64_t vaddr); + +/** + * @brief 创建anon_vma,并将其与页面结构体进行绑定 + * 若提供的页面结构体指针为NULL,则只创建,不绑定 + * + * @param page 页面结构体的指针 + * @param lock_page 是否将页面结构体加锁 + * @return struct anon_vma_t* 创建好的anon_vma + */ +struct anon_vma_t *__anon_vma_create_alloc(struct Page *page, bool lock_page); + +/** + * @brief 释放anon vma结构体 + * + * @param anon_vma 待释放的anon_vma结构体 + * @return int 返回码 + */ +int __anon_vma_free(struct anon_vma_t *anon_vma); + +/** + * @brief 将指定的vma加入到anon_vma的管理范围之中 + * + * @param anon_vma 页面的anon_vma + * @param vma 待加入的vma + * @return int 返回码 + */ +int __anon_vma_add(struct anon_vma_t *anon_vma, struct vm_area_struct *vma); + +/** + * @brief 从anon_vma的管理范围中删除指定的vma + * (在进入这个函数之前,应该要加锁) + * @param vma 将要取消对应的anon_vma管理的vma结构体 + * @return int 返回码 + */ +int __anon_vma_del(struct vm_area_struct *vma); \ No newline at end of file diff --git a/kernel/mm/mm-types.h b/kernel/mm/mm-types.h index 156b35a5..ab76a9cf 100644 --- a/kernel/mm/mm-types.h +++ b/kernel/mm/mm-types.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include #include struct mm_struct; @@ -163,10 +164,18 @@ struct mm_struct */ struct anon_vma_t { + // anon vma的操作信号量 semaphore_t sem; + + /** + * 记录当前有多少个vma与该anon_vma关联,当vma被释放时, + * 应当检查这个值。当该值为0时,应当释放anon_vma结构体 + */ atomic_t ref_count; // todo: 把下面的循环链表更换成红黑树 // 与当前anon_vma相关的vma的列表 struct List vma_list; + // 当前anon vma对应的page + struct Page* page; }; \ No newline at end of file diff --git a/kernel/mm/mm.c b/kernel/mm/mm.c index 6d682ab7..77088409 100644 --- a/kernel/mm/mm.c +++ b/kernel/mm/mm.c @@ -213,6 +213,7 @@ void mm_init() barrier(); tmp_page = memory_management_struct.pages_struct + j; page_init(tmp_page, PAGE_PGT_MAPPED | PAGE_KERNEL | PAGE_KERNEL_INIT); + barrier(); page_num = tmp_page->addr_phys >> PAGE_2M_SHIFT; *(memory_management_struct.bmp + (page_num >> 6)) |= (1UL << (page_num % 64)); ++tmp_page->zone->count_pages_using; @@ -248,7 +249,7 @@ unsigned long page_init(struct Page *page, ul flags) ++page->zone->total_pages_link; } page->anon_vma = NULL; - spin_init(&page->op_lock); + spin_init(&(page->op_lock)); return 0; } @@ -588,7 +589,9 @@ uint64_t mm_do_brk(uint64_t old_brk_end_addr, int64_t offset) { for (uint64_t i = old_brk_end_addr; i < end_addr; i += PAGE_2M_SIZE) { - mm_map_vma(current_pcb->mm, i, PAGE_2M_SIZE, alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys, VM_USER | VM_ACCESS_FLAGS, NULL); + struct vm_area_struct * vma=NULL; + mm_create_vma(current_pcb->mm, i, PAGE_2M_SIZE, VM_USER | VM_ACCESS_FLAGS, NULL, &vma); + mm_map_vma(vma, alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys); } current_pcb->mm->brk_end = end_addr; } diff --git a/kernel/mm/mm.h b/kernel/mm/mm.h index 0e252d60..36322960 100644 --- a/kernel/mm/mm.h +++ b/kernel/mm/mm.h @@ -325,6 +325,7 @@ static inline void vma_init(struct vm_area_struct *vma, struct mm_struct *mm) vma->vm_mm = mm; vma->vm_prev = vma->vm_next = NULL; vma->vm_ops = NULL; + list_init(&vma->anon_vma_list); } /** @@ -442,17 +443,26 @@ void mm_unmap_proc_table(ul proc_page_table_addr, bool is_phys, ul virt_addr_sta }) /** - * @brief 创建VMA,并将物理地址映射到指定的虚拟地址处 + * @brief 创建VMA * * @param mm 要绑定的内存空间分布结构体 * @param vaddr 起始虚拟地址 * @param length 长度(字节) - * @param paddr 起始物理地址 * @param vm_flags vma的标志 * @param vm_ops vma的操作接口 + * @param res_vma 返回的vma指针 * @return int 错误码 */ -int mm_map_vma(struct mm_struct *mm, uint64_t vaddr, uint64_t length, uint64_t paddr, vm_flags_t vm_flags, struct vm_operations_t *vm_ops); +int mm_create_vma(struct mm_struct *mm, uint64_t vaddr, uint64_t length, vm_flags_t vm_flags, struct vm_operations_t *vm_ops, struct vm_area_struct **res_vma); + +/** + * @brief 将指定的物理地址映射到指定的vma处 + * + * @param vma 要进行映射的VMA结构体 + * @param paddr 起始物理地址 + * @return int 错误码 + */ +int mm_map_vma(struct vm_area_struct *vma, uint64_t paddr); /** * @brief 在页表中取消指定的vma的映射 diff --git a/kernel/mm/mmap.c b/kernel/mm/mmap.c index 4ebb7223..98508ac7 100644 --- a/kernel/mm/mmap.c +++ b/kernel/mm/mmap.c @@ -306,17 +306,17 @@ void mm_unmap_proc_table(ul proc_page_table_addr, bool is_phys, ul virt_addr_sta } /** - * @brief 创建VMA,并将物理地址映射到指定的虚拟地址处 + * @brief 创建VMA * * @param mm 要绑定的内存空间分布结构体 * @param vaddr 起始虚拟地址 * @param length 长度(字节) - * @param paddr 起始物理地址 * @param vm_flags vma的标志 * @param vm_ops vma的操作接口 + * @param res_vma 返回的vma指针 * @return int 错误码 */ -int mm_map_vma(struct mm_struct *mm, uint64_t vaddr, uint64_t length, uint64_t paddr, vm_flags_t vm_flags, struct vm_operations_t *vm_ops) +int mm_create_vma(struct mm_struct *mm, uint64_t vaddr, uint64_t length, vm_flags_t vm_flags, struct vm_operations_t *vm_ops, struct vm_area_struct **res_vma) { int retval = 0; struct vm_area_struct *vma = vm_area_alloc(mm); @@ -327,19 +327,50 @@ int mm_map_vma(struct mm_struct *mm, uint64_t vaddr, uint64_t length, uint64_t p vma->vm_start = vaddr; vma->vm_end = vaddr + length; - // 将vma与对应的anon_vma进行绑定 - struct Page * pg = Phy_to_2M_Page(paddr); - // 将VMA加入链表 + // 将VMA加入mm的链表 retval = vma_insert(mm, vma); if (retval == -EEXIST) // 之前已经存在了相同的vma,直接返回 { + *res_vma = vma_find(mm, vma->vm_start); kfree(vma); return -EEXIST; } + *res_vma = vma; + return 0; +} + +/** + * @brief 将指定的物理地址映射到指定的vma处 + * + * @param vma 要进行映射的VMA结构体 + * @param paddr 起始物理地址 + * @return int 错误码 + */ +int mm_map_vma(struct vm_area_struct *vma, uint64_t paddr) +{ + int retval = 0; + // 获取物理地址对应的页面 + struct Page *pg = Phy_to_2M_Page(paddr); + if (unlikely(pg->anon_vma == NULL)) // 若页面不存在anon_vma,则为页面创建anon_vma + { + uint64_t rflags; + // todo: 查明为什么在mm_init中,spin init之后,pg会变为0 + spin_init(&pg->op_lock); + spin_lock_irqsave(&pg->op_lock, rflags); + if (unlikely(pg->anon_vma == NULL)) + __anon_vma_create_alloc(pg, false); + spin_unlock_irqrestore(&pg->op_lock, rflags); + } + barrier(); + // 将anon vma与vma进行绑定 + __anon_vma_add(pg->anon_vma, vma); + barrier(); + + uint64_t length = vma->vm_end - vma->vm_start; + // ==== 将地址映射到页表 ==== uint64_t len_4k = length % PAGE_2M_SIZE; uint64_t len_2m = length - len_4k; - // ==== 将地址映射到页表 /* todo: 限制页面的读写权限 */ @@ -348,12 +379,12 @@ int mm_map_vma(struct mm_struct *mm, uint64_t vaddr, uint64_t length, uint64_t p if (likely(len_2m > 0)) { uint64_t page_flags = 0; - if (vm_flags & VM_USER) + if (vma->vm_flags & VM_USER) page_flags = PAGE_USER_PAGE; else page_flags = PAGE_KERNEL_PAGE; // 这里直接设置user标志位为false,因为该函数内部会对其进行自动校正 - retval = mm_map_proc_page_table((uint64_t)mm->pgd, true, vaddr, paddr, len_2m, page_flags, false, false, false); + retval = mm_map_proc_page_table((uint64_t)vma->vm_mm->pgd, true, vma->vm_start, paddr, len_2m, page_flags, false, false, false); if (unlikely(retval != 0)) goto failed; } @@ -363,12 +394,12 @@ int mm_map_vma(struct mm_struct *mm, uint64_t vaddr, uint64_t length, uint64_t p len_4k = ALIGN(len_4k, PAGE_4K_SIZE); uint64_t page_flags = 0; - if (vm_flags & VM_USER) + if (vma->vm_flags & VM_USER) page_flags = PAGE_USER_4K_PAGE; else page_flags = PAGE_KERNEL_4K_PAGE; // 这里直接设置user标志位为false,因为该函数内部会对其进行自动校正 - retval = mm_map_proc_page_table((uint64_t)mm->pgd, true, vaddr + len_2m, paddr + len_2m, len_4k, page_flags, false, false, true); + retval = mm_map_proc_page_table((uint64_t)vma->vm_mm->pgd, true, vma->vm_start + len_2m, paddr + len_2m, len_4k, page_flags, false, false, true); if (unlikely(retval != 0)) goto failed; } @@ -376,9 +407,7 @@ int mm_map_vma(struct mm_struct *mm, uint64_t vaddr, uint64_t length, uint64_t p flush_tlb(); return 0; failed:; - kdebug("failed."); - __vma_unlink_list(mm, vma); - vm_area_free(vma); + kdebug("map VMA failed."); return retval; } diff --git a/kernel/mm/vma.c b/kernel/mm/vma.c index 72578144..40789b60 100644 --- a/kernel/mm/vma.c +++ b/kernel/mm/vma.c @@ -139,10 +139,10 @@ int vma_insert(struct mm_struct *mm, struct vm_area_struct *vma) prev = prev->vm_prev; if (prev == NULL) // 要将当前vma插入到链表的尾部 { - struct vm_area_struct * ptr = mm->vmas; - while(ptr) + struct vm_area_struct *ptr = mm->vmas; + while (ptr) { - if(ptr->vm_next) + if (ptr->vm_next) ptr = ptr->vm_next; else { @@ -153,4 +153,103 @@ int vma_insert(struct mm_struct *mm, struct vm_area_struct *vma) } __vma_link_list(mm, vma, prev); return 0; -} \ No newline at end of file +} + +/** + * @brief 创建anon_vma,并将其与页面结构体进行绑定 + * 若提供的页面结构体指针为NULL,则只创建,不绑定 + * + * @param page 页面结构体的指针 + * @param lock_page 是否将页面结构体加锁 + * @return struct anon_vma_t* 创建好的anon_vma + */ +struct anon_vma_t *__anon_vma_create_alloc(struct Page *page, bool lock_page) +{ + struct anon_vma_t *anon_vma = (struct anon_vma_t *)kmalloc(sizeof(struct anon_vma_t), 0); + if (unlikely(anon_vma == NULL)) + return NULL; + memset(anon_vma, 0, sizeof(struct anon_vma_t)); + + list_init(&anon_vma->vma_list); + semaphore_init(&anon_vma->sem, 1); + + // 需要和page进行绑定 + if (page != NULL) + { + if (lock_page == true) // 需要加锁 + { + uint64_t rflags; + spin_lock_irqsave(&page->op_lock, rflags); + page->anon_vma = anon_vma; + spin_unlock_irqrestore(&page->op_lock, rflags); + } + else + page->anon_vma = anon_vma; + + anon_vma->page = page; + } + return anon_vma; +} + +/** + * @brief 将指定的vma加入到anon_vma的管理范围之中 + * + * @param anon_vma 页面的anon_vma + * @param vma 待加入的vma + * @return int 返回码 + */ +int __anon_vma_add(struct anon_vma_t *anon_vma, struct vm_area_struct *vma) +{ + semaphore_down(&anon_vma->sem); + list_add(&anon_vma->vma_list, &vma->anon_vma_list); + vma->anon_vma = anon_vma; + atomic_inc(&anon_vma->ref_count); + semaphore_up(&anon_vma->sem); + return 0; +} + +/** + * @brief 释放anon vma结构体 + * + * @param anon_vma 待释放的anon_vma结构体 + * @return int 返回码 + */ +int __anon_vma_free(struct anon_vma_t *anon_vma) +{ + if (anon_vma->page != NULL) + { + spin_lock(&anon_vma->page->op_lock); + anon_vma->page->anon_vma = NULL; + spin_unlock(&anon_vma->page->op_lock); + } + kfree(anon_vma); + + return 0; +} + +/** + * @brief 从anon_vma的管理范围中删除指定的vma + * (在进入这个函数之前,应该要加锁) + * @param vma 将要取消对应的anon_vma管理的vma结构体 + * @return int 返回码 + */ +int __anon_vma_del(struct vm_area_struct *vma) +{ + // 当前vma没有绑定anon_vma + if (vma->anon_vma == NULL) + return -EINVAL; + + list_del(&vma->anon_vma_list); + semaphore_down(&vma->anon_vma->sem); + atomic_dec(&vma->anon_vma->ref_count); + + if (unlikely(atomic_read(&vma->anon_vma->ref_count) == 0)) // 应当释放该anon_vma + { + __anon_vma_free(vma->anon_vma); + // 释放了anon_vma之后,清理当前vma的关联数据 + vma->anon_vma = NULL; + list_init(&vma->anon_vma_list); + } + else + semaphore_up(&vma->anon_vma->sem); +} diff --git a/kernel/process/preempt.h b/kernel/process/preempt.h index 43689060..c3f4794b 100644 --- a/kernel/process/preempt.h +++ b/kernel/process/preempt.h @@ -1,6 +1,7 @@ #pragma once -#include +#include +#include "proc-types.h" /** * @brief 增加自旋锁计数变量 diff --git a/kernel/process/proc-types.h b/kernel/process/proc-types.h new file mode 100644 index 00000000..1b186b6a --- /dev/null +++ b/kernel/process/proc-types.h @@ -0,0 +1,131 @@ +#pragma once + +#include + +// 进程最大可拥有的文件描述符数量 +#define PROC_MAX_FD_NUM 16 + +// 进程的内核栈大小 32K +#define STACK_SIZE 32768 + +// 进程的运行状态 +// 正在运行 +#define PROC_RUNNING (1 << 0) +// 可被中断 +#define PROC_INTERRUPTIBLE (1 << 1) +// 不可被中断 +#define PROC_UNINTERRUPTIBLE (1 << 2) +// 挂起 +#define PROC_ZOMBIE (1 << 3) +// 已停止 +#define PROC_STOPPED (1 << 4) + +// 内核代码段基地址 +#define KERNEL_CS (0x08) +// 内核数据段基地址 +#define KERNEL_DS (0x10) +// 用户代码段基地址 +#define USER_CS (0x28) +// 用户数据段基地址 +#define USER_DS (0x30) + +// 进程初始化时的数据拷贝标志位 +#define CLONE_FS (1 << 0) // 在进程间共享打开的文件 +#define CLONE_SIGNAL (1 << 1) +#define CLONE_VM (1 << 2) // 在进程间共享虚拟内存空间 + +struct thread_struct +{ + // 内核层栈基指针 + ul rbp; // in tss rsp0 + // 内核层代码指针 + ul rip; + // 内核层栈指针 + ul rsp; + + ul fs, gs; + + ul cr2; + // 异常号 + ul trap_num; + // 错误码 + ul err_code; +}; + +// ========= pcb->flags ========= +// 进程标志位 +#define PF_KTHREAD (1UL << 0) // 内核线程 +#define PF_NEED_SCHED (1UL << 1) // 进程需要被调度 +#define PF_VFORK (1UL << 2) // 标志进程是否由于vfork而存在资源共享 +#define PF_KFORK (1UL << 3) // 标志在内核态下调用fork(临时标记,do_fork()结束后会将其复位) + +/** + * @brief 进程控制块 + * + */ +struct process_control_block +{ + // 进程的状态 + volatile long state; + // 进程标志:进程、线程、内核线程 + unsigned long flags; + int64_t preempt_count; // 持有的自旋锁的数量 + long signal; + long cpu_id; // 当前进程在哪个CPU核心上运行 + // 内存空间分布结构体, 记录内存页表和程序段信息 + struct mm_struct *mm; + + // 进程切换时保存的状态信息 + struct thread_struct *thread; + + // 连接各个pcb的双向链表 + struct List list; + + // 地址空间范围 + // 用户空间: 0x0000 0000 0000 0000 ~ 0x0000 7fff ffff ffff + // 内核空间: 0xffff 8000 0000 0000 ~ 0xffff ffff ffff ffff + uint64_t addr_limit; + + long pid; + long priority; // 优先级 + int64_t virtual_runtime; // 虚拟运行时间 + + // 进程拥有的文件描述符的指针数组 + // todo: 改用动态指针数组 + struct vfs_file_t *fds[PROC_MAX_FD_NUM]; + + // 链表中的下一个pcb + struct process_control_block *next_pcb; + // 父进程的pcb + struct process_control_block *parent_pcb; + + int32_t exit_code; // 进程退出时的返回码 + wait_queue_node_t wait_child_proc_exit; // 子进程退出等待队列 +}; + +// 将进程的pcb和内核栈融合到一起,8字节对齐 +union proc_union +{ + struct process_control_block pcb; + ul stack[STACK_SIZE / sizeof(ul)]; +} __attribute__((aligned(8))); + +struct tss_struct +{ + unsigned int reserved0; + ul rsp0; + ul rsp1; + ul rsp2; + ul reserved1; + ul ist1; + ul ist2; + ul ist3; + ul ist4; + ul ist5; + ul ist6; + ul ist7; + ul reserved2; + unsigned short reserved3; + // io位图基地址 + unsigned short io_map_base_addr; +} __attribute__((packed)); // 使用packed表明是紧凑结构,编译器不会对成员变量进行字节对齐。 \ No newline at end of file diff --git a/kernel/process/process.c b/kernel/process/process.c index 7e93fd71..f860d81d 100644 --- a/kernel/process/process.c +++ b/kernel/process/process.c @@ -262,10 +262,13 @@ static int process_load_elf_file(struct pt_regs *regs, char *path) { uint64_t pa = alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys; - int ret = mm_map_vma(current_pcb->mm, virt_base, PAGE_2M_SIZE, pa, VM_USER | VM_ACCESS_FLAGS, NULL); + struct vm_area_struct *vma = NULL; + int ret = mm_create_vma(current_pcb->mm, virt_base, PAGE_2M_SIZE, VM_USER | VM_ACCESS_FLAGS, NULL, &vma); // 防止内存泄露 if (ret == -EEXIST) free_pages(Phy_to_2M_Page(pa), 1); + else + mm_map_vma(vma, pa); memset((void *)virt_base, 0, PAGE_2M_SIZE); map_size = PAGE_2M_SIZE; } @@ -278,9 +281,12 @@ static int process_load_elf_file(struct pt_regs *regs, char *path) { uint64_t paddr = virt_2_phys((uint64_t)kmalloc(PAGE_4K_SIZE, 0)); - int val = mm_map_vma(current_pcb->mm, virt_base + off, PAGE_4K_SIZE, paddr, VM_USER | VM_ACCESS_FLAGS, NULL); + struct vm_area_struct *vma = NULL; + int val = mm_create_vma(current_pcb->mm, virt_base + off, PAGE_4K_SIZE, VM_USER | VM_ACCESS_FLAGS, NULL, &vma); if (val == -EEXIST) kfree(phys_2_virt(paddr)); + else + mm_map_vma(vma, paddr); memset((void *)(virt_base + off), 0, PAGE_4K_SIZE); } } @@ -307,10 +313,13 @@ static int process_load_elf_file(struct pt_regs *regs, char *path) regs->rbp = current_pcb->mm->stack_start; { + struct vm_area_struct *vma = NULL; uint64_t pa = alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys; - int val = mm_map_vma(current_pcb->mm, current_pcb->mm->stack_start - PAGE_2M_SIZE, PAGE_2M_SIZE, pa, VM_USER | VM_ACCESS_FLAGS, NULL); + int val = mm_create_vma(current_pcb->mm, current_pcb->mm->stack_start - PAGE_2M_SIZE, PAGE_2M_SIZE, VM_USER | VM_ACCESS_FLAGS, NULL, &vma); if (val == -EEXIST) free_pages(Phy_to_2M_Page(pa), 1); + else + mm_map_vma(vma, pa); } // 清空栈空间 @@ -928,8 +937,14 @@ uint64_t process_copy_mm(uint64_t clone_flags, struct process_control_block *pcb { uint64_t pa = alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys; - mm_map_vma(new_mms, vma->vm_start + i * PAGE_2M_SIZE, PAGE_2M_SIZE, pa, vma->vm_flags, vma->vm_ops); - // kdebug("phys_2_virt(pa)=%#018lx, vaddr=%#018lx", phys_2_virt(pa), vma->vm_start + i * PAGE_2M_SIZE); + struct vm_area_struct *new_vma = NULL; + int ret = mm_create_vma(new_mms, vma->vm_start + i * PAGE_2M_SIZE, PAGE_2M_SIZE, vma->vm_flags, vma->vm_ops, &new_vma); + // 防止内存泄露 + if (unlikely(ret == -EEXIST)) + free_pages(Phy_to_2M_Page(pa), 1); + else + mm_map_vma(new_vma, pa); + memcpy((void *)phys_2_virt(pa), (void *)(vma->vm_start + i * PAGE_2M_SIZE), (vma_size >= PAGE_2M_SIZE) ? PAGE_2M_SIZE : vma_size); vma_size -= PAGE_2M_SIZE; } @@ -938,7 +953,15 @@ uint64_t process_copy_mm(uint64_t clone_flags, struct process_control_block *pcb { uint64_t map_size = PAGE_4K_ALIGN(vma_size); uint64_t va = (uint64_t)kmalloc(map_size, 0); - mm_map_vma(new_mms, vma->vm_start, map_size, virt_2_phys(va), vma->vm_flags, vma->vm_ops); + + struct vm_area_struct *new_vma = NULL; + int ret = mm_create_vma(new_mms, vma->vm_start, map_size, vma->vm_flags, vma->vm_ops, &new_vma); + // 防止内存泄露 + if (unlikely(ret == -EEXIST)) + kfree((void *)va); + else + mm_map_vma(new_vma, virt_2_phys(va)); + memcpy((void *)va, (void *)vma->vm_start, vma_size); } vma = vma->vm_next; diff --git a/kernel/process/process.h b/kernel/process/process.h index 9e9b3cf0..108fe54b 100644 --- a/kernel/process/process.h +++ b/kernel/process/process.h @@ -17,114 +17,8 @@ #include #include #include - -// 进程最大可拥有的文件描述符数量 -#define PROC_MAX_FD_NUM 16 - -// 进程的内核栈大小 32K -#define STACK_SIZE 32768 - -// 进程的运行状态 -// 正在运行 -#define PROC_RUNNING (1 << 0) -// 可被中断 -#define PROC_INTERRUPTIBLE (1 << 1) -// 不可被中断 -#define PROC_UNINTERRUPTIBLE (1 << 2) -// 挂起 -#define PROC_ZOMBIE (1 << 3) -// 已停止 -#define PROC_STOPPED (1 << 4) - -// 内核代码段基地址 -#define KERNEL_CS (0x08) -// 内核数据段基地址 -#define KERNEL_DS (0x10) -// 用户代码段基地址 -#define USER_CS (0x28) -// 用户数据段基地址 -#define USER_DS (0x30) - -// 进程初始化时的数据拷贝标志位 -#define CLONE_FS (1 << 0) // 在进程间共享打开的文件 -#define CLONE_SIGNAL (1 << 1) -#define CLONE_VM (1 << 2) // 在进程间共享虚拟内存空间 - -struct thread_struct -{ - // 内核层栈基指针 - ul rbp; // in tss rsp0 - // 内核层代码指针 - ul rip; - // 内核层栈指针 - ul rsp; - - ul fs, gs; - - ul cr2; - // 异常号 - ul trap_num; - // 错误码 - ul err_code; -}; - -// ========= pcb->flags ========= -// 进程标志位 -#define PF_KTHREAD (1UL << 0) // 内核线程 -#define PF_NEED_SCHED (1UL << 1) // 进程需要被调度 -#define PF_VFORK (1UL << 2) // 标志进程是否由于vfork而存在资源共享 -#define PF_KFORK (1UL << 3) // 标志在内核态下调用fork(临时标记,do_fork()结束后会将其复位) - -/** - * @brief 进程控制块 - * - */ -struct process_control_block -{ - // 进程的状态 - volatile long state; - // 进程标志:进程、线程、内核线程 - unsigned long flags; - int64_t preempt_count; // 持有的自旋锁的数量 - long signal; - long cpu_id; // 当前进程在哪个CPU核心上运行 - // 内存空间分布结构体, 记录内存页表和程序段信息 - struct mm_struct *mm; - - // 进程切换时保存的状态信息 - struct thread_struct *thread; - - // 连接各个pcb的双向链表 - struct List list; - - // 地址空间范围 - // 用户空间: 0x0000 0000 0000 0000 ~ 0x0000 7fff ffff ffff - // 内核空间: 0xffff 8000 0000 0000 ~ 0xffff ffff ffff ffff - uint64_t addr_limit; - - long pid; - long priority; // 优先级 - int64_t virtual_runtime; // 虚拟运行时间 - - // 进程拥有的文件描述符的指针数组 - // todo: 改用动态指针数组 - struct vfs_file_t *fds[PROC_MAX_FD_NUM]; - - // 链表中的下一个pcb - struct process_control_block *next_pcb; - // 父进程的pcb - struct process_control_block *parent_pcb; - - int32_t exit_code; // 进程退出时的返回码 - wait_queue_node_t wait_child_proc_exit; // 子进程退出等待队列 -}; - -// 将进程的pcb和内核栈融合到一起,8字节对齐 -union proc_union -{ - struct process_control_block pcb; - ul stack[STACK_SIZE / sizeof(ul)]; -} __attribute__((aligned(8))); +#include +#include "proc-types.h" // 设置初始进程的PCB #define INITIAL_PROC(proc) \ @@ -151,25 +45,7 @@ union proc_union * @brief 任务状态段结构体 * */ -struct tss_struct -{ - unsigned int reserved0; - ul rsp0; - ul rsp1; - ul rsp2; - ul reserved1; - ul ist1; - ul ist2; - ul ist3; - ul ist4; - ul ist5; - ul ist6; - ul ist7; - ul reserved2; - unsigned short reserved3; - // io位图基地址 - unsigned short io_map_base_addr; -} __attribute__((packed)); // 使用packed表明是紧凑结构,编译器不会对成员变量进行字节对齐。 + // 设置初始进程的tss #define INITIAL_TSS \ @@ -191,22 +67,7 @@ struct tss_struct .io_map_base_addr = 0 \ } -#pragma GCC push_options -#pragma GCC optimize("O0") -// 获取当前的pcb -struct process_control_block *get_current_pcb() -{ - struct process_control_block *current = NULL; - // 利用了当前pcb和栈空间总大小为32k大小对齐,将rsp低15位清空,即可获得pcb的起始地址 - barrier(); - __asm__ __volatile__("andq %%rsp, %0 \n\t" - : "=r"(current) - : "0"(~32767UL)); - barrier(); - return current; -}; -#pragma GCC pop_options -#define current_pcb get_current_pcb() + #define GET_CURRENT_PCB \ "movq %rsp, %rbx \n\t" \