refactor: put a limit on kernel heap.
refactor: reduce overhead on kernel stack creation when we fork into our first process.
#include <lunaix/ds/llist.h>
#include <lunaix/ds/mutex.h>
-typedef struct
+typedef struct
{
void* start;
void* brk;
/**
* @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
{
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
#define __LUNAIX_REGION_H
#include <lunaix/mm/mm.h>
-#include <lunaix/process.h>
-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 */
// Virtual memory manager
#define VMAP_NULL 0
+/**
+ * @brief 映射模式:忽略已存在映射
+ *
+ */
#define VMAP_IGNORE 1
+/**
+ * @brief 映射模式:不作实际映射。该功能用于预留出特定的地址空间
+ *
+ */
+#define VMAP_NOMAP 2
/**
* @brief 初始化虚拟内存管理器
void*
vmm_dup_page(pid_t pid, void* pa);
+void*
+vmm_dup_vmspace(pid_t pid);
+
/**
* @brief 挂载另一个虚拟地址空间至当前虚拟地址空间
*
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;
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...
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);
// 方案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);
*/
#include <lunaix/mm/dmm.h>
#include <lunaix/mm/kalloc.h>
+#include <lunaix/mm/vmm.h>
#include <lunaix/common.h>
#include <lunaix/spike.h>
// 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;
-#include <lunaix/mm/region.h>
#include <lunaix/mm/kalloc.h>
-#include <lunaix/process.h>
+#include <lunaix/mm/region.h>
-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;
}
cpu_invplg(va);
}
+ if ((options & VMAP_NOMAP)) {
+ return 1;
+ }
+
l2pt->entry[l2_inx] = NEW_L2_ENTRY(attr, pa);
return 1;
}
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;
}
void*
-dup_pagetable(pid_t pid)
+vmm_dup_vmspace(pid_t pid)
{
return __dup_pagetable(pid, PD_REFERENCED);
}
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()
{
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
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;
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:
// 定义用户栈区域,但是不分配实际的物理页。我们会在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