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 pfn_t start_pn = pfn(region->start);
29 pfn_t end_pn = pfn(region->end);
31 for (size_t i = start_pn; i <= end_pn; i++) {
32 pte_t* self = mkptep_pn(VMS_SELF, i);
33 pte_t* guest = mkptep_pn(VMS_MOUNT_1, i);
35 cpu_flush_page(page_addr(ptep_pfn(self)));
37 if ((attr & REGION_MODE_MASK) == REGION_RSHARED) {
38 set_pte(self, pte_mkwprotect(*self));
39 set_pte(guest, pte_mkwprotect(*guest));
41 // 如果是私有页,则将该页从新进程中移除。
42 set_pte(guest, null_pte);
48 __dup_fdtable(struct proc_info* pcb)
50 for (size_t i = 0; i < VFS_MAX_FD; i++) {
51 struct v_fd* fd = __current->fdtable->fds[i];
54 vfs_dup_fd(fd, &pcb->fdtable->fds[i]);
60 __dup_kernel_stack(struct thread* thread, ptr_t vm_mnt)
62 ptr_t kstack_pn = pfn(current_thread->kstack);
63 kstack_pn -= pfn(KSTACK_SIZE) - 1;
65 // copy the kernel stack
66 pte_t* src_ptep = mkptep_pn(VMS_SELF, kstack_pn);
67 pte_t* dest_ptep = mkptep_pn(vm_mnt, kstack_pn);
68 for (size_t i = 0; i < pfn(KSTACK_SIZE); i++) {
71 if (pte_isguardian(p)) {
72 set_pte(dest_ptep, guard_pte);
74 ptr_t ppa = vmm_dup_page(pte_paddr(p));
75 set_pte(dest_ptep, pte_setpaddr(p, ppa));
84 Duplicate the current active thread to the forked process's
87 This is not the same as "fork a thread within the same
88 process". In fact, it is impossible to do such "thread forking"
89 as the new forked thread's kernel and user stack must not
90 coincide with the original thread (because the same vm space)
91 thus all reference to the stack space are staled which could
92 lead to undefined behaviour.
97 dup_active_thread(ptr_t vm_mnt, struct proc_info* duped_pcb)
99 struct thread* th = alloc_thread(duped_pcb);
104 th->intr_ctx = current_thread->intr_ctx;
105 th->kstack = current_thread->kstack;
107 signal_dup_context(&th->sigctx);
110 * store the return value for forked process.
111 * this will be implicit carried over after kernel stack is copied.
113 store_retval_to(th, 0);
115 __dup_kernel_stack(th, vm_mnt);
117 if (!current_thread->ustack) {
121 struct mm_region* old_stack = current_thread->ustack;
122 struct mm_region *pos, *n;
123 llist_for_each(pos, n, vmregions(duped_pcb), head)
125 // remove stack of other threads.
126 if (!stack_region(pos)) {
130 if (!same_region(pos, old_stack)) {
131 mem_unmap_region(vm_mnt, pos);
147 struct proc_info* pcb = alloc_process();
149 syscall_result(ENOMEM);
153 pcb->parent = __current;
155 // FIXME need a more elagent refactoring
156 if (__current->cmd) {
157 pcb->cmd_len = __current->cmd_len;
158 pcb->cmd = valloc(pcb->cmd_len);
159 memcpy(pcb->cmd, __current->cmd, pcb->cmd_len);
162 if (__current->cwd) {
163 pcb->cwd = __current->cwd;
164 vfs_ref_dnode(pcb->cwd);
169 struct proc_mm* mm = vmspace(pcb);
170 procvm_dupvms_mount(mm);
172 struct thread* main_thread = dup_active_thread(VMS_MOUNT_1, pcb);
174 syscall_result(ENOMEM);
180 // 根据 mm_region 进一步配置页表
181 struct mm_region *pos, *n;
182 llist_for_each(pos, n, &pcb->mm->regions, head)
184 region_maybe_cow(pos);
190 commit_thread(main_thread);
195 __DEFINE_LXSYSCALL(pid_t, fork)