| //! Configure the process resource limits. |
| use cfg_if::cfg_if; |
| use libc::{c_int, c_long, rusage}; |
| |
| use crate::errno::Errno; |
| use crate::sys::time::TimeVal; |
| use crate::Result; |
| pub use libc::rlim_t; |
| pub use libc::RLIM_INFINITY; |
| use std::mem; |
| |
| cfg_if! { |
| if #[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{ |
| use libc::{__rlimit_resource_t, rlimit}; |
| } else if #[cfg(any( |
| target_os = "freebsd", |
| target_os = "openbsd", |
| target_os = "netbsd", |
| target_os = "macos", |
| target_os = "ios", |
| target_os = "android", |
| target_os = "dragonfly", |
| all(target_os = "linux", not(target_env = "gnu")) |
| ))]{ |
| use libc::rlimit; |
| } |
| } |
| |
| libc_enum! { |
| /// Types of process resources. |
| /// |
| /// The Resource enum is platform dependent. Check different platform |
| /// manuals for more details. Some platform links have been provided for |
| /// easier reference (non-exhaustive). |
| /// |
| /// * [Linux](https://man7.org/linux/man-pages/man2/getrlimit.2.html) |
| /// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=setrlimit) |
| /// * [NetBSD](https://man.netbsd.org/setrlimit.2) |
| |
| // linux-gnu uses u_int as resource enum, which is implemented in libc as |
| // well. |
| // |
| // https://gcc.gnu.org/legacy-ml/gcc/2015-08/msg00441.html |
| // https://github.com/rust-lang/libc/blob/master/src/unix/linux_like/linux/gnu/mod.rs |
| #[cfg_attr(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")), repr(u32))] |
| #[cfg_attr(any( |
| target_os = "freebsd", |
| target_os = "openbsd", |
| target_os = "netbsd", |
| target_os = "macos", |
| target_os = "ios", |
| target_os = "android", |
| target_os = "dragonfly", |
| all(target_os = "linux", not(any(target_env = "gnu", target_env = "uclibc"))) |
| ), repr(i32))] |
| #[non_exhaustive] |
| pub enum Resource { |
| #[cfg(not(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd")))] |
| #[cfg_attr(docsrs, doc(cfg(all())))] |
| /// The maximum amount (in bytes) of virtual memory the process is |
| /// allowed to map. |
| RLIMIT_AS, |
| /// The largest size (in bytes) core(5) file that may be created. |
| RLIMIT_CORE, |
| /// The maximum amount of cpu time (in seconds) to be used by each |
| /// process. |
| RLIMIT_CPU, |
| /// The maximum size (in bytes) of the data segment for a process |
| RLIMIT_DATA, |
| /// The largest size (in bytes) file that may be created. |
| RLIMIT_FSIZE, |
| /// The maximum number of open files for this process. |
| RLIMIT_NOFILE, |
| /// The maximum size (in bytes) of the stack segment for a process. |
| RLIMIT_STACK, |
| |
| #[cfg(target_os = "freebsd")] |
| #[cfg_attr(docsrs, doc(cfg(all())))] |
| /// The maximum number of kqueues this user id is allowed to create. |
| RLIMIT_KQUEUES, |
| |
| #[cfg(any(target_os = "android", target_os = "linux"))] |
| #[cfg_attr(docsrs, doc(cfg(all())))] |
| /// A limit on the combined number of flock locks and fcntl leases that |
| /// this process may establish. |
| RLIMIT_LOCKS, |
| |
| #[cfg(any( |
| target_os = "android", |
| target_os = "freebsd", |
| target_os = "openbsd", |
| target_os = "linux", |
| target_os = "netbsd" |
| ))] |
| #[cfg_attr(docsrs, doc(cfg(all())))] |
| /// The maximum size (in bytes) which a process may lock into memory |
| /// using the mlock(2) system call. |
| RLIMIT_MEMLOCK, |
| |
| #[cfg(any(target_os = "android", target_os = "linux"))] |
| #[cfg_attr(docsrs, doc(cfg(all())))] |
| /// A limit on the number of bytes that can be allocated for POSIX |
| /// message queues for the real user ID of the calling process. |
| RLIMIT_MSGQUEUE, |
| |
| #[cfg(any(target_os = "android", target_os = "linux"))] |
| #[cfg_attr(docsrs, doc(cfg(all())))] |
| /// A ceiling to which the process's nice value can be raised using |
| /// setpriority or nice. |
| RLIMIT_NICE, |
| |
| #[cfg(any( |
| target_os = "android", |
| target_os = "freebsd", |
| target_os = "netbsd", |
| target_os = "openbsd", |
| target_os = "linux", |
| ))] |
| #[cfg_attr(docsrs, doc(cfg(all())))] |
| /// The maximum number of simultaneous processes for this user id. |
| RLIMIT_NPROC, |
| |
| #[cfg(target_os = "freebsd")] |
| #[cfg_attr(docsrs, doc(cfg(all())))] |
| /// The maximum number of pseudo-terminals this user id is allowed to |
| /// create. |
| RLIMIT_NPTS, |
| |
| #[cfg(any(target_os = "android", |
| target_os = "freebsd", |
| target_os = "netbsd", |
| target_os = "openbsd", |
| target_os = "linux", |
| ))] |
| #[cfg_attr(docsrs, doc(cfg(all())))] |
| /// When there is memory pressure and swap is available, prioritize |
| /// eviction of a process' resident pages beyond this amount (in bytes). |
| RLIMIT_RSS, |
| |
| #[cfg(any(target_os = "android", target_os = "linux"))] |
| #[cfg_attr(docsrs, doc(cfg(all())))] |
| /// A ceiling on the real-time priority that may be set for this process |
| /// using sched_setscheduler and sched_set‐ param. |
| RLIMIT_RTPRIO, |
| |
| #[cfg(any(target_os = "linux"))] |
| #[cfg_attr(docsrs, doc(cfg(all())))] |
| /// A limit (in microseconds) on the amount of CPU time that a process |
| /// scheduled under a real-time scheduling policy may con‐ sume without |
| /// making a blocking system call. |
| RLIMIT_RTTIME, |
| |
| #[cfg(any(target_os = "android", target_os = "linux"))] |
| #[cfg_attr(docsrs, doc(cfg(all())))] |
| /// A limit on the number of signals that may be queued for the real |
| /// user ID of the calling process. |
| RLIMIT_SIGPENDING, |
| |
| #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] |
| #[cfg_attr(docsrs, doc(cfg(all())))] |
| /// The maximum size (in bytes) of socket buffer usage for this user. |
| RLIMIT_SBSIZE, |
| |
| #[cfg(target_os = "freebsd")] |
| #[cfg_attr(docsrs, doc(cfg(all())))] |
| /// The maximum size (in bytes) of the swap space that may be reserved |
| /// or used by all of this user id's processes. |
| RLIMIT_SWAP, |
| |
| #[cfg(target_os = "freebsd")] |
| #[cfg_attr(docsrs, doc(cfg(all())))] |
| /// An alias for RLIMIT_AS. |
| RLIMIT_VMEM, |
| } |
| } |
| |
| /// Get the current processes resource limits |
| /// |
| /// The special value [`RLIM_INFINITY`] indicates that no limit will be |
| /// enforced. |
| /// |
| /// # Parameters |
| /// |
| /// * `resource`: The [`Resource`] that we want to get the limits of. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// # use nix::sys::resource::{getrlimit, Resource}; |
| /// |
| /// let (soft_limit, hard_limit) = getrlimit(Resource::RLIMIT_NOFILE).unwrap(); |
| /// println!("current soft_limit: {}", soft_limit); |
| /// println!("current hard_limit: {}", hard_limit); |
| /// ``` |
| /// |
| /// # References |
| /// |
| /// [getrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215) |
| /// |
| /// [`Resource`]: enum.Resource.html |
| pub fn getrlimit(resource: Resource) -> Result<(rlim_t, rlim_t)> { |
| let mut old_rlim = mem::MaybeUninit::<rlimit>::uninit(); |
| |
| cfg_if! { |
| if #[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{ |
| let res = unsafe { libc::getrlimit(resource as __rlimit_resource_t, old_rlim.as_mut_ptr()) }; |
| } else { |
| let res = unsafe { libc::getrlimit(resource as c_int, old_rlim.as_mut_ptr()) }; |
| } |
| } |
| |
| Errno::result(res).map(|_| { |
| let rlimit { rlim_cur, rlim_max } = unsafe { old_rlim.assume_init() }; |
| (rlim_cur, rlim_max) |
| }) |
| } |
| |
| /// Set the current processes resource limits |
| /// |
| /// # Parameters |
| /// |
| /// * `resource`: The [`Resource`] that we want to set the limits of. |
| /// * `soft_limit`: The value that the kernel enforces for the corresponding |
| /// resource. |
| /// * `hard_limit`: The ceiling for the soft limit. Must be lower or equal to |
| /// the current hard limit for non-root users. |
| /// |
| /// The special value [`RLIM_INFINITY`] indicates that no limit will be |
| /// enforced. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// # use nix::sys::resource::{setrlimit, Resource}; |
| /// |
| /// let soft_limit = 512; |
| /// let hard_limit = 1024; |
| /// setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap(); |
| /// ``` |
| /// |
| /// # References |
| /// |
| /// [setrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215) |
| /// |
| /// [`Resource`]: enum.Resource.html |
| /// |
| /// Note: `setrlimit` provides a safe wrapper to libc's `setrlimit`. |
| pub fn setrlimit( |
| resource: Resource, |
| soft_limit: rlim_t, |
| hard_limit: rlim_t, |
| ) -> Result<()> { |
| let new_rlim = rlimit { |
| rlim_cur: soft_limit, |
| rlim_max: hard_limit, |
| }; |
| cfg_if! { |
| if #[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{ |
| let res = unsafe { libc::setrlimit(resource as __rlimit_resource_t, &new_rlim as *const rlimit) }; |
| }else{ |
| let res = unsafe { libc::setrlimit(resource as c_int, &new_rlim as *const rlimit) }; |
| } |
| } |
| |
| Errno::result(res).map(drop) |
| } |
| |
| libc_enum! { |
| /// Whose resource usage should be returned by [`getrusage`]. |
| #[repr(i32)] |
| #[non_exhaustive] |
| pub enum UsageWho { |
| /// Resource usage for the current process. |
| RUSAGE_SELF, |
| |
| /// Resource usage for all the children that have terminated and been waited for. |
| RUSAGE_CHILDREN, |
| |
| #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))] |
| #[cfg_attr(docsrs, doc(cfg(all())))] |
| /// Resource usage for the calling thread. |
| RUSAGE_THREAD, |
| } |
| } |
| |
| /// Output of `getrusage` with information about resource usage. Some of the fields |
| /// may be unused in some platforms, and will be always zeroed out. See their manuals |
| /// for details. |
| #[repr(transparent)] |
| #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] |
| pub struct Usage(rusage); |
| |
| impl AsRef<rusage> for Usage { |
| fn as_ref(&self) -> &rusage { |
| &self.0 |
| } |
| } |
| |
| impl AsMut<rusage> for Usage { |
| fn as_mut(&mut self) -> &mut rusage { |
| &mut self.0 |
| } |
| } |
| |
| impl Usage { |
| /// Total amount of time spent executing in user mode. |
| pub fn user_time(&self) -> TimeVal { |
| TimeVal::from(self.0.ru_utime) |
| } |
| |
| /// Total amount of time spent executing in kernel mode. |
| pub fn system_time(&self) -> TimeVal { |
| TimeVal::from(self.0.ru_stime) |
| } |
| |
| /// The resident set size at its peak, in kilobytes. |
| pub fn max_rss(&self) -> c_long { |
| self.0.ru_maxrss |
| } |
| |
| /// Integral value expressed in kilobytes times ticks of execution indicating |
| /// the amount of text memory shared with other processes. |
| pub fn shared_integral(&self) -> c_long { |
| self.0.ru_ixrss |
| } |
| |
| /// Integral value expressed in kilobytes times ticks of execution indicating |
| /// the amount of unshared memory used by data. |
| pub fn unshared_data_integral(&self) -> c_long { |
| self.0.ru_idrss |
| } |
| |
| /// Integral value expressed in kilobytes times ticks of execution indicating |
| /// the amount of unshared memory used for stack space. |
| pub fn unshared_stack_integral(&self) -> c_long { |
| self.0.ru_isrss |
| } |
| |
| /// Number of page faults that were served without resorting to I/O, with pages |
| /// that have been allocated previously by the kernel. |
| pub fn minor_page_faults(&self) -> c_long { |
| self.0.ru_minflt |
| } |
| |
| /// Number of page faults that were served through I/O (i.e. swap). |
| pub fn major_page_faults(&self) -> c_long { |
| self.0.ru_majflt |
| } |
| |
| /// Number of times all of the memory was fully swapped out. |
| pub fn full_swaps(&self) -> c_long { |
| self.0.ru_nswap |
| } |
| |
| /// Number of times a read was done from a block device. |
| pub fn block_reads(&self) -> c_long { |
| self.0.ru_inblock |
| } |
| |
| /// Number of times a write was done to a block device. |
| pub fn block_writes(&self) -> c_long { |
| self.0.ru_oublock |
| } |
| |
| /// Number of IPC messages sent. |
| pub fn ipc_sends(&self) -> c_long { |
| self.0.ru_msgsnd |
| } |
| |
| /// Number of IPC messages received. |
| pub fn ipc_receives(&self) -> c_long { |
| self.0.ru_msgrcv |
| } |
| |
| /// Number of signals received. |
| pub fn signals(&self) -> c_long { |
| self.0.ru_nsignals |
| } |
| |
| /// Number of times a context switch was voluntarily invoked. |
| pub fn voluntary_context_switches(&self) -> c_long { |
| self.0.ru_nvcsw |
| } |
| |
| /// Number of times a context switch was imposed by the kernel (usually due to |
| /// time slice expiring or preemption by a higher priority process). |
| pub fn involuntary_context_switches(&self) -> c_long { |
| self.0.ru_nivcsw |
| } |
| } |
| |
| /// Get usage information for a process, its children or the current thread |
| /// |
| /// Real time information can be obtained for either the current process or (in some |
| /// systems) thread, but information about children processes is only provided for |
| /// those that have terminated and been waited for (see [`super::wait::wait`]). |
| /// |
| /// Some information may be missing depending on the platform, and the way information |
| /// is provided for children may also vary. Check the manuals for details. |
| /// |
| /// # References |
| /// |
| /// * [getrusage(2)](https://pubs.opengroup.org/onlinepubs/009696699/functions/getrusage.html) |
| /// * [Linux](https://man7.org/linux/man-pages/man2/getrusage.2.html) |
| /// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=getrusage) |
| /// * [NetBSD](https://man.netbsd.org/getrusage.2) |
| /// * [MacOS](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getrusage.2.html) |
| /// |
| /// [`UsageWho`]: enum.UsageWho.html |
| /// |
| /// Note: `getrusage` provides a safe wrapper to libc's [`libc::getrusage`]. |
| pub fn getrusage(who: UsageWho) -> Result<Usage> { |
| unsafe { |
| let mut rusage = mem::MaybeUninit::<rusage>::uninit(); |
| let res = libc::getrusage(who as c_int, rusage.as_mut_ptr()); |
| Errno::result(res).map(|_| Usage(rusage.assume_init())) |
| } |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use super::{getrusage, UsageWho}; |
| |
| #[test] |
| pub fn test_self_cpu_time() { |
| // Make sure some CPU time is used. |
| let mut numbers: Vec<i32> = (1..1_000_000).collect(); |
| numbers.iter_mut().for_each(|item| *item *= 2); |
| |
| // FIXME: this is here to help ensure the compiler does not optimize the whole |
| // thing away. Replace the assert with test::black_box once stabilized. |
| assert_eq!(numbers[100..200].iter().sum::<i32>(), 30_100); |
| |
| let usage = getrusage(UsageWho::RUSAGE_SELF) |
| .expect("Failed to call getrusage for SELF"); |
| let rusage = usage.as_ref(); |
| |
| let user = usage.user_time(); |
| assert!(user.tv_sec() > 0 || user.tv_usec() > 0); |
| assert_eq!(user.tv_sec(), rusage.ru_utime.tv_sec); |
| assert_eq!(user.tv_usec(), rusage.ru_utime.tv_usec); |
| } |
| } |