Linux内核中的cgroups v2资源管理技术什么是cgroupscgroupsControl Groups是Linux内核提供的一种资源管理机制它允许我们对进程组进行资源限制、优先级设置、资源使用监控等操作。cgroups是容器技术如Docker、Kubernetes的核心基础之一它为容器提供了资源隔离和限制的能力。cgroups有两个主要版本cgroups v1较早的版本提供了基本的资源管理功能cgroups v2较新的版本提供了更统一、更灵活的资源管理接口cgroups v2的设计目标cgroups v2的设计目标包括统一的层次结构提供一个统一的层次化结构简化资源管理更好的资源隔离更严格的资源隔离避免资源竞争更灵活的资源分配支持动态调整资源分配更简单的API简化用户空间接口降低使用复杂度更好的安全性增强安全特性防止资源逃逸cgroups v2的核心概念1. 层次结构cgroups v2使用一个统一的层次结构来组织进程和资源控制器根cgroup整个系统的根cgroup所有进程默认属于根cgroup子cgroup根cgroup下可以创建多个子cgroup形成树状结构进程分配每个进程只能属于一个cgroup但可以从一个cgroup迁移到另一个cgroup2. 资源控制器cgroups v2支持多种资源控制器用于管理不同类型的资源cpu控制CPU时间分配cpuset控制CPU和内存节点的分配memory控制内存使用io控制块设备I/Opids控制进程数量hugetlb控制大页内存使用rdma控制RDMA资源使用3. 挂载和管理cgroups v2通过文件系统接口进行管理挂载cgroup2文件系统使用mount -t cgroup2 none /sys/fs/cgroup创建cgroup在挂载点下创建目录管理进程将进程ID写入cgroup.procs文件配置资源通过写入相应的配置文件来设置资源限制cgroups v2的使用方法1. 基本操作挂载cgroup2文件系统# 挂载cgroup2文件系统 mount -t cgroup2 none /sys/fs/cgroup # 查看挂载情况 mount | grep cgroup创建和管理cgroup# 创建一个名为myapp的cgroup mkdir /sys/fs/cgroup/myapp # 将进程ID 1234加入到myapp cgroup echo 1234 /sys/fs/cgroup/myapp/cgroup.procs # 查看myapp cgroup中的进程 cat /sys/fs/cgroup/myapp/cgroup.procs # 删除myapp cgroup需要先移走所有进程 rmdir /sys/fs/cgroup/myapp2. 资源限制配置CPU限制# 设置CPU权重相对值默认100 echo 500 /sys/fs/cgroup/myapp/cpu.weight # 设置CPU最大使用限制单位微秒/毫秒1000000表示100% echo max 100000 /sys/fs/cgroup/myapp/cpu.max # 设置CPU利用率限制单位百分比 echo 75000 100000 /sys/fs/cgroup/myapp/cpu.max内存限制# 设置内存使用上限 echo 256M /sys/fs/cgroup/myapp/memory.max # 设置内存软限制 echo 128M /sys/fs/cgroup/myapp/memory.high # 查看内存使用情况 cat /sys/fs/cgroup/myapp/memory.currentI/O限制# 设置块设备的I/O权重 echo 8:0 500 /sys/fs/cgroup/myapp/io.weight # 设置读操作的速率限制单位字节/秒 echo 8:0 rbps1048576 /sys/fs/cgroup/myapp/io.max # 设置写操作的速率限制 echo 8:0 wbps2097152 /sys/fs/cgroup/myapp/io.max进程数量限制# 设置最大进程数量 echo 100 /sys/fs/cgroup/myapp/pids.max # 查看当前进程数量 cat /sys/fs/cgroup/myapp/pids.currentcgroups v2的编程接口1. 系统调用cgroups v2提供了以下系统调用mkdir()/rmdir()创建和删除cgroupopen()/read()/write()读取和写入cgroup配置文件getpid()/getppid()获取进程IDwrite()将进程加入cgroup2. 示例代码#include stdio.h #include stdlib.h #include unistd.h #include fcntl.h #include string.h // 创建cgroup int create_cgroup(const char *path) { return mkdir(path, 0755); } // 将进程加入cgroup int add_process_to_cgroup(const char *cgroup_path, pid_t pid) { char procs_path[256]; char pid_str[16]; int fd; snprintf(procs_path, sizeof(procs_path), %s/cgroup.procs, cgroup_path); fd open(procs_path, O_WRONLY); if (fd 0) { perror(open); return -1; } snprintf(pid_str, sizeof(pid_str), %d, pid); if (write(fd, pid_str, strlen(pid_str)) 0) { perror(write); close(fd); return -1; } close(fd); return 0; } // 设置CPU限制 int set_cpu_limit(const char *cgroup_path, int weight, int max_us) { char cpu_weight_path[256]; char cpu_max_path[256]; char weight_str[16]; char max_str[32]; int fd; // 设置CPU权重 snprintf(cpu_weight_path, sizeof(cpu_weight_path), %s/cpu.weight, cgroup_path); fd open(cpu_weight_path, O_WRONLY); if (fd 0) { perror(open cpu.weight); return -1; } snprintf(weight_str, sizeof(weight_str), %d, weight); if (write(fd, weight_str, strlen(weight_str)) 0) { perror(write cpu.weight); close(fd); return -1; } close(fd); // 设置CPU最大使用限制 snprintf(cpu_max_path, sizeof(cpu_max_path), %s/cpu.max, cgroup_path); fd open(cpu_max_path, O_WRONLY); if (fd 0) { perror(open cpu.max); return -1; } snprintf(max_str, sizeof(max_str), %d 1000000, max_us); if (write(fd, max_str, strlen(max_str)) 0) { perror(write cpu.max); close(fd); return -1; } close(fd); return 0; } // 设置内存限制 int set_memory_limit(const char *cgroup_path, const char *max) { char memory_max_path[256]; int fd; snprintf(memory_max_path, sizeof(memory_max_path), %s/memory.max, cgroup_path); fd open(memory_max_path, O_WRONLY); if (fd 0) { perror(open memory.max); return -1; } if (write(fd, max, strlen(max)) 0) { perror(write memory.max); close(fd); return -1; } close(fd); return 0; } int main() { const char *cgroup_path /sys/fs/cgroup/myapp; pid_t pid getpid(); // 创建cgroup if (create_cgroup(cgroup_path) 0) { fprintf(stderr, Failed to create cgroup\n); return 1; } // 将当前进程加入cgroup if (add_process_to_cgroup(cgroup_path, pid) 0) { fprintf(stderr, Failed to add process to cgroup\n); return 1; } // 设置资源限制 if (set_cpu_limit(cgroup_path, 500, 500000) 0) { fprintf(stderr, Failed to set CPU limit\n); return 1; } if (set_memory_limit(cgroup_path, 256M) 0) { fprintf(stderr, Failed to set memory limit\n); return 1; } printf(Process %d added to cgroup %s with resource limits\n, pid, cgroup_path); // 保持进程运行 while (1) { sleep(1); } return 0; }cgroups v2与cgroups v1的区别1. 层次结构cgroups v1每个资源控制器有独立的层次结构cgroups v2所有资源控制器共享一个层次结构2. 资源分配模型cgroups v1使用配额和限制的方式cgroups v2使用统一的比例分配模型3. 接口设计cgroups v1每个资源控制器有独立的文件接口cgroups v2提供统一的文件接口简化管理4. 安全性cgroups v1存在一些安全漏洞如资源逃逸cgroups v2增强了安全特性提供更严格的资源隔离5. 兼容性cgroups v1被广泛使用大多数容器运行时支持cgroups v2较新正在被越来越多的系统和容器运行时采用实际应用场景1. 容器管理cgroups v2是现代容器运行时如Docker、containerd的基础为每个容器提供资源限制确保容器之间的资源隔离防止单个容器消耗过多系统资源2. 服务器资源管理在多用户或多服务的服务器上为不同的服务分配资源份额限制单个服务的资源使用确保关键服务的资源需求3. 边缘设备管理在资源受限的边缘设备上合理分配有限的资源确保核心功能的资源需求防止非关键进程消耗过多资源4. 高性能计算在高性能计算环境中为不同的计算任务分配资源确保计算任务的资源需求提高资源利用率性能优化建议1. 合理设置资源限制根据应用的实际需求设置资源限制避免设置过于严格的限制导致应用性能下降定期监控和调整资源限制2. 优化cgroup层次结构保持cgroup层次结构简单避免过深的嵌套合理组织cgroup根据应用类型和资源需求进行分组避免创建过多的cgroup减少管理开销3. 监控资源使用定期监控cgroup的资源使用情况使用工具如cgroup-monitor、systemd-cgtop等根据监控结果调整资源分配4. 结合其他技术与namespaces结合使用提供更完整的隔离与seccomp结合使用增强安全性与Linux调度器结合使用优化CPU分配代码优化案例1. 容器资源管理// container_cgroup.c #include stdio.h #include stdlib.h #include unistd.h #include fcntl.h #include string.h #define CGROUP_BASE /sys/fs/cgroup // 创建容器的cgroup int create_container_cgroup(const char *container_id) { char cgroup_path[256]; snprintf(cgroup_path, sizeof(cgroup_path), %s/%s, CGROUP_BASE, container_id); return mkdir(cgroup_path, 0755); } // 配置容器的资源限制 int configure_container_resources(const char *container_id, const char *cpu_weight, const char *memory_max, const char *pids_max) { char cgroup_path[256]; char file_path[256]; int fd; snprintf(cgroup_path, sizeof(cgroup_path), %s/%s, CGROUP_BASE, container_id); // 设置CPU权重 snprintf(file_path, sizeof(file_path), %s/cpu.weight, cgroup_path); fd open(file_path, O_WRONLY); if (fd 0) { write(fd, cpu_weight, strlen(cpu_weight)); close(fd); } // 设置内存限制 snprintf(file_path, sizeof(file_path), %s/memory.max, cgroup_path); fd open(file_path, O_WRONLY); if (fd 0) { write(fd, memory_max, strlen(memory_max)); close(fd); } // 设置进程数量限制 snprintf(file_path, sizeof(file_path), %s/pids.max, cgroup_path); fd open(file_path, O_WRONLY); if (fd 0) { write(fd, pids_max, strlen(pids_max)); close(fd); } return 0; } // 将进程加入容器的cgroup int add_process_to_container(const char *container_id, pid_t pid) { char cgroup_path[256]; char procs_path[256]; char pid_str[16]; int fd; snprintf(cgroup_path, sizeof(cgroup_path), %s/%s, CGROUP_BASE, container_id); snprintf(procs_path, sizeof(procs_path), %s/cgroup.procs, cgroup_path); fd open(procs_path, O_WRONLY); if (fd 0) { perror(open); return -1; } snprintf(pid_str, sizeof(pid_str), %d, pid); if (write(fd, pid_str, strlen(pid_str)) 0) { perror(write); close(fd); return -1; } close(fd); return 0; } // 清理容器的cgroup int cleanup_container_cgroup(const char *container_id) { char cgroup_path[256]; snprintf(cgroup_path, sizeof(cgroup_path), %s/%s, CGROUP_BASE, container_id); return rmdir(cgroup_path); } int main(int argc, char *argv[]) { if (argc ! 5) { fprintf(stderr, Usage: %s container_id cpu_weight memory_max pids_max\n, argv[0]); return 1; } const char *container_id argv[1]; const char *cpu_weight argv[2]; const char *memory_max argv[3]; const char *pids_max argv[4]; // 创建cgroup if (create_container_cgroup(container_id) 0) { fprintf(stderr, Failed to create container cgroup\n); return 1; } // 配置资源限制 if (configure_container_resources(container_id, cpu_weight, memory_max, pids_max) 0) { fprintf(stderr, Failed to configure container resources\n); return 1; } // 将当前进程加入cgroup if (add_process_to_container(container_id, getpid()) 0) { fprintf(stderr, Failed to add process to container\n); return 1; } printf(Container %s created with resources: cpu_weight%s, memory_max%s, pids_max%s\n, container_id, cpu_weight, memory_max, pids_max); // 模拟容器运行 while (1) { sleep(1); } return 0; }2. 服务资源管理// service_cgroup.c #include stdio.h #include stdlib.h #include unistd.h #include fcntl.h #include string.h #define CGROUP_BASE /sys/fs/cgroup/services // 初始化服务cgroup层次结构 int init_service_cgroups() { return mkdir(CGROUP_BASE, 0755); } // 创建服务的cgroup int create_service_cgroup(const char *service_name) { char cgroup_path[256]; snprintf(cgroup_path, sizeof(cgroup_path), %s/%s, CGROUP_BASE, service_name); return mkdir(cgroup_path, 0755); } // 配置服务的资源限制 int configure_service_resources(const char *service_name, const char *cpu_weight, const char *memory_max, const char *io_weight) { char cgroup_path[256]; char file_path[256]; int fd; snprintf(cgroup_path, sizeof(cgroup_path), %s/%s, CGROUP_BASE, service_name); // 设置CPU权重 snprintf(file_path, sizeof(file_path), %s/cpu.weight, cgroup_path); fd open(file_path, O_WRONLY); if (fd 0) { write(fd, cpu_weight, strlen(cpu_weight)); close(fd); } // 设置内存限制 snprintf(file_path, sizeof(file_path), %s/memory.max, cgroup_path); fd open(file_path, O_WRONLY); if (fd 0) { write(fd, memory_max, strlen(memory_max)); close(fd); } // 设置I/O权重 snprintf(file_path, sizeof(file_path), %s/io.weight, cgroup_path); fd open(file_path, O_WRONLY); if (fd 0) { write(fd, io_weight, strlen(io_weight)); close(fd); } return 0; } // 将服务进程加入cgroup int add_service_process(const char *service_name, pid_t pid) { char cgroup_path[256]; char procs_path[256]; char pid_str[16]; int fd; snprintf(cgroup_path, sizeof(cgroup_path), %s/%s, CGROUP_BASE, service_name); snprintf(procs_path, sizeof(procs_path), %s/cgroup.procs, cgroup_path); fd open(procs_path, O_WRONLY); if (fd 0) { perror(open); return -1; } snprintf(pid_str, sizeof(pid_str), %d, pid); if (write(fd, pid_str, strlen(pid_str)) 0) { perror(write); close(fd); return -1; } close(fd); return 0; } int main(int argc, char *argv[]) { if (argc ! 5) { fprintf(stderr, Usage: %s service_name cpu_weight memory_max io_weight\n, argv[0]); return 1; } const char *service_name argv[1]; const char *cpu_weight argv[2]; const char *memory_max argv[3]; const char *io_weight argv[4]; // 初始化服务cgroup层次结构 init_service_cgroups(); // 创建服务cgroup if (create_service_cgroup(service_name) 0) { fprintf(stderr, Failed to create service cgroup\n); return 1; } // 配置资源限制 if (configure_service_resources(service_name, cpu_weight, memory_max, io_weight) 0) { fprintf(stderr, Failed to configure service resources\n); return 1; } // 将当前进程加入cgroup if (add_service_process(service_name, getpid()) 0) { fprintf(stderr, Failed to add process to service cgroup\n); return 1; } printf(Service %s created with resources: cpu_weight%s, memory_max%s, io_weight%s\n, service_name, cpu_weight, memory_max, io_weight); // 模拟服务运行 while (1) { sleep(1); } return 0; }总结cgroups v2是Linux内核中一项重要的资源管理技术它为我们提供了一种灵活、高效的方式来管理系统资源。通过cgroups v2我们可以对进程组进行资源限制和监控实现更严格的资源隔离为容器技术提供基础支持优化系统资源的使用作为内核开发者和系统管理员掌握cgroups v2技术是非常重要的。它不仅是容器技术的基础也是系统资源管理的重要工具。随着容器技术的不断发展和普及cgroups v2的重要性将会越来越高。相信在不久的将来cgroups v2将会成为Linux系统中资源管理的标准方案为各种应用场景提供更强大、更灵活的资源管理能力。