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>
13 #include <sys/mm/mm_defs.h>
15 #include <klibc/string.h>
20 region_maybe_cow(struct mm_region* region)
22 int attr = region->attr;
23 if ((attr & REGION_WSHARED)) {
27 pfn_t start_pn = pfn(region->start);
28 pfn_t end_pn = pfn(region->end);
30 for (size_t i = start_pn; i <= end_pn; i++) {
31 pte_t* self = mkptep_pn(VMS_SELF, i);
32 pte_t* guest = mkptep_pn(VMS_MOUNT_1, i);
33 ptr_t va = page_addr(ptep_pfn(self));
35 if ((attr & REGION_MODE_MASK) == REGION_RSHARED) {
36 set_pte(self, pte_mkwprotect(*self));
37 set_pte(guest, pte_mkwprotect(*guest));
39 // 如果是私有页,则将该页从新进程中移除。
40 set_pte(guest, null_pte);
44 tlb_flush_vmr_all(region);
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 struct leaflet* leaflet;
64 ptr_t kstack_pn = pfn(current_thread->kstack);
65 kstack_pn -= pfn(KSTACK_SIZE) - 1;
67 // copy the kernel stack
68 pte_t* src_ptep = mkptep_pn(VMS_SELF, kstack_pn);
69 pte_t* dest_ptep = mkptep_pn(vm_mnt, kstack_pn);
70 for (size_t i = 0; i < pfn(KSTACK_SIZE); i++) {
73 if (pte_isguardian(p)) {
74 set_pte(dest_ptep, guard_pte);
76 leaflet = dup_leaflet(pte_leaflet(p));
77 i += ptep_map_leaflet(dest_ptep, p, leaflet);
84 struct proc_mm* mm = vmspace(thread->process);
85 tlb_flush_mm_range(mm, kstack_pn, leaf_count(KSTACK_SIZE));
89 Duplicate the current active thread to the forked process's
92 This is not the same as "fork a thread within the same
93 process". In fact, it is impossible to do such "thread forking"
94 as the new forked thread's kernel and user stack must not
95 coincide with the original thread (because the same vm space)
96 thus all reference to the stack space are staled which could
97 lead to undefined behaviour.
101 static struct thread*
102 dup_active_thread(ptr_t vm_mnt, struct proc_info* duped_pcb)
104 struct thread* th = alloc_thread(duped_pcb);
109 th->intr_ctx = current_thread->intr_ctx;
110 th->kstack = current_thread->kstack;
112 signal_dup_context(&th->sigctx);
115 * store the return value for forked process.
116 * this will be implicit carried over after kernel stack is copied.
118 store_retval_to(th, 0);
120 __dup_kernel_stack(th, vm_mnt);
122 if (!current_thread->ustack) {
126 struct mm_region* old_stack = current_thread->ustack;
127 struct mm_region *pos, *n;
128 llist_for_each(pos, n, vmregions(duped_pcb), head)
130 // remove stack of other threads.
131 if (!stack_region(pos)) {
135 if (!same_region(pos, old_stack)) {
136 mem_unmap_region(vm_mnt, pos);
152 // FIXME need investigate: issue with fork, as well as pthread
153 // especially when involving frequent alloc and dealloc ops
154 // (could be issue in allocator's segregated free list)
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)