getrlimit() 和 setrlimit() 系统调用用于获取和设置资源限制。每种资源都有相关的软限制和硬限制,如 rlimit 结构体所定义:
x
struct rlimit { rlim_t rlim_cur; /* Soft limit */ rlim_t rlim_max; /* Hard limit (ceiling for rlim_cur) */ };软限制是内核为相应资源执行的值。硬限制则作为软限制的上限:非特权进程仅能将其软限制设置为从 0 到硬限制范围内的值,并且不可逆地降低其硬限制。特权进程(在 Linux 下:初始用户命名空间中具有 CAP_SYS_RESOURCE 权限的进程)可以任意修改限制值。
RLIM_INFINITY 表示不限制资源(在 getrlimit() 返回的结构体,以及传递给 setrlimit() 的结构体中)。
与 CPU 和内存相关的 resource 参数如下:
RLIMIT_CPU
关于进程可消耗的 CPU 时间数量的限制,单位是秒。当进程达到软限制时,将被发送 SIGXCPU 信号。该信号的默认操作是终止进程。但是,可以捕获该信号,处理程序可以返回控制给主程序。如果进程继续消耗 CPU 时间,那么每秒被发送一次 SIGXCPU,直到达到硬限制,此时被发送 SIGKILL。(后一点描述 Linux 行为,不同的实现在处理达到软限制后继续消耗 CPU 时间的进程时有所不同。需要捕获该信号的可移植应用程序应在首次收到 SIGXCPU 信号时进行有序终止。)
RLIMIT_AS
进程的虚拟内存(地址空间)的最大大小。该限制以字节为单位,并且向下舍入为系统页面大小。该限制影响对 brk(2)、mmap(2) 和 mremap(2) 的调用,这些调用在超过此限制时,将以错误 ENOMEM 失败。另外,自动的栈展开将失败(并且如果没有通过 sigaltstack(2) 提供可用的备用堆栈,那么生成杀死进程的 SIGSEGV)。因为该值是 long,因此在 32 位 long 的机器上,该限制最多为 2 GiB,或者不限制该资源
RLIMIT_DATA
进程的数据段(初始化数据、未初始化数据和堆)的最大大小。该限制以字节为单位,并且向下舍入为系统页面大小。该限制影响对 brk(2)、sbrk(2) 和(从 Linux 4.7 起)mmap(2),这些调用在遇到该资源的软限制时,将以错误 ENOMEM 失败
RLIMIT_RSS
进程驻留集(驻留在 RAM 中的虚拟页面数)的限制(以字节为单位)。该限制仅在 Linux 2.4.x,x < 30 中有效,并且仅影响对指定 MADV_WILLNEED 的 madvise(2) 的调用
RLIMIT_STACK
进程栈的最大大小,单位是字节。当到达该限制时,将生成 SIGSEGV 信号。为处理该信号,进程必须使用替代的信号栈(sigaltstack(2))。
自 Linux 2.6.23 起,该限制还影响用于进程命令行参数和环境变量的空间量;详细信息请参阅 execve(2)
xxxxxxxxxxname = "rlimittest"version = "0.1.0"edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]libc = "0.2"xxxxxxxxxxuse std::process;
fn main() { // 获取当前进程的 PID let pid = process::id(); println!("Command: top -p {} -d 1", pid);
// 设置软限制为 100 MB let limit = libc::rlimit { rlim_cur: 100 * 1024 * 1024, // 100 MB rlim_max: 100 * 1024 * 1024, // 100 MB };
// 调用 setrlimit 函数来设置地址空间大小限制 unsafe { if libc::setrlimit(libc::RLIMIT_AS, &limit as *const libc::rlimit) != 0 { eprintln!("Failed to set memory limit"); process::exit(1); } }
let mut vec = Vec::new(); loop { vec.push(String::from("a").repeat(1024 * 1024)); std::thread::sleep(std::time::Duration::from_millis(100)); } // loop { // let mem_ptr = unsafe { libc::malloc(100 * 1024) }; // if mem_ptr.is_null() { // panic!("Failed to allocate memory"); // } // std::thread::sleep(std::time::Duration::from_millis(10)); // }}通过 /proc/<PID>/status 查看进程使用的内存:
VmSize:虚拟内存大小,对应 RLIMIT_AS
VmData:数据段大小,对应 RLIMIT_DATA
VmRss:驻留集合大小,在新版本的 Linux 中,无法通过 rlimit 限制
ps 命令查看进程内存xxxxxxxxxxps -p <PID> -o pid,vsize,rss其中:
vsize 或 vsz:虚拟内存大小
rss:驻留集大小
top 命令查看进程内存xxxxxxxxxxtop -p <PID> -d <刷新间隔>其中:
VIRT 列表示虚拟内存大小
RSS 列表示驻留集大小
SHR 列表示共享内存大小
CPU 亲和性(CPU affinity)是指将特定的进程或线程限制在特定的 CPU 核心上运行的能力。通过设置 CPU 亲和性,可以控制进程或线程在多核系统中的调度和执行方式,以优化性能和资源利用。通过设置 CPU 亲和性,可以优化多核系统的性能,减少线程迁移和缓存失效的开销,并且提供更好的局部性和并行性。
xxxxxxxxxx[package]name = "affinitytest"version = "0.1.0"edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]libc = "0.2"xxxxxxxxxxuse libc::{cpu_set_t, CPU_SET, CPU_ZERO};use std::mem;use std::thread;
fn main() { println!("Command: top -p {} -d 1", std::process::id()); // 要绑定的 CPU 核心列表 let cpu_cores = vec![0];
// 创建 cpu_set_t 结构体,并且清零 let mut cpuset: cpu_set_t = unsafe { mem::zeroed() }; unsafe { CPU_ZERO(&mut cpuset) };
// 设置 CPU 核心列表 for &core in &cpu_cores { unsafe { CPU_SET(core, &mut cpuset) }; }
// 将当前线程绑定到指定的 CPU 核心 let result = unsafe { libc::sched_setaffinity(0, mem::size_of_val(&cpuset), &cpuset as *const _) }; if result == -1 { panic!("Failed to set CPU affinity"); }
// 开启多线程进行测试 let handles: Vec<_> = (0..10) .map(|_| { thread::spawn(move || { loop { let _tid = unsafe { libc::syscall(libc::SYS_gettid) }; // println!("Thread ID: {}", tid); } }) }) .collect();
// 等待所有线程完成 for handle in handles { handle.join().unwrap(); }}