From 12293ed1d71cd306ed9a5d021a79ba945fe4e680 Mon Sep 17 00:00:00 2001 From: Minep Date: Sat, 18 Jun 2022 17:27:38 +0100 Subject: [PATCH 1/1] chore: make things more general refactor: put a limit on kernel heap. refactor: reduce overhead on kernel stack creation when we fork into our first process. --- lunaix-os/includes/lunaix/mm/mm.h | 35 +++++++++------ lunaix-os/includes/lunaix/mm/page.h | 2 +- lunaix-os/includes/lunaix/mm/region.h | 15 +++++-- lunaix-os/includes/lunaix/mm/vmm.h | 12 ++++++ lunaix-os/includes/lunaix/process.h | 11 +++++ lunaix-os/kernel/asm/x86/pfault.c | 2 +- lunaix-os/kernel/k_init.c | 51 +++++++++++----------- lunaix-os/kernel/mm/kalloc.c | 13 +++++- lunaix-os/kernel/mm/region.c | 62 +++++++++++++++++---------- lunaix-os/kernel/mm/vmm.c | 4 ++ lunaix-os/kernel/process.c | 55 ++++++++++++++---------- 11 files changed, 170 insertions(+), 92 deletions(-) diff --git a/lunaix-os/includes/lunaix/mm/mm.h b/lunaix-os/includes/lunaix/mm/mm.h index 04f2f02..060b881 100644 --- a/lunaix-os/includes/lunaix/mm/mm.h +++ b/lunaix-os/includes/lunaix/mm/mm.h @@ -4,7 +4,7 @@ #include #include -typedef struct +typedef struct { void* start; void* brk; @@ -14,28 +14,35 @@ typedef struct /** * @brief 私有区域,该区域中的页无法进行任何形式的共享。 - * + * */ -#define REGION_PRIVATE 0x0 +#define REGION_PRIVATE 0x0 /** - * @brief 读共享区域,该区域中的页可以被两个进程之间读共享,但任何写操作须应用Copy-On-Write - * + * @brief + * 读共享区域,该区域中的页可以被两个进程之间读共享,但任何写操作须应用Copy-On-Write + * */ -#define REGION_RSHARED 0x1 +#define REGION_RSHARED 0x1 /** - * @brief 写共享区域,该区域中的页可以被两个进程之间读共享,任何的写操作无需执行Copy-On-Write - * + * @brief + * 写共享区域,该区域中的页可以被两个进程之间读共享,任何的写操作无需执行Copy-On-Write + * */ -#define REGION_WSHARED 0x2 +#define REGION_WSHARED 0x2 -#define REGION_PERM_MASK 0x1c +#define REGION_PERM_MASK 0x1c -#define REGION_READ (1 << 2) -#define REGION_WRITE (1 << 3) -#define REGION_EXEC (1 << 4) -#define REGION_RW REGION_READ | REGION_WRITE +#define REGION_READ (1 << 2) +#define REGION_WRITE (1 << 3) +#define REGION_EXEC (1 << 4) +#define REGION_RW REGION_READ | REGION_WRITE + +#define REGION_TYPE_CODE (1 << 16); +#define REGION_TYPE_GENERAL (2 << 16); +#define REGION_TYPE_HEAP (3 << 16); +#define REGION_TYPE_STACK (4 << 16); struct mm_region { diff --git a/lunaix-os/includes/lunaix/mm/page.h b/lunaix-os/includes/lunaix/mm/page.h index 76ea882..446e14e 100644 --- a/lunaix-os/includes/lunaix/mm/page.h +++ b/lunaix-os/includes/lunaix/mm/page.h @@ -98,7 +98,7 @@ typedef struct extern void __pg_mount_point; /* 四个页挂载点,两个页目录挂载点: 用于临时创建&编辑页表 */ - +#define PG_MOUNT_RANGE(l1_index) (701 <= l1_index && l1_index <= 703) #define PD_MOUNT_1 0xAFC00000 #define PD_MOUNT_2 0xAF800000 #define PG_MOUNT_BASE 0xAF7FF000 diff --git a/lunaix-os/includes/lunaix/mm/region.h b/lunaix-os/includes/lunaix/mm/region.h index 8da41d2..8271d2b 100644 --- a/lunaix-os/includes/lunaix/mm/region.h +++ b/lunaix-os/includes/lunaix/mm/region.h @@ -2,13 +2,20 @@ #define __LUNAIX_REGION_H #include -#include -void region_add(struct proc_info* proc, unsigned long start, unsigned long end, unsigned int attr); +void +region_add(struct mm_region** proc, + unsigned long start, + unsigned long end, + unsigned int attr); -void region_release_all(struct proc_info* proc); +void +region_release_all(struct mm_region** proc); -struct mm_region* region_get(struct proc_info* proc, unsigned long vaddr); +struct mm_region* +region_get(struct mm_region** proc, unsigned long vaddr); +void +region_copy(struct mm_region** src, struct mm_region** dest); #endif /* __LUNAIX_REGION_H */ diff --git a/lunaix-os/includes/lunaix/mm/vmm.h b/lunaix-os/includes/lunaix/mm/vmm.h index 5d88f3c..bd074c5 100644 --- a/lunaix-os/includes/lunaix/mm/vmm.h +++ b/lunaix-os/includes/lunaix/mm/vmm.h @@ -7,7 +7,16 @@ // Virtual memory manager #define VMAP_NULL 0 +/** + * @brief 映射模式:忽略已存在映射 + * + */ #define VMAP_IGNORE 1 +/** + * @brief 映射模式:不作实际映射。该功能用于预留出特定的地址空间 + * + */ +#define VMAP_NOMAP 2 /** * @brief 初始化虚拟内存管理器 @@ -69,6 +78,9 @@ vmm_lookup(uintptr_t va, v_mapping* mapping); void* vmm_dup_page(pid_t pid, void* pa); +void* +vmm_dup_vmspace(pid_t pid); + /** * @brief 挂载另一个虚拟地址空间至当前虚拟地址空间 * diff --git a/lunaix-os/includes/lunaix/process.h b/lunaix-os/includes/lunaix/process.h index 0b743d3..26f33bd 100644 --- a/lunaix-os/includes/lunaix/process.h +++ b/lunaix-os/includes/lunaix/process.h @@ -38,11 +38,22 @@ struct proc_sig struct proc_info { + /* + Any change to *critical section*, including layout, size + must be reflected in kernel/asm/x86/interrupt.S to avoid + disaster! + */ + + /* ---- critical section start ---- */ + pid_t pid; struct proc_info* parent; isr_param intr_ctx; // size=76 uintptr_t ustack_top; void* page_table; + + /* ---- critical section end ---- */ + struct llist_header siblings; struct llist_header children; struct llist_header grp_member; diff --git a/lunaix-os/kernel/asm/x86/pfault.c b/lunaix-os/kernel/asm/x86/pfault.c index 15f11ee..3ad640e 100644 --- a/lunaix-os/kernel/asm/x86/pfault.c +++ b/lunaix-os/kernel/asm/x86/pfault.c @@ -43,7 +43,7 @@ intr_routine_page_fault(const isr_param* param) goto segv_term; } - struct mm_region* hit_region = region_get(__current, ptr); + struct mm_region* hit_region = region_get(&__current->mm.regions, ptr); if (!hit_region) { // Into the void... diff --git a/lunaix-os/kernel/k_init.c b/lunaix-os/kernel/k_init.c index 7600caf..427ca51 100644 --- a/lunaix-os/kernel/k_init.c +++ b/lunaix-os/kernel/k_init.c @@ -80,15 +80,6 @@ _kernel_init() setup_memory((multiboot_memory_map_t*)_k_init_mb_info->mmap_addr, map_size); - // 为内核创建一个专属栈空间。 - for (size_t i = 0; i < (KSTACK_SIZE >> PG_SIZE_BITS); i++) { - uintptr_t pa = pmm_alloc_page(KERNEL_PID, 0); - vmm_set_mapping(PD_REFERENCED, - KSTACK_START + (i << PG_SIZE_BITS), - pa, - PG_PREM_RW, - VMAP_NULL); - } kprintf(KINFO "[MM] Allocated %d pages for stack start at %p\n", KSTACK_SIZE >> PG_SIZE_BITS, KSTACK_START); @@ -135,31 +126,37 @@ spawn_proc0() // 方案1:必须在读取eflags之后禁用。否则当进程被调度时,中断依然是关闭的! // cpu_disable_interrupt(); - setup_proc_mem(&proc0, PD_REFERENCED); - - // Ok... 首先fork进我们的零号进程,而后由那里,我们fork进init进程。 - /* - 这里是一些栈的设置,因为我们将切换到一个新的地址空间里,并且使用一个全新的栈。 - 让iret满意! - */ - asm volatile("movl %%cr3, %%eax\n" - "movl %%esp, %%ebx\n" - "movl %1, %%cr3\n" - "movl %2, %%esp\n" + /* Ok... 首先fork进我们的零号进程,而后由那里,我们fork进init进程。 */ + + // 把当前虚拟地址空间(内核)复制一份。 + proc0.page_table = vmm_dup_vmspace(proc0.pid); + + // 直接切换到新的拷贝,进行配置。 + cpu_lcr3(proc0.page_table); + + // 为内核创建一个专属栈空间。 + for (size_t i = 0; i < (KSTACK_SIZE >> PG_SIZE_BITS); i++) { + uintptr_t pa = pmm_alloc_page(KERNEL_PID, 0); + vmm_set_mapping(PD_REFERENCED, + KSTACK_START + (i << PG_SIZE_BITS), + pa, + PG_PREM_RW, + VMAP_NULL); + } + + // 手动设置进程上下文:用于第一次调度 + asm volatile("movl %%esp, %%ebx\n" + "movl %1, %%esp\n" "pushf\n" + "pushl %2\n" "pushl %3\n" - "pushl %4\n" "pushl $0\n" "pushl $0\n" "movl %%esp, %0\n" - "movl %%eax, %%cr3\n" "movl %%ebx, %%esp\n" : "=m"(proc0.intr_ctx.registers.esp) - : "r"(proc0.page_table), - "i"(KSTACK_TOP), - "i"(KCODE_SEG), - "r"(proc0.intr_ctx.eip) - : "%eax", "%ebx", "memory"); + : "i"(KSTACK_TOP), "i"(KCODE_SEG), "r"(proc0.intr_ctx.eip) + : "%ebx", "memory"); // 向调度器注册进程。 push_process(&proc0); diff --git a/lunaix-os/kernel/mm/kalloc.c b/lunaix-os/kernel/mm/kalloc.c index 3aba61e..d5116a2 100644 --- a/lunaix-os/kernel/mm/kalloc.c +++ b/lunaix-os/kernel/mm/kalloc.c @@ -13,6 +13,7 @@ */ #include #include +#include #include #include @@ -61,12 +62,22 @@ lx_grow_heap(heap_context_t* heap, size_t sz); // FIXME: This should be per-process but not global! static heap_context_t kheap; +#define KHEAP_SIZE_MB 256 + int kalloc_init() { kheap.start = &__kernel_heap_start; kheap.brk = NULL; - kheap.max_addr = (void*)KSTACK_START; + kheap.max_addr = (void*)((uintptr_t)kheap.start + (KHEAP_SIZE_MB << 20)); + + for (size_t i = 0; i < KHEAP_SIZE_MB >> 2; i++) { + vmm_set_mapping(PD_REFERENCED, + (uintptr_t)kheap.start + (i << 22), + 0, + PG_PREM_RW, + VMAP_NOMAP); + } if (!dmm_init(&kheap)) { return 0; diff --git a/lunaix-os/kernel/mm/region.c b/lunaix-os/kernel/mm/region.c index 1935583..d9fddeb 100644 --- a/lunaix-os/kernel/mm/region.c +++ b/lunaix-os/kernel/mm/region.c @@ -1,46 +1,64 @@ -#include #include -#include +#include -void region_add(struct proc_info* proc,unsigned long start, unsigned long end, unsigned int attr) { +void +region_add(struct mm_region** regions, + unsigned long start, + unsigned long end, + unsigned int attr) +{ struct mm_region* region = lxmalloc(sizeof(struct mm_region)); - *region = (struct mm_region) { - .attr = attr, - .end = end, - .start = start - }; + *region = (struct mm_region){ .attr = attr, .end = end, .start = start }; - if (!proc->mm.regions) { + if (!*regions) { llist_init_head(®ion->head); - proc->mm.regions = region; - } - else { - llist_append(&proc->mm.regions->head, ®ion->head); + *regions = region; + } else { + llist_append(&(*regions)->head, ®ion->head); } } -void region_release_all(struct proc_info* proc) { - struct mm_region* head = proc->mm.regions; +void +region_release_all(struct mm_region** regions) +{ struct mm_region *pos, *n; - llist_for_each(pos, n, &head->head, head) { + llist_for_each(pos, n, &(*regions)->head, head) + { lxfree(pos); } - proc->mm.regions = NULL; + *regions = NULL; +} + +void +region_copy(struct mm_region** src, struct mm_region** dest) +{ + if (!*src) { + return; + } + + struct mm_region *pos, *n; + + llist_init_head(*dest); + llist_for_each(pos, n, &(*src)->head, head) + { + region_add(dest, pos->start, pos->end, pos->attr); + } } -struct mm_region* region_get(struct proc_info* proc, unsigned long vaddr) { - struct mm_region* head = proc->mm.regions; - - if (!head) { +struct mm_region* +region_get(struct mm_region** regions, unsigned long vaddr) +{ + if (!*regions) { return NULL; } struct mm_region *pos, *n; - llist_for_each(pos, n, &head->head, head) { + llist_for_each(pos, n, &(*regions)->head, head) + { if (vaddr >= pos->start && vaddr < pos->end) { return pos; } diff --git a/lunaix-os/kernel/mm/vmm.c b/lunaix-os/kernel/mm/vmm.c index 1453c89..40e3724 100644 --- a/lunaix-os/kernel/mm/vmm.c +++ b/lunaix-os/kernel/mm/vmm.c @@ -64,6 +64,10 @@ vmm_set_mapping(uintptr_t mnt, cpu_invplg(va); } + if ((options & VMAP_NOMAP)) { + return 1; + } + l2pt->entry[l2_inx] = NEW_L2_ENTRY(attr, pa); return 1; } diff --git a/lunaix-os/kernel/process.c b/lunaix-os/kernel/process.c index c4e348f..ce491ed 100644 --- a/lunaix-os/kernel/process.c +++ b/lunaix-os/kernel/process.c @@ -22,6 +22,12 @@ __dup_pagetable(pid_t pid, uintptr_t mount_point) x86_page_table* pptd = (x86_page_table*)(mount_point | (0x3FF << 12)); for (size_t i = 0; i < PG_MAX_ENTRIES - 1; i++) { + // 没有必要拷贝临时挂载点 + if (PG_MOUNT_RANGE(i)) { + ptd->entry[i] = 0; + continue; + } + x86_pte_t ptde = pptd->entry[i]; if (!ptde || !(ptde & PG_PRESENT)) { ptd->entry[i] = ptde; @@ -77,7 +83,7 @@ __del_pagetable(pid_t pid, uintptr_t mount_point) } void* -dup_pagetable(pid_t pid) +vmm_dup_vmspace(pid_t pid) { return __dup_pagetable(pid, PD_REFERENCED); } @@ -138,6 +144,26 @@ init_proc(struct proc_info* pcb) pcb->pgid = pcb->pid; } +void +__mark_region(uintptr_t start_vpn, uintptr_t end_vpn, int attr) +{ + for (size_t i = start_vpn; i < end_vpn; i++) { + x86_pte_t* curproc = &PTE_MOUNTED(PD_REFERENCED, i); + x86_pte_t* newproc = &PTE_MOUNTED(PD_MOUNT_2, i); + cpu_invplg(newproc); + + if (attr == REGION_RSHARED) { + // 如果读共享,则将两者的都标注为只读,那么任何写入都将会应用COW策略。 + cpu_invplg(curproc); + *curproc = *curproc & ~PG_WRITE; + *newproc = *newproc & ~PG_WRITE; + } else { + // 如果是私有页,则将该页从新进程中移除。 + *newproc = 0; + } + } +} + pid_t dup_proc() { @@ -147,6 +173,8 @@ dup_proc() pcb.intr_ctx = __current->intr_ctx; pcb.parent = __current; + region_copy(&__current->mm.regions, &pcb.mm.regions); + #ifdef USE_KERNEL_PG setup_proc_mem(&pcb, PD_MOUNT_1); //挂载点#1是当前进程的页表 #else @@ -158,12 +186,9 @@ dup_proc() goto not_copy; } - llist_init_head(&pcb.mm.regions); struct mm_region *pos, *n; - llist_for_each(pos, n, &__current->mm.regions->head, head) + llist_for_each(pos, n, &pcb.mm.regions->head, head) { - region_add(&pcb, pos->start, pos->end, pos->attr); - // 如果写共享,则不作处理。 if ((pos->attr & REGION_WSHARED)) { continue; @@ -171,21 +196,7 @@ dup_proc() uintptr_t start_vpn = PG_ALIGN(pos->start) >> 12; uintptr_t end_vpn = PG_ALIGN(pos->end) >> 12; - for (size_t i = start_vpn; i < end_vpn; i++) { - x86_pte_t* curproc = &PTE_MOUNTED(PD_MOUNT_1, i); - x86_pte_t* newproc = &PTE_MOUNTED(PD_MOUNT_2, i); - cpu_invplg(newproc); - - if (pos->attr == REGION_RSHARED) { - // 如果读共享,则将两者的都标注为只读,那么任何写入都将会应用COW策略。 - cpu_invplg(curproc); - *curproc = *curproc & ~PG_WRITE; - *newproc = *newproc & ~PG_WRITE; - } else { - // 如果是私有页,则将该页从新进程中移除。 - *newproc = 0; - } - } + __mark_region(start_vpn, end_vpn, pos->attr); } not_copy: @@ -235,9 +246,9 @@ setup_proc_mem(struct proc_info* proc, uintptr_t usedMnt) // 定义用户栈区域,但是不分配实际的物理页。我们会在Page fault // handler里面实现动态分配物理页的逻辑。(虚拟内存的好处!) // FIXME: 这里应该放到spawn_proc里面。 - // region_add(proc, USTACK_END, USTACK_SIZE, REGION_PRIVATE | REGION_RW); + // region_add(proc, USTACK_END, USTACK_SIZE, REGION_PRIVATE | + // REGION_RW); // 至于其他的区域我们暂时没有办法知道,因为那需要知道用户程序的信息。我们留到之后在处理。 - proc->page_table = pt_copy; } \ No newline at end of file -- 2.27.0