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 <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);
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_fdtable(struct proc_info* pcb)
51 for (size_t i = 0; i < VFS_MAX_FD; i++) {
52 struct v_fd* fd = __current->fdtable->fds[i];
55 vfs_dup_fd(fd, &pcb->fdtable->fds[i]);
61 __dup_kernel_stack(struct thread* thread, ptr_t vm_mnt)
63 struct leaflet* leaflet;
65 ptr_t kstack_pn = pfn(current_thread->kstack);
66 kstack_pn -= pfn(KSTACK_SIZE);
68 // copy the kernel stack
69 pte_t* src_ptep = mkptep_pn(VMS_SELF, kstack_pn);
70 pte_t* dest_ptep = mkptep_pn(vm_mnt, kstack_pn);
71 for (size_t i = 0; i <= pfn(KSTACK_SIZE); i++) {
74 if (pte_isguardian(p)) {
75 set_pte(dest_ptep, guard_pte);
77 leaflet = dup_leaflet(pte_leaflet(p));
78 i += ptep_map_leaflet(dest_ptep, p, leaflet);
85 struct proc_mm* mm = vmspace(thread->process);
86 tlb_flush_mm_range(mm, kstack_pn, leaf_count(KSTACK_SIZE));
90 Duplicate the current active thread to the forked process's
93 This is not the same as "fork a thread within the same
94 process". In fact, it is impossible to do such "thread forking"
95 as the new forked thread's kernel and user stack must not
96 coincide with the original thread (because the same vm space)
97 thus all reference to the stack space are staled which could
98 lead to undefined behaviour.
102 static struct thread*
103 dup_active_thread(ptr_t vm_mnt, struct proc_info* duped_pcb)
105 struct thread* th = alloc_thread(duped_pcb);
110 th->hstate = current_thread->hstate;
111 th->kstack = current_thread->kstack;
113 signal_dup_context(&th->sigctx);
116 * store the return value for forked process.
117 * this will be implicit carried over after kernel stack is copied.
119 store_retval_to(th, 0);
121 __dup_kernel_stack(th, vm_mnt);
123 if (!current_thread->ustack) {
127 struct mm_region* old_stack = current_thread->ustack;
128 struct mm_region *pos, *n;
129 llist_for_each(pos, n, vmregions(duped_pcb), head)
131 // remove stack of other threads.
132 if (!stack_region(pos)) {
136 if (!same_region(pos, old_stack)) {
137 mem_unmap_region(vm_mnt, pos);
155 struct proc_info* pcb = alloc_process();
157 syscall_result(ENOMEM);
161 pcb->parent = __current;
163 // FIXME need a more elagent refactoring
164 if (__current->cmd) {
165 pcb->cmd_len = __current->cmd_len;
166 pcb->cmd = valloc(pcb->cmd_len);
167 memcpy(pcb->cmd, __current->cmd, pcb->cmd_len);
170 if (__current->cwd) {
171 pcb->cwd = __current->cwd;
172 vfs_ref_dnode(pcb->cwd);
177 struct proc_mm* mm = vmspace(pcb);
178 procvm_dupvms_mount(mm);
180 struct thread* main_thread = dup_active_thread(mm->vm_mnt, pcb);
182 syscall_result(ENOMEM);
188 // 根据 mm_region 进一步配置页表
189 struct mm_region *pos, *n;
190 llist_for_each(pos, n, &pcb->mm->regions, head)
192 region_maybe_cow(pos);
198 commit_thread(main_thread);
203 __DEFINE_LXSYSCALL(pid_t, fork)