mirror of
https://github.com/RGBCube/serenity
synced 2025-05-15 07:54:58 +00:00

This change looks more involved than it actually is. This simply reshuffles the previous Process constructor and splits out the parts which can fail (resource allocation) into separate methods which can be called from a factory method. The factory is then used everywhere instead of the constructor.
101 lines
3.6 KiB
C++
101 lines
3.6 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/FileSystem/FileDescription.h>
|
|
#include <Kernel/PerformanceManager.h>
|
|
#include <Kernel/Process.h>
|
|
#include <Kernel/VM/Region.h>
|
|
|
|
namespace Kernel {
|
|
|
|
KResultOr<pid_t> Process::sys$fork(RegisterState& regs)
|
|
{
|
|
REQUIRE_PROMISE(proc);
|
|
RefPtr<Thread> child_first_thread;
|
|
auto child = Process::create(child_first_thread, m_name, uid(), gid(), pid(), m_is_kernel_process, m_cwd, m_executable, m_tty, this);
|
|
if (!child || !child_first_thread)
|
|
return ENOMEM;
|
|
child->m_root_directory = m_root_directory;
|
|
child->m_root_directory_relative_to_global_root = m_root_directory_relative_to_global_root;
|
|
child->m_veil_state = m_veil_state;
|
|
child->m_unveiled_paths = m_unveiled_paths.deep_copy();
|
|
child->m_fds = m_fds;
|
|
child->m_pg = m_pg;
|
|
|
|
{
|
|
ProtectedDataMutationScope scope { *child };
|
|
child->m_promises = m_promises;
|
|
child->m_execpromises = m_execpromises;
|
|
child->m_has_promises = m_has_promises;
|
|
child->m_has_execpromises = m_has_execpromises;
|
|
child->m_sid = m_sid;
|
|
child->m_extra_gids = m_extra_gids;
|
|
child->m_umask = m_umask;
|
|
child->m_signal_trampoline = m_signal_trampoline;
|
|
child->m_dumpable = m_dumpable;
|
|
}
|
|
|
|
dbgln_if(FORK_DEBUG, "fork: child={}", child);
|
|
child->space().set_enforces_syscall_regions(space().enforces_syscall_regions());
|
|
|
|
auto& child_tss = child_first_thread->m_tss;
|
|
child_tss.eax = 0; // fork() returns 0 in the child :^)
|
|
child_tss.ebx = regs.ebx;
|
|
child_tss.ecx = regs.ecx;
|
|
child_tss.edx = regs.edx;
|
|
child_tss.ebp = regs.ebp;
|
|
child_tss.esp = regs.userspace_esp;
|
|
child_tss.esi = regs.esi;
|
|
child_tss.edi = regs.edi;
|
|
child_tss.eflags = regs.eflags;
|
|
child_tss.eip = regs.eip;
|
|
child_tss.cs = regs.cs;
|
|
child_tss.ds = regs.ds;
|
|
child_tss.es = regs.es;
|
|
child_tss.fs = regs.fs;
|
|
child_tss.gs = regs.gs;
|
|
child_tss.ss = regs.userspace_ss;
|
|
|
|
dbgln_if(FORK_DEBUG, "fork: child will begin executing at {:04x}:{:08x} with stack {:04x}:{:08x}, kstack {:04x}:{:08x}", child_tss.cs, child_tss.eip, child_tss.ss, child_tss.esp, child_tss.ss0, child_tss.esp0);
|
|
|
|
{
|
|
ScopedSpinLock lock(space().get_lock());
|
|
for (auto& region : space().regions()) {
|
|
dbgln_if(FORK_DEBUG, "fork: cloning Region({}) '{}' @ {}", region, region->name(), region->vaddr());
|
|
auto region_clone = region->clone(*child);
|
|
if (!region_clone) {
|
|
dbgln("fork: Cannot clone region, insufficient memory");
|
|
// TODO: tear down new process?
|
|
return ENOMEM;
|
|
}
|
|
|
|
auto& child_region = child->space().add_region(region_clone.release_nonnull());
|
|
child_region.map(child->space().page_directory(), ShouldFlushTLB::No);
|
|
|
|
if (region == m_master_tls_region.unsafe_ptr())
|
|
child->m_master_tls_region = child_region;
|
|
}
|
|
|
|
ScopedSpinLock processes_lock(g_processes_lock);
|
|
g_processes->prepend(child);
|
|
}
|
|
|
|
PerformanceManager::add_process_created_event(*child);
|
|
|
|
ScopedSpinLock 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();
|
|
// We need to leak one reference so we don't destroy the Process,
|
|
// which will be dropped by Process::reap
|
|
(void)child.leak_ref();
|
|
return child_pid;
|
|
}
|
|
|
|
}
|