1 #include <lunaix/mm/region.h>
2 #include <lunaix/mm/valloc.h>
3 #include <lunaix/mm/vmm.h>
4 #include <lunaix/mm/pmm.h>
5 #include <lunaix/mm/mmap.h>
6 #include <lunaix/process.h>
7 #include <lunaix/spike.h>
8 #include <lunaix/status.h>
9 #include <lunaix/syscall.h>
10 #include <lunaix/syslog.h>
11 #include <lunaix/signal.h>
14 #include <sys/mm/mm_defs.h>
16 #include <klibc/string.h>
21 region_maybe_cow(struct mm_region* region)
23 int attr = region->attr;
24 if ((attr & REGION_WSHARED)) {
28 ptr_t start_vpn = PN(region->start);
29 ptr_t end_vpn = PN(region->end);
30 for (size_t i = start_vpn; i <= end_vpn; i++) {
31 x86_pte_t* curproc = &PTE_MOUNTED(VMS_SELF, i);
32 x86_pte_t* newproc = &PTE_MOUNTED(VMS_MOUNT_1, i);
34 cpu_flush_page((ptr_t)newproc);
36 if ((attr & REGION_MODE_MASK) == REGION_RSHARED) {
37 // 如果读共享,则将两者的都标注为只读,那么任何写入都将会应用COW策略。
38 cpu_flush_page((ptr_t)curproc);
39 cpu_flush_page((ptr_t)(i << 12));
41 *curproc = *curproc & ~PG_WRITE;
42 *newproc = *newproc & ~PG_WRITE;
44 // 如果是私有页,则将该页从新进程中移除。
51 __dup_fdtable(struct proc_info* pcb)
53 for (size_t i = 0; i < VFS_MAX_FD; i++) {
54 struct v_fd* fd = __current->fdtable->fds[i];
57 vfs_dup_fd(fd, &pcb->fdtable->fds[i]);
63 __dup_kernel_stack(struct thread* thread, ptr_t vm_mnt)
65 ptr_t kstack_pn = PN(current_thread->kstack);
67 // copy the kernel stack
68 for (size_t i = 0; i < PN(KSTACK_SIZE); i++) {
69 volatile x86_pte_t* orig_ppte = &PTE_MOUNTED(VMS_SELF, kstack_pn);
70 x86_pte_t p = *orig_ppte;
71 ptr_t kstack = kstack_pn * PG_SIZE;
73 if (guardian_page(p)) {
74 vmm_set_mapping(vm_mnt, kstack, 0, 0, VMAP_GUARDPAGE);
76 ptr_t ppa = vmm_dup_page(PG_ENTRY_ADDR(p));
77 vmm_set_mapping(vm_mnt, kstack, ppa, p & 0xfff, 0);
85 Duplicate the current active thread to the forked process's
88 This is not the same as "fork a thread within the same
89 process". In fact, it is impossible to do such "thread forking"
90 as the new forked thread's kernel and user stack must not
91 coincide with the original thread (because the same vm space)
92 thus all reference to the stack space are staled which could
93 lead to undefined behaviour.
98 dup_active_thread(ptr_t vm_mnt, struct proc_info* duped_pcb)
100 struct thread* th = alloc_thread(duped_pcb);
105 th->intr_ctx = current_thread->intr_ctx;
106 th->kstack = current_thread->kstack;
108 signal_dup_context(&th->sigctx);
111 * store the return value for forked process.
112 * this will be implicit carried over after kernel stack is copied.
114 store_retval_to(th, 0);
116 __dup_kernel_stack(th, vm_mnt);
118 if (!current_thread->ustack) {
122 struct mm_region* old_stack = current_thread->ustack;
123 struct mm_region *pos, *n;
124 llist_for_each(pos, n, vmregions(duped_pcb), head)
126 // remove stack of other threads.
127 if (!stack_region(pos)) {
131 if (!same_region(pos, old_stack)) {
132 mem_unmap_region(vm_mnt, pos);
148 struct proc_info* pcb = alloc_process();
150 syscall_result(ENOMEM);
154 pcb->parent = __current;
156 // FIXME need a more elagent refactoring
157 if (__current->cmd) {
158 pcb->cmd_len = __current->cmd_len;
159 pcb->cmd = valloc(pcb->cmd_len);
160 memcpy(pcb->cmd, __current->cmd, pcb->cmd_len);
163 if (__current->cwd) {
164 pcb->cwd = __current->cwd;
165 vfs_ref_dnode(pcb->cwd);
171 vmm_mount_pd(VMS_MOUNT_1, vmroot(pcb));
173 struct thread* main_thread = dup_active_thread(VMS_MOUNT_1, pcb);
175 syscall_result(ENOMEM);
176 vmm_unmount_pd(VMS_MOUNT_1);
181 // 根据 mm_region 进一步配置页表
182 struct mm_region *pos, *n;
183 llist_for_each(pos, n, &pcb->mm->regions, head)
185 region_maybe_cow(pos);
188 vmm_unmount_pd(VMS_MOUNT_1);
191 commit_thread(main_thread);
196 __DEFINE_LXSYSCALL(pid_t, fork)