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)
xxxxxxxxxx
name = "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"
xxxxxxxxxx
use 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
命令查看进程内存xxxxxxxxxx
ps -p <PID> -o pid,vsize,rss
其中:
vsize 或 vsz:虚拟内存大小
rss:驻留集大小
top
命令查看进程内存xxxxxxxxxx
top -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"
xxxxxxxxxx
use 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();
}
}