use std::fmt::{Display, Formatter}; use nix::sys::signal::kill; use nix::unistd::Pid; use nix::sys::signal::Signal; use serde::de::value; use std::path::Path; use crate::{create_dir, get_container_info, ContainerInfo}; use crate::error::Result; static CGROUP_PATH: &str = "/sys/fs/cgroup/rocker"; pub enum CgroupLevel { V1, V2, V3, V4, V5, V6, V7, V8, V9, } impl From<&str> for CgroupLevel { fn from(value: &str) -> Self { match value { "v1" => CgroupLevel::V1, "v2" => CgroupLevel::V2, "v3" => CgroupLevel::V3, "v4" => CgroupLevel::V4, "v5" => CgroupLevel::V5, "v6" => CgroupLevel::V6, "v7" => CgroupLevel::V7, "v8" => CgroupLevel::V8, "v9" => CgroupLevel::V9, _ => todo!("invalid cgroup level"), } } } impl Display for CgroupLevel { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Self::V1 => write!(f, "v1"), Self::V2 => write!(f, "v2"), Self::V3 => write!(f, "v3"), Self::V4 => write!(f, "v4"), Self::V5 => write!(f, "v5"), Self::V6 => write!(f, "v6"), Self::V7 => write!(f, "v7"), Self::V8 => write!(f, "v8"), Self::V9 => write!(f, "v9"), } } } impl Default for CgroupLevel { fn default() -> Self { CgroupLevel::V1 } } pub struct Cgroup { pub procs: Vec, pub populated: i32 } impl From<&str> for Cgroup { fn from(container_id: &str) -> Self { let cgroup_path = format!("{CGROUP_PATH}/{}", container_id); let procs = std::fs::read_to_string(format!("{cgroup_path}/cgroup.procs")) .unwrap_or_default() .lines() .filter_map(|pid|pid.parse::().ok()) .collect(); // 找到 cgroup.events 第一行的值 let populated = std::fs::read_to_string(format!("{cgroup_path}/cgroup.events")) .unwrap_or_default() .lines() .next() .unwrap_or_default() .split(" ") .nth(1) .unwrap_or("0") .parse::() .unwrap_or_default(); Self { procs, populated } } } impl Cgroup { pub fn create(container_id: &str, pid: i32, cgroup_s: &str) -> Result<()> { let cgroup_path = format!("{CGROUP_PATH}/{container_id}"); create_dir(&cgroup_path, false)?; // 根据 cgroup_s 写入指定参数 let _ = cgroup_s.split(" ") .map(|s|s.split("=")) .map(|mut x|(x.next(), x.next())) .filter_map(|(dev, value)| { if let (Some(dev), Ok(value)) = (dev, value.unwrap_or("").parse::()) { Some((dev, value)) } else { None } }).for_each(|(dev, value)| { match (dev, value) { ("cpu", 1..=100) => { if let Ok(_) = std::fs::write(format!("{cgroup_path}/cpu.max"), format!("{} 100000", 100000 / 100 * value)) { println!("cpu 限制 {value}% 成功") } } ("memory", 1..=4096) => { } _ => { println!("🔴 没有定义的 cgroup 参数: {dev}={value}") } } }); // 加入控制组 std::fs::write(format!("{cgroup_path}/cgroup.procs"), pid.to_string())?; Ok(()) } pub fn remove(container_id: &str) { let _self = Self::from(container_id); for pid in _self.procs { let _ = kill(Pid::from_raw(pid), Signal::SIGKILL); let pid_path = Path::new("/proc").join(pid.to_string()); (0..100).any(|i|{ std::thread::sleep(std::time::Duration::from_millis(i)); pid_path.exists() == false }); } let cgroup_path = format!("{CGROUP_PATH}/{container_id}"); let _ = std::fs::remove_dir(&cgroup_path); println!("删除cgroup {cgroup_path}") } }