Move functions related to spawning the init process to a seperate module

This commit is contained in:
jiangjianfeng
2025-05-08 06:43:17 +00:00
committed by Ruihan Li
parent 50ba735e96
commit 89e8cd5936
11 changed files with 223 additions and 318 deletions

View File

@ -15,7 +15,7 @@ use crate::{
prelude::*, prelude::*,
process::{ process::{
signal::{signals::kernel::KernelSignal, PollHandle, Pollable}, signal::{signals::kernel::KernelSignal, PollHandle, Pollable},
JobControl, Process, Terminal, JobControl, Terminal,
}, },
}; };
@ -211,21 +211,3 @@ pub fn new_job_control_and_ldisc() -> (Arc<JobControl>, Arc<LineDiscipline>) {
pub fn get_n_tty() -> &'static Arc<Tty> { pub fn get_n_tty() -> &'static Arc<Tty> {
N_TTY.get().unwrap() N_TTY.get().unwrap()
} }
/// Open `N_TTY` as the controlling terminal for the process. This method should
/// only be called when creating the init process.
pub fn open_ntty_as_controlling_terminal(process: &Process) -> Result<()> {
let tty = get_n_tty();
let session = &process.session().unwrap();
let process_group = process.process_group().unwrap();
session.set_terminal(|| {
tty.job_control.set_session(session);
Ok(tty.clone())
})?;
tty.job_control.set_foreground(Some(&process_group))?;
Ok(())
}

View File

@ -35,7 +35,7 @@ use ostd::{
boot::boot_info, boot::boot_info,
cpu::{CpuId, CpuSet, PinCurrentCpu}, cpu::{CpuId, CpuSet, PinCurrentCpu},
}; };
use process::Process; use process::{spawn_init_process, Process};
use sched::SchedPolicy; use sched::SchedPolicy;
use crate::{prelude::*, thread::kernel_thread::ThreadOptions}; use crate::{prelude::*, thread::kernel_thread::ThreadOptions};
@ -151,7 +151,7 @@ fn init_thread() {
let karg: KCmdlineArg = boot_info().kernel_cmdline.as_str().into(); let karg: KCmdlineArg = boot_info().kernel_cmdline.as_str().into();
let initproc = Process::spawn_user_process( let initproc = spawn_init_process(
karg.get_initproc_path().unwrap(), karg.get_initproc_path().unwrap(),
karg.get_initproc_argv().to_vec(), karg.get_initproc_argv().to_vec(),
karg.get_initproc_envp().to_vec(), karg.get_initproc_envp().to_vec(),

View File

@ -8,8 +8,9 @@ use super::{
posix_thread::{AsPosixThread, PosixThreadBuilder, ThreadName}, posix_thread::{AsPosixThread, PosixThreadBuilder, ThreadName},
process_table, process_table,
process_vm::ProcessVm, process_vm::ProcessVm,
rlimit::ResourceLimits,
signal::{constants::SIGCHLD, sig_disposition::SigDispositions, sig_num::SigNum}, signal::{constants::SIGCHLD, sig_disposition::SigDispositions, sig_num::SigNum},
Credentials, Process, ProcessBuilder, Credentials, Pid, Process,
}; };
use crate::{ use crate::{
cpu::LinuxAbi, cpu::LinuxAbi,
@ -17,6 +18,7 @@ use crate::{
fs::{file_table::FileTable, thread_info::ThreadFsInfo}, fs::{file_table::FileTable, thread_info::ThreadFsInfo},
prelude::*, prelude::*,
process::posix_thread::allocate_posix_tid, process::posix_thread::allocate_posix_tid,
sched::Nice,
thread::{AsThread, Tid}, thread::{AsThread, Tid},
}; };
@ -299,13 +301,13 @@ fn clone_child_process(
let clone_flags = clone_args.flags; let clone_flags = clone_args.flags;
// clone vm // Clone the virtual memory space
let child_process_vm = { let child_process_vm = {
let parent_process_vm = process.vm(); let parent_process_vm = process.vm();
clone_vm(parent_process_vm, clone_flags)? clone_vm(parent_process_vm, clone_flags)?
}; };
// clone user space // Clone the user context
let child_user_ctx = Arc::new(clone_user_ctx( let child_user_ctx = Arc::new(clone_user_ctx(
parent_context, parent_context,
clone_args.stack, clone_args.stack,
@ -314,22 +316,25 @@ fn clone_child_process(
clone_flags, clone_flags,
)); ));
// clone file table // Clone the file table
let child_file_table = clone_files(thread_local.borrow_file_table().unwrap(), clone_flags); let child_file_table = clone_files(thread_local.borrow_file_table().unwrap(), clone_flags);
// clone fs // Clone the filesystem information
let child_fs = clone_fs(posix_thread.fs(), clone_flags); let child_fs = clone_fs(posix_thread.fs(), clone_flags);
// clone sig dispositions // Clone signal dispositions
let child_sig_dispositions = clone_sighand(process.sig_dispositions(), clone_flags); let child_sig_dispositions = clone_sighand(process.sig_dispositions(), clone_flags);
// clone system V semaphore // Clone System V semaphore
clone_sysvsem(clone_flags)?; clone_sysvsem(clone_flags)?;
// inherit parent's sig mask // Inherit the parent's signal mask
let child_sig_mask = posix_thread.sig_mask().load(Ordering::Relaxed).into(); let child_sig_mask = posix_thread.sig_mask().load(Ordering::Relaxed).into();
// inherit parent's nice value // Inherit the parent's resource limits
let child_resource_limits = process.resource_limits().clone();
// Inherit the parent's nice value
let child_nice = process.nice().load(Ordering::Relaxed); let child_nice = process.nice().load(Ordering::Relaxed);
let child_tid = allocate_posix_tid(); let child_tid = allocate_posix_tid();
@ -358,16 +363,16 @@ fn clone_child_process(
child_thread_builder = child_thread_builder =
clone_child_settid(child_thread_builder, clone_args.child_tid, clone_flags); clone_child_settid(child_thread_builder, clone_args.child_tid, clone_flags);
let mut process_builder = create_child_process(
ProcessBuilder::new(child_tid, &child_elf_path, posix_thread.weak_process()); child_tid,
posix_thread.weak_process(),
process_builder &child_elf_path,
.main_thread_builder(child_thread_builder) child_process_vm,
.process_vm(child_process_vm) child_resource_limits,
.sig_dispositions(child_sig_dispositions) child_nice,
.nice(child_nice); child_sig_dispositions,
child_thread_builder,
process_builder.build()? )
}; };
if let Some(sig) = clone_args.exit_signal { if let Some(sig) = clone_args.exit_signal {
@ -510,6 +515,32 @@ fn clone_sysvsem(clone_flags: CloneFlags) -> Result<()> {
Ok(()) Ok(())
} }
fn create_child_process(
pid: Pid,
parent: Weak<Process>,
executable_path: &str,
process_vm: ProcessVm,
resource_limits: ResourceLimits,
nice: Nice,
sig_dispositions: Arc<Mutex<SigDispositions>>,
thread_builder: PosixThreadBuilder,
) -> Arc<Process> {
let child_proc = Process::new(
pid,
parent,
executable_path.to_string(),
process_vm,
resource_limits,
nice,
sig_dispositions,
);
let child_task = thread_builder.process(Arc::downgrade(&child_proc)).build();
child_proc.tasks().lock().insert(child_task).unwrap();
child_proc
}
fn set_parent_and_group(parent: &Process, child: &Arc<Process>) { fn set_parent_and_group(parent: &Process, child: &Arc<Process>) {
// Lock order: process table -> children -> group of process // Lock order: process table -> children -> group of process
// -> group inner -> session inner // -> group inner -> session inner

View File

@ -23,7 +23,8 @@ pub use clone::{clone_child, CloneArgs, CloneFlags};
pub use credentials::{Credentials, Gid, Uid}; pub use credentials::{Credentials, Gid, Uid};
pub use kill::{kill, kill_all, kill_group, tgkill}; pub use kill::{kill, kill_all, kill_group, tgkill};
pub use process::{ pub use process::{
ExitCode, JobControl, Pgid, Pid, Process, ProcessBuilder, ProcessGroup, Session, Sid, Terminal, spawn_init_process, ExitCode, JobControl, Pgid, Pid, Process, ProcessGroup, Session, Sid,
Terminal,
}; };
pub use process_filter::ProcessFilter; pub use process_filter::ProcessFilter;
pub use process_vm::{ pub use process_vm::{

View File

@ -1,7 +1,5 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
#![expect(dead_code)]
use ostd::{ use ostd::{
cpu::{context::UserContext, CpuSet}, cpu::{context::UserContext, CpuSet},
sync::RwArc, sync::RwArc,
@ -94,11 +92,6 @@ impl PosixThreadBuilder {
self self
} }
pub fn sched_policy(mut self, sched_policy: SchedPolicy) -> Self {
self.sched_policy = sched_policy;
self
}
pub fn build(self) -> Arc<Task> { pub fn build(self) -> Arc<Task> {
let Self { let Self {
tid, tid,

View File

@ -38,7 +38,7 @@ pub mod thread_table;
pub use builder::PosixThreadBuilder; pub use builder::PosixThreadBuilder;
pub use exit::{do_exit, do_exit_group}; pub use exit::{do_exit, do_exit_group};
pub use name::{ThreadName, MAX_THREAD_NAME_LEN}; pub use name::{ThreadName, MAX_THREAD_NAME_LEN};
pub use posix_thread_ext::{create_posix_task_from_executable, AsPosixThread}; pub use posix_thread_ext::AsPosixThread;
pub use robust_list::RobustListHead; pub use robust_list::RobustListHead;
pub use thread_local::{AsThreadLocal, FileTableRefMut, ThreadLocal}; pub use thread_local::{AsThreadLocal, FileTableRefMut, ThreadLocal};

View File

@ -1,17 +1,9 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use ostd::{cpu::context::UserContext, task::Task, user::UserContextApi}; use ostd::task::Task;
use super::{builder::PosixThreadBuilder, name::ThreadName, PosixThread}; use super::PosixThread;
use crate::{ use crate::thread::{AsThread, Thread};
fs::{
fs_resolver::{FsPath, AT_FDCWD},
thread_info::ThreadFsInfo,
},
prelude::*,
process::{process_vm::ProcessVm, program_loader::ProgramToLoad, Credentials, Process},
thread::{AsThread, Thread, Tid},
};
/// A trait to provide the `as_posix_thread` method for tasks and threads. /// A trait to provide the `as_posix_thread` method for tasks and threads.
pub trait AsPosixThread { pub trait AsPosixThread {
@ -30,37 +22,3 @@ impl AsPosixThread for Task {
self.as_thread()?.as_posix_thread() self.as_thread()?.as_posix_thread()
} }
} }
/// Creates a task for running an executable file.
///
/// This function should _only_ be used to create the init user task.
pub fn create_posix_task_from_executable(
tid: Tid,
credentials: Credentials,
process_vm: &ProcessVm,
executable_path: &str,
process: Weak<Process>,
argv: Vec<CString>,
envp: Vec<CString>,
) -> Result<Arc<Task>> {
let fs = ThreadFsInfo::default();
let (_, elf_load_info) = {
let fs_resolver = fs.resolver().read();
let fs_path = FsPath::new(AT_FDCWD, executable_path)?;
let elf_file = fs.resolver().read().lookup(&fs_path)?;
let program_to_load =
ProgramToLoad::build_from_file(elf_file, &fs_resolver, argv, envp, 1)?;
process_vm.clear_and_map();
program_to_load.load_to_vm(process_vm, &fs_resolver)?
};
let mut user_ctx = UserContext::default();
user_ctx.set_instruction_pointer(elf_load_info.entry_point() as _);
user_ctx.set_stack_pointer(elf_load_info.user_stack_top() as _);
let thread_name = Some(ThreadName::new_from_executable_path(executable_path)?);
let thread_builder = PosixThreadBuilder::new(tid, Arc::new(user_ctx), credentials)
.thread_name(thread_name)
.process(process)
.fs(Arc::new(fs));
Ok(thread_builder.build())
}

View File

@ -1,167 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
#![expect(dead_code)]
use super::{Pid, Process};
use crate::{
prelude::*,
process::{
posix_thread::{create_posix_task_from_executable, PosixThreadBuilder},
process_vm::ProcessVm,
rlimit::ResourceLimits,
signal::sig_disposition::SigDispositions,
Credentials,
},
sched::Nice,
};
pub struct ProcessBuilder<'a> {
// Essential parts
pid: Pid,
executable_path: &'a str,
parent: Weak<Process>,
// Optional parts
main_thread_builder: Option<PosixThreadBuilder>,
argv: Option<Vec<CString>>,
envp: Option<Vec<CString>>,
process_vm: Option<ProcessVm>,
resource_limits: Option<ResourceLimits>,
sig_dispositions: Option<Arc<Mutex<SigDispositions>>>,
credentials: Option<Credentials>,
nice: Option<Nice>,
}
impl<'a> ProcessBuilder<'a> {
pub fn new(pid: Pid, executable_path: &'a str, parent: Weak<Process>) -> Self {
ProcessBuilder {
pid,
executable_path,
parent,
main_thread_builder: None,
argv: None,
envp: None,
process_vm: None,
resource_limits: None,
sig_dispositions: None,
credentials: None,
nice: None,
}
}
pub fn main_thread_builder(&mut self, builder: PosixThreadBuilder) -> &mut Self {
self.main_thread_builder = Some(builder);
self
}
pub fn process_vm(&mut self, process_vm: ProcessVm) -> &mut Self {
self.process_vm = Some(process_vm);
self
}
pub fn resource_limits(&mut self, resource_limits: ResourceLimits) -> &mut Self {
self.resource_limits = Some(resource_limits);
self
}
pub fn sig_dispositions(&mut self, sig_dispositions: Arc<Mutex<SigDispositions>>) -> &mut Self {
self.sig_dispositions = Some(sig_dispositions);
self
}
pub fn argv(&mut self, argv: Vec<CString>) -> &mut Self {
self.argv = Some(argv);
self
}
pub fn envp(&mut self, envp: Vec<CString>) -> &mut Self {
self.envp = Some(envp);
self
}
pub fn credentials(&mut self, credentials: Credentials) -> &mut Self {
self.credentials = Some(credentials);
self
}
pub fn nice(&mut self, nice: Nice) -> &mut Self {
self.nice = Some(nice);
self
}
fn check_build(&self) -> Result<()> {
if self.main_thread_builder.is_some() {
debug_assert!(self.parent.upgrade().is_some());
debug_assert!(self.argv.is_none());
debug_assert!(self.envp.is_none());
debug_assert!(self.credentials.is_none());
}
if self.main_thread_builder.is_none() {
debug_assert!(self.parent.upgrade().is_none());
debug_assert!(self.argv.is_some());
debug_assert!(self.envp.is_some());
debug_assert!(self.credentials.is_some());
}
Ok(())
}
pub fn build(self) -> Result<Arc<Process>> {
self.check_build()?;
let Self {
pid,
executable_path,
parent,
main_thread_builder,
argv,
envp,
process_vm,
resource_limits,
sig_dispositions,
credentials,
nice,
} = self;
let process_vm = process_vm.or_else(|| Some(ProcessVm::alloc())).unwrap();
let resource_limits = resource_limits
.or_else(|| Some(ResourceLimits::default()))
.unwrap();
let sig_dispositions = sig_dispositions
.or_else(|| Some(Arc::new(Mutex::new(SigDispositions::new()))))
.unwrap();
let nice = nice.or_else(|| Some(Nice::default())).unwrap();
let process = Process::new(
pid,
parent,
executable_path.to_string(),
process_vm,
resource_limits,
nice,
sig_dispositions,
);
let task = if let Some(thread_builder) = main_thread_builder {
let builder = thread_builder.process(Arc::downgrade(&process));
builder.build()
} else {
create_posix_task_from_executable(
pid,
credentials.unwrap(),
process.vm(),
executable_path,
Arc::downgrade(&process),
argv.unwrap(),
envp.unwrap(),
)?
};
process.tasks().lock().insert(task).unwrap();
Ok(process)
}
}

View File

@ -0,0 +1,146 @@
// SPDX-License-Identifier: MPL-2.0
//! This module defines functions related to spawning the init process.
use ostd::{cpu::context::UserContext, task::Task, user::UserContextApi};
use super::{Process, Terminal};
use crate::{
device::tty::get_n_tty,
fs::{
fs_resolver::{FsPath, AT_FDCWD},
thread_info::ThreadFsInfo,
},
prelude::*,
process::{
posix_thread::{allocate_posix_tid, PosixThreadBuilder, ThreadName},
process_table,
process_vm::ProcessVm,
rlimit::ResourceLimits,
signal::sig_disposition::SigDispositions,
Credentials, ProgramToLoad,
},
sched::Nice,
thread::Tid,
};
/// Creates and schedules the init process to run.
pub fn spawn_init_process(
executable_path: &str,
argv: Vec<CString>,
envp: Vec<CString>,
) -> Result<Arc<Process>> {
// Ensure the path for init process executable is absolute.
debug_assert!(executable_path.starts_with('/'));
let process = create_init_process(executable_path, argv, envp)?;
set_session_and_group(&process);
open_ntty_as_controlling_terminal(&process)?;
process.run();
Ok(process)
}
fn create_init_process(
executable_path: &str,
argv: Vec<CString>,
envp: Vec<CString>,
) -> Result<Arc<Process>> {
let pid = allocate_posix_tid();
let parent = Weak::new();
let process_vm = ProcessVm::alloc();
let resource_limits = ResourceLimits::default();
let nice = Nice::default();
let sig_dispositions = Arc::new(Mutex::new(SigDispositions::default()));
let init_proc = Process::new(
pid,
parent,
executable_path.to_string(),
process_vm,
resource_limits,
nice,
sig_dispositions,
);
let init_task = create_init_task(
pid,
init_proc.vm(),
executable_path,
Arc::downgrade(&init_proc),
argv,
envp,
)?;
init_proc.tasks().lock().insert(init_task).unwrap();
Ok(init_proc)
}
fn set_session_and_group(process: &Arc<Process>) {
// Locking order: session table -> group table -> process table -> process group
let mut session_table_mut = process_table::session_table_mut();
let mut group_table_mut = process_table::group_table_mut();
let mut process_table_mut = process_table::process_table_mut();
// Create a new process group and session for the process
process.set_new_session(
&mut process.process_group.lock(),
&mut session_table_mut,
&mut group_table_mut,
);
// Add the new process to the global table
process_table_mut.insert(process.pid(), process.clone());
}
/// Creates the init task from the given executable file.
fn create_init_task(
tid: Tid,
process_vm: &ProcessVm,
executable_path: &str,
process: Weak<Process>,
argv: Vec<CString>,
envp: Vec<CString>,
) -> Result<Arc<Task>> {
let credentials = Credentials::new_root();
let fs = ThreadFsInfo::default();
let (_, elf_load_info) = {
let fs_resolver = fs.resolver().read();
let fs_path = FsPath::new(AT_FDCWD, executable_path)?;
let elf_file = fs.resolver().read().lookup(&fs_path)?;
let program_to_load =
ProgramToLoad::build_from_file(elf_file, &fs_resolver, argv, envp, 1)?;
process_vm.clear_and_map();
program_to_load.load_to_vm(process_vm, &fs_resolver)?
};
let mut user_ctx = UserContext::default();
user_ctx.set_instruction_pointer(elf_load_info.entry_point() as _);
user_ctx.set_stack_pointer(elf_load_info.user_stack_top() as _);
let thread_name = Some(ThreadName::new_from_executable_path(executable_path)?);
let thread_builder = PosixThreadBuilder::new(tid, Arc::new(user_ctx), credentials)
.thread_name(thread_name)
.process(process)
.fs(Arc::new(fs));
Ok(thread_builder.build())
}
/// Opens `N_TTY` as the controlling terminal for the process.
fn open_ntty_as_controlling_terminal(process: &Process) -> Result<()> {
let tty = get_n_tty();
let session = &process.session().unwrap();
let process_group = process.process_group().unwrap();
session.set_terminal(|| {
tty.job_control().set_session(session);
Ok(tty.clone())
})?;
tty.job_control().set_foreground(Some(&process_group))?;
Ok(())
}

View File

@ -4,7 +4,7 @@ use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use self::timer_manager::PosixTimerManager; use self::timer_manager::PosixTimerManager;
use super::{ use super::{
posix_thread::{allocate_posix_tid, AsPosixThread}, posix_thread::AsPosixThread,
process_table, process_table,
process_vm::{Heap, InitStackReader, ProcessVm, ProcessVmarGuard}, process_vm::{Heap, InitStackReader, ProcessVm, ProcessVmarGuard},
rlimit::ResourceLimits, rlimit::ResourceLimits,
@ -15,17 +15,15 @@ use super::{
}, },
status::ProcessStatus, status::ProcessStatus,
task_set::TaskSet, task_set::TaskSet,
Credentials,
}; };
use crate::{ use crate::{
device::tty::open_ntty_as_controlling_terminal,
prelude::*, prelude::*,
sched::{AtomicNice, Nice}, sched::{AtomicNice, Nice},
thread::{AsThread, Thread}, thread::{AsThread, Thread},
time::clocks::ProfClock, time::clocks::ProfClock,
}; };
mod builder; mod init_proc;
mod job_control; mod job_control;
mod process_group; mod process_group;
mod session; mod session;
@ -33,7 +31,7 @@ mod terminal;
mod timer_manager; mod timer_manager;
use atomic_integer_wrapper::define_atomic_version_of_integer_like_type; use atomic_integer_wrapper::define_atomic_version_of_integer_like_type;
pub use builder::ProcessBuilder; pub use init_proc::spawn_init_process;
pub use job_control::JobControl; pub use job_control::JobControl;
use ostd::{sync::WaitQueue, task::Task}; use ostd::{sync::WaitQueue, task::Task};
pub use process_group::ProcessGroup; pub use process_group::ProcessGroup;
@ -184,7 +182,7 @@ impl Process {
Some(Task::current()?.as_posix_thread()?.process()) Some(Task::current()?.as_posix_thread()?.process())
} }
fn new( pub(super) fn new(
pid: Pid, pid: Pid,
parent: Weak<Process>, parent: Weak<Process>,
executable_path: String, executable_path: String,
@ -222,57 +220,8 @@ impl Process {
}) })
} }
/// init a user process and run the process /// Runs the process.
pub fn spawn_user_process( pub(super) fn run(&self) {
executable_path: &str,
argv: Vec<CString>,
envp: Vec<CString>,
) -> Result<Arc<Self>> {
// spawn user process should give an absolute path
debug_assert!(executable_path.starts_with('/'));
let process = Process::create_user_process(executable_path, argv, envp)?;
open_ntty_as_controlling_terminal(&process)?;
process.run();
Ok(process)
}
fn create_user_process(
executable_path: &str,
argv: Vec<CString>,
envp: Vec<CString>,
) -> Result<Arc<Self>> {
let process = {
let pid = allocate_posix_tid();
let parent = Weak::new();
let credentials = Credentials::new_root();
let mut builder = ProcessBuilder::new(pid, executable_path, parent);
builder.argv(argv).envp(envp).credentials(credentials);
builder.build()?
};
// Lock order: session table -> group table -> process table -> group of process
let mut session_table_mut = process_table::session_table_mut();
let mut group_table_mut = process_table::group_table_mut();
let mut process_table_mut = process_table::process_table_mut();
// Create a new process group and a new session for the new process
process.set_new_session(
&mut process.process_group.lock(),
&mut session_table_mut,
&mut group_table_mut,
);
// Insert the new process to the global table
process_table_mut.insert(process.pid(), process.clone());
Ok(process)
}
/// start to run current process
pub fn run(&self) {
let tasks = self.tasks.lock(); let tasks = self.tasks.lock();
// when run the process, the process should has only one thread // when run the process, the process should has only one thread
debug_assert!(tasks.as_slice().len() == 1); debug_assert!(tasks.as_slice().len() == 1);

View File

@ -26,6 +26,7 @@ const INIT_RLIMIT_MEMLOCK: u64 = 8 * 1024 * 1024;
// https://github.com/torvalds/linux/blob/fac04efc5c793dccbd07e2d59af9f90b7fc0dca4/include/uapi/linux/mqueue.h#L26 // https://github.com/torvalds/linux/blob/fac04efc5c793dccbd07e2d59af9f90b7fc0dca4/include/uapi/linux/mqueue.h#L26
const INIT_RLIMIT_MSGQUEUE: u64 = 819200; const INIT_RLIMIT_MSGQUEUE: u64 = 819200;
#[derive(Clone)]
pub struct ResourceLimits { pub struct ResourceLimits {
rlimits: [RLimit64; RLIMIT_COUNT], rlimits: [RLimit64; RLIMIT_COUNT],
} }
@ -169,3 +170,14 @@ impl Default for RLimit64 {
} }
} }
} }
impl Clone for RLimit64 {
fn clone(&self) -> Self {
let (cur, max) = self.get_cur_and_max();
Self {
cur: AtomicU64::new(cur),
max: AtomicU64::new(max),
lock: SpinLock::new(()),
}
}
}