1 #include <lunaix/mm/region.h>
2 #include <lunaix/mm/valloc.h>
3 #include <lunaix/mm/page.h>
4 #include <lunaix/mm/mmap.h>
5 #include <lunaix/process.h>
6 #include <lunaix/spike.h>
7 #include <lunaix/status.h>
8 #include <lunaix/syscall.h>
9 #include <lunaix/syslog.h>
10 #include <lunaix/signal.h>
11 #include <lunaix/kpreempt.h>
14 #include <asm/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);
34 ptr_t va = page_addr(ptep_pfn(self));
36 if ((attr & REGION_MODE_MASK) == REGION_RSHARED) {
37 set_pte(self, pte_mkwprotect(*self));
38 set_pte(guest, pte_mkwprotect(*guest));
40 // 如果是私有页,则将该页从新进程中移除。
41 set_pte(guest, null_pte);
45 tlb_flush_vmr_all(region);
49 __dup_kernel_stack(struct thread* thread, ptr_t vm_mnt)
51 struct leaflet* leaflet;
53 ptr_t kstack_pn = pfn(current_thread->kstack);
54 kstack_pn -= pfn(KSTACK_SIZE);
56 // copy the kernel stack
57 pte_t* src_ptep = mkptep_pn(VMS_SELF, kstack_pn);
58 pte_t* dest_ptep = mkptep_pn(vm_mnt, kstack_pn);
59 for (size_t i = 0; i <= pfn(KSTACK_SIZE); i++) {
62 if (pte_isguardian(p)) {
63 set_pte(dest_ptep, guard_pte);
65 leaflet = dup_leaflet(pte_leaflet(p));
66 i += ptep_map_leaflet(dest_ptep, p, leaflet);
73 struct proc_mm* mm = vmspace(thread->process);
74 tlb_flush_mm_range(mm, kstack_pn, leaf_count(KSTACK_SIZE));
78 Duplicate the current active thread to the forked process's
81 This is not the same as "fork a thread within the same
82 process". In fact, it is impossible to do such "thread forking"
83 as the new forked thread's kernel and user stack must not
84 coincide with the original thread (because the same vm space)
85 thus all reference to the stack space are staled which could
86 lead to undefined behaviour.
91 dup_active_thread(ptr_t vm_mnt, struct proc_info* duped_pcb)
93 struct thread* th = alloc_thread(duped_pcb);
98 th->hstate = current_thread->hstate;
99 th->kstack = current_thread->kstack;
101 signal_dup_context(&th->sigctx);
104 * store the return value for forked process.
105 * this will be implicit carried over after kernel stack is copied.
107 store_retval_to(th, 0);
109 __dup_kernel_stack(th, vm_mnt);
111 if (!current_thread->ustack) {
115 struct mm_region* old_stack = current_thread->ustack;
116 struct mm_region *pos, *n;
117 llist_for_each(pos, n, vmregions(duped_pcb), head)
119 // remove stack of other threads.
120 if (!stack_region(pos)) {
124 if (!same_region(pos, old_stack)) {
125 mem_unmap_region(vm_mnt, pos);
143 struct proc_info* pcb = alloc_process();
145 syscall_result(ENOMEM);
149 pcb->parent = __current;
151 // FIXME need a more elagent refactoring
152 if (__current->cmd) {
153 pcb->cmd_len = __current->cmd_len;
154 pcb->cmd = valloc(pcb->cmd_len);
155 memcpy(pcb->cmd, __current->cmd, pcb->cmd_len);
158 if (__current->cwd) {
159 pcb->cwd = __current->cwd;
160 vfs_ref_dnode(pcb->cwd);
163 fdtable_copy(pcb->fdtable, __current->fdtable);
164 uscope_copy(&pcb->uscope, current_user_scope());
166 struct proc_mm* mm = vmspace(pcb);
167 procvm_dupvms_mount(mm);
169 struct thread* main_thread = dup_active_thread(mm->vm_mnt, pcb);
171 syscall_result(ENOMEM);
177 // 根据 mm_region 进一步配置页表
178 struct mm_region *pos, *n;
179 llist_for_each(pos, n, &pcb->mm->regions, head)
181 region_maybe_cow(pos);
187 commit_thread(main_thread);
192 __DEFINE_LXSYSCALL(pid_t, fork)