From 54fe61ad97673faa4e6af26a617d88f4b565c48c Mon Sep 17 00:00:00 2001 From: Steven Kang Date: Thu, 7 Jul 2022 16:29:31 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20070=20=E7=B3=BB=E7=BB=9F=E8=B0=83?= =?UTF-8?q?=E7=94=A8=20fork?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...7\273\237\350\260\203\347\224\250 fork.md" | 8 +++ src/include/onix/syscall.h | 4 ++ src/include/onix/task.h | 2 + src/kernel/gate.c | 2 + src/kernel/memory.c | 72 +++++++++++++++++++ src/kernel/task.c | 59 +++++++++++++++ src/kernel/thread.c | 17 +++-- src/lib/syscall.c | 5 ++ 8 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 "docs/08 \347\263\273\347\273\237\350\260\203\347\224\250/070 \347\263\273\347\273\237\350\260\203\347\224\250 fork.md" diff --git "a/docs/08 \347\263\273\347\273\237\350\260\203\347\224\250/070 \347\263\273\347\273\237\350\260\203\347\224\250 fork.md" "b/docs/08 \347\263\273\347\273\237\350\260\203\347\224\250/070 \347\263\273\347\273\237\350\260\203\347\224\250 fork.md" new file mode 100644 index 00000000..b8c7b6fa --- /dev/null +++ "b/docs/08 \347\263\273\347\273\237\350\260\203\347\224\250/070 \347\263\273\347\273\237\350\260\203\347\224\250 fork.md" @@ -0,0 +1,8 @@ +# 系统调用 fork + +```c++ +pid_t fork(void); // 创建子进程 +``` + +- 子进程返回值为 0 +- 父进程返回值为子进程 id diff --git a/src/include/onix/syscall.h b/src/include/onix/syscall.h index f8ecc229..5a3140b7 100644 --- a/src/include/onix/syscall.h +++ b/src/include/onix/syscall.h @@ -6,6 +6,7 @@ typedef enum syscall_t { SYS_NR_TEST, + SYS_NR_FORK = 2, SYS_NR_WRITE = 4, SYS_NR_GETPID = 20, SYS_NR_BRK = 45, @@ -15,6 +16,9 @@ typedef enum syscall_t } syscall_t; u32 test(); + +pid_t fork(); + void yield(); void sleep(u32 ms); diff --git a/src/include/onix/task.h b/src/include/onix/task.h index 99cfa6b7..dd996260 100644 --- a/src/include/onix/task.h +++ b/src/include/onix/task.h @@ -84,6 +84,8 @@ typedef struct intr_frame_t task_t *running_task(); void schedule(); +pid_t task_fork(); + void task_yield(); void task_block(task_t *task, list_t *blist, task_state_t state); void task_unblock(task_t *task); diff --git a/src/kernel/gate.c b/src/kernel/gate.c index 843dc810..12c70b52 100644 --- a/src/kernel/gate.c +++ b/src/kernel/gate.c @@ -50,6 +50,8 @@ void syscall_init() } syscall_table[SYS_NR_TEST] = sys_test; + syscall_table[SYS_NR_FORK] = task_fork; + syscall_table[SYS_NR_SLEEP] = task_sleep; syscall_table[SYS_NR_YIELD] = task_yield; diff --git a/src/kernel/memory.c b/src/kernel/memory.c index a0f2967a..927eb82b 100644 --- a/src/kernel/memory.c +++ b/src/kernel/memory.c @@ -422,10 +422,24 @@ void unlink_page(u32 vaddr) flush_tlb(vaddr); } +// 拷贝一页,返回拷贝后的物理地址 +static u32 copy_page(void *page) +{ + u32 paddr = get_page(); + + page_entry_t *entry = get_pte(0, false); + entry_init(entry, IDX(paddr)); + memcpy((void *)0, (void *)page, PAGE_SIZE); + + entry->present = false; + return paddr; +} + // 拷贝当前页目录 page_entry_t *copy_pde() { task_t *task = running_task(); + page_entry_t *pde = (page_entry_t *)alloc_kpage(1); // todo free memcpy(pde, (void *)task->pde, PAGE_SIZE); @@ -433,6 +447,38 @@ page_entry_t *copy_pde() page_entry_t *entry = &pde[1023]; entry_init(entry, IDX(pde)); + page_entry_t *dentry; + + for (size_t didx = 2; didx < 1023; didx++) + { + dentry = &pde[didx]; + if (!dentry->present) + continue; + + page_entry_t *pte = (page_entry_t *)(PDE_MASK | (didx << 12)); + + for (size_t tidx = 0; tidx < 1024; tidx++) + { + entry = &pte[tidx]; + if (!entry->present) + continue; + + // 对应物理内存引用大于 0 + assert(memory_map[entry->index] > 0); + // 置为只读 + entry->write = false; + // 对应物理页引用加 1 + memory_map[entry->index]++; + + assert(memory_map[entry->index] < 255); + } + + u32 paddr = copy_page(pte); + dentry->index = IDX(paddr); + } + + set_cr3(task->pde); + return pde; } @@ -496,6 +542,32 @@ void page_fault( assert(KERNEL_MEMORY_SIZE <= vaddr < USER_STACK_TOP); + if (code->present) + { + assert(code->write); + + page_entry_t *pte = get_pte(vaddr, false); + page_entry_t *entry = &pte[TIDX(vaddr)]; + + assert(entry->present); + assert(memory_map[entry->index] > 0); + if (memory_map[entry->index] == 1) + { + entry->write = true; + LOGK("WRITE page for 0x%p\n", vaddr); + } + else + { + void *page = (void *)PAGE(IDX(vaddr)); + u32 paddr = copy_page(page); + memory_map[entry->index]--; + entry_init(entry, IDX(paddr)); + flush_tlb(vaddr); + LOGK("COPY page for 0x%p\n", vaddr); + } + return; + } + if (!code->present && (vaddr < task->brk || vaddr >= USER_STACK_BOTTOM)) { u32 page = PAGE(IDX(vaddr)); diff --git a/src/kernel/task.c b/src/kernel/task.c index 9f9a768e..145ff2ef 100644 --- a/src/kernel/task.c +++ b/src/kernel/task.c @@ -322,6 +322,65 @@ void task_to_user_mode(target_t target) "jmp interrupt_exit\n" ::"m"(iframe)); } +extern void interrupt_exit(); + +static void task_build_stack(task_t *task) +{ + u32 addr = (u32)task + PAGE_SIZE; + addr -= sizeof(intr_frame_t); + intr_frame_t *iframe = (intr_frame_t *)addr; + iframe->eax = 0; + + addr -= sizeof(task_frame_t); + task_frame_t *frame = (task_frame_t *)addr; + + frame->ebp = 0xaa55aa55; + frame->ebx = 0xaa55aa55; + frame->edi = 0xaa55aa55; + frame->esi = 0xaa55aa55; + + frame->eip = interrupt_exit; + + task->stack = (u32 *)frame; +} + +pid_t task_fork() +{ + // LOGK("fork is called\n"); + task_t *task = running_task(); + + // 当前进程没有阻塞,且正在执行 + assert(task->node.next == NULL && task->node.prev == NULL && task->state == TASK_RUNNING); + + // 拷贝内核栈 和 PCB + task_t *child = get_free_task(); + pid_t pid = child->pid; + memcpy(child, task, PAGE_SIZE); + + child->pid = pid; + child->ppid = task->pid; + child->ticks = child->priority; + child->state = TASK_READY; + + // 拷贝用户进程虚拟内存位图 + child->vmap = kmalloc(sizeof(bitmap_t)); // todo kfree + memcpy(child->vmap, task->vmap, sizeof(bitmap_t)); + + // 拷贝虚拟位图缓存 + void *buf = (void *)alloc_kpage(1); // todo free_kpage + memcpy(buf, task->vmap->bits, PAGE_SIZE); + child->vmap->bits = buf; + + // 拷贝页目录 + child->pde = (u32)copy_pde(); + + // 构造 child 内核栈 + task_build_stack(child); // ROP + // schedule(); + + return child->pid; +} + static void task_setup() { task_t *task = running_task(); diff --git a/src/kernel/thread.c b/src/kernel/thread.c index 7c1c54d9..ee5c14f3 100644 --- a/src/kernel/thread.c +++ b/src/kernel/thread.c @@ -29,13 +29,23 @@ static void user_init_thread() { u32 counter = 0; - char ch; while (true) { // test(); - printf("init thread %d %d %d...\n", getpid(), getppid(), counter++); + // printf("init thread %d %d %d...\n", getpid(), getppid(), counter++); // printf("task is in user mode %d\n", counter++); - sleep(1000); + pid_t pid = fork(); + + if (pid) + { + printf("fork after parent %d, %d, %d\n", pid, getpid(), getppid()); + } + else + { + printf("fork after child %d, %d, %d\n", pid, getpid(), getppid()); + } + hang(); + sleep(100); } } @@ -53,7 +63,6 @@ void test_thread() while (true) { - printf("test thread %d %d %d...\n", getpid(), getppid(), counter++); // LOGK("test task %d....\n", counter++); // BMB; sleep(2000); diff --git a/src/lib/syscall.c b/src/lib/syscall.c index e9cd3ac3..36588ed7 100644 --- a/src/lib/syscall.c +++ b/src/lib/syscall.c @@ -45,6 +45,11 @@ u32 test() return _syscall0(SYS_NR_TEST); } +pid_t fork() +{ + return _syscall0(SYS_NR_FORK); +} + void yield() { _syscall0(SYS_NR_YIELD);