mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 22:02:44 +00:00 
			
		
		
		
	 da24a937f5
			
		
	
	
		da24a937f5
		
	
	
	
	
		
			
			Now that AddressSpace itself is always SpinlockProtected, we don't need to also wrap the RegionTree. Whoever has the AddressSpace locked is free to poke around its tree.
		
			
				
	
	
		
			156 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			156 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #include <Kernel/Debug.h>
 | |
| #include <Kernel/FileSystem/Custody.h>
 | |
| #include <Kernel/Memory/Region.h>
 | |
| #include <Kernel/PerformanceManager.h>
 | |
| #include <Kernel/Process.h>
 | |
| #include <Kernel/Scheduler.h>
 | |
| 
 | |
| namespace Kernel {
 | |
| 
 | |
| ErrorOr<FlatPtr> Process::sys$fork(RegisterState& regs)
 | |
| {
 | |
|     VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this);
 | |
|     TRY(require_promise(Pledge::proc));
 | |
|     LockRefPtr<Thread> child_first_thread;
 | |
| 
 | |
|     ArmedScopeGuard thread_finalizer_guard = [&child_first_thread]() {
 | |
|         SpinlockLocker lock(g_scheduler_lock);
 | |
|         if (child_first_thread) {
 | |
|             child_first_thread->detach();
 | |
|             child_first_thread->set_state(Thread::State::Dying);
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     auto child_name = TRY(m_name->try_clone());
 | |
|     auto credentials = this->credentials();
 | |
|     auto child = TRY(Process::try_create(child_first_thread, move(child_name), credentials->uid(), credentials->gid(), pid(), m_is_kernel_process, current_directory(), executable(), m_tty, this));
 | |
| 
 | |
|     // NOTE: All user processes have a leaked ref on them. It's balanced by Thread::WaitBlockerSet::finalize().
 | |
|     child->ref();
 | |
| 
 | |
|     TRY(m_unveil_data.with([&](auto& parent_unveil_data) -> ErrorOr<void> {
 | |
|         return child->m_unveil_data.with([&](auto& child_unveil_data) -> ErrorOr<void> {
 | |
|             child_unveil_data.state = parent_unveil_data.state;
 | |
|             child_unveil_data.paths = TRY(parent_unveil_data.paths.deep_copy());
 | |
|             return {};
 | |
|         });
 | |
|     }));
 | |
| 
 | |
|     TRY(child->m_fds.with_exclusive([&](auto& child_fds) {
 | |
|         return m_fds.with_exclusive([&](auto& parent_fds) {
 | |
|             return child_fds.try_clone(parent_fds);
 | |
|         });
 | |
|     }));
 | |
| 
 | |
|     child->m_pg = m_pg;
 | |
| 
 | |
|     with_protected_data([&](auto& my_protected_data) {
 | |
|         child->with_mutable_protected_data([&](auto& child_protected_data) {
 | |
|             child_protected_data.promises = my_protected_data.promises.load();
 | |
|             child_protected_data.execpromises = my_protected_data.execpromises.load();
 | |
|             child_protected_data.has_promises = my_protected_data.has_promises.load();
 | |
|             child_protected_data.has_execpromises = my_protected_data.has_execpromises.load();
 | |
|             child_protected_data.sid = my_protected_data.sid;
 | |
|             child_protected_data.credentials = my_protected_data.credentials;
 | |
|             child_protected_data.umask = my_protected_data.umask;
 | |
|             child_protected_data.signal_trampoline = my_protected_data.signal_trampoline;
 | |
|             child_protected_data.dumpable = my_protected_data.dumpable;
 | |
|         });
 | |
|     });
 | |
| 
 | |
|     dbgln_if(FORK_DEBUG, "fork: child={}", child);
 | |
| 
 | |
|     // A child created via fork(2) inherits a copy of its parent's signal mask
 | |
|     child_first_thread->update_signal_mask(Thread::current()->signal_mask());
 | |
| 
 | |
|     // A child process created via fork(2) inherits a copy of its parent's alternate signal stack settings.
 | |
|     child_first_thread->m_alternative_signal_stack = Thread::current()->m_alternative_signal_stack;
 | |
|     child_first_thread->m_alternative_signal_stack_size = Thread::current()->m_alternative_signal_stack_size;
 | |
| 
 | |
| #if ARCH(I386)
 | |
|     auto& child_regs = child_first_thread->m_regs;
 | |
|     child_regs.eax = 0; // fork() returns 0 in the child :^)
 | |
|     child_regs.ebx = regs.ebx;
 | |
|     child_regs.ecx = regs.ecx;
 | |
|     child_regs.edx = regs.edx;
 | |
|     child_regs.ebp = regs.ebp;
 | |
|     child_regs.esp = regs.userspace_esp;
 | |
|     child_regs.esi = regs.esi;
 | |
|     child_regs.edi = regs.edi;
 | |
|     child_regs.eflags = regs.eflags;
 | |
|     child_regs.eip = regs.eip;
 | |
|     child_regs.cs = regs.cs;
 | |
|     child_regs.ds = regs.ds;
 | |
|     child_regs.es = regs.es;
 | |
|     child_regs.fs = regs.fs;
 | |
|     child_regs.gs = regs.gs;
 | |
|     child_regs.ss = regs.userspace_ss;
 | |
| 
 | |
|     dbgln_if(FORK_DEBUG, "fork: child will begin executing at {:#04x}:{:p} with stack {:#04x}:{:p}, kstack {:#04x}:{:p}",
 | |
|         child_regs.cs, child_regs.eip, child_regs.ss, child_regs.esp, child_regs.ss0, child_regs.esp0);
 | |
| #elif ARCH(X86_64)
 | |
|     auto& child_regs = child_first_thread->m_regs;
 | |
|     child_regs.rax = 0; // fork() returns 0 in the child :^)
 | |
|     child_regs.rbx = regs.rbx;
 | |
|     child_regs.rcx = regs.rcx;
 | |
|     child_regs.rdx = regs.rdx;
 | |
|     child_regs.rbp = regs.rbp;
 | |
|     child_regs.rsp = regs.userspace_rsp;
 | |
|     child_regs.rsi = regs.rsi;
 | |
|     child_regs.rdi = regs.rdi;
 | |
|     child_regs.r8 = regs.r8;
 | |
|     child_regs.r9 = regs.r9;
 | |
|     child_regs.r10 = regs.r10;
 | |
|     child_regs.r11 = regs.r11;
 | |
|     child_regs.r12 = regs.r12;
 | |
|     child_regs.r13 = regs.r13;
 | |
|     child_regs.r14 = regs.r14;
 | |
|     child_regs.r15 = regs.r15;
 | |
|     child_regs.rflags = regs.rflags;
 | |
|     child_regs.rip = regs.rip;
 | |
|     child_regs.cs = regs.cs;
 | |
| 
 | |
|     dbgln_if(FORK_DEBUG, "fork: child will begin executing at {:#04x}:{:p} with stack {:p}, kstack {:p}",
 | |
|         child_regs.cs, child_regs.rip, child_regs.rsp, child_regs.rsp0);
 | |
| #else
 | |
| #    error Unknown architecture
 | |
| #endif
 | |
| 
 | |
|     TRY(address_space().with([&](auto& parent_space) {
 | |
|         return child->address_space().with([&](auto& child_space) -> ErrorOr<void> {
 | |
|             child_space->set_enforces_syscall_regions(parent_space->enforces_syscall_regions());
 | |
|             for (auto& region : parent_space->region_tree().regions()) {
 | |
|                 dbgln_if(FORK_DEBUG, "fork: cloning Region '{}' @ {}", region.name(), region.vaddr());
 | |
|                 auto region_clone = TRY(region.try_clone());
 | |
|                 TRY(region_clone->map(child_space->page_directory(), Memory::ShouldFlushTLB::No));
 | |
|                 TRY(child_space->region_tree().place_specifically(*region_clone, region.range()));
 | |
|                 auto* child_region = region_clone.leak_ptr();
 | |
| 
 | |
|                 if (®ion == m_master_tls_region.unsafe_ptr())
 | |
|                     child->m_master_tls_region = TRY(child_region->try_make_weak_ptr());
 | |
|             }
 | |
|             return {};
 | |
|         });
 | |
|     }));
 | |
| 
 | |
|     thread_finalizer_guard.disarm();
 | |
| 
 | |
|     Process::register_new(*child);
 | |
| 
 | |
|     PerformanceManager::add_process_created_event(*child);
 | |
| 
 | |
|     SpinlockLocker lock(g_scheduler_lock);
 | |
|     child_first_thread->set_affinity(Thread::current()->affinity());
 | |
|     child_first_thread->set_state(Thread::State::Runnable);
 | |
| 
 | |
|     auto child_pid = child->pid().value();
 | |
| 
 | |
|     return child_pid;
 | |
| }
 | |
| }
 |