Linux control group笔记

Linux CGroup(Control Groups)是一个强大的内核功能,用于限制、记录和隔离进程组(process groups)使用的系统资源(如 CPU、内存、磁盘 I/O、网络等)。它通过将进程分组并对这些组进行资源分配和控制,来实现资源的精细化管理。

🔧 CGroup 核心概念

CGroup 主要围绕以下几个核心概念构建:

  • 子系统 (Subsystem) :也称为控制器 ,是资源控制的具体模块。例如:
    • cpu:控制 CPU 访问。
    • memory:限制内存使用。
    • blkio:限制块设备 I/O。
    • cpuset:分配 CPU 和内存节点。
    • pids:限制进程数量。
  • 控制组 (CGroup):一组进程的集合。每个控制组都可以关联一个或多个子系统,并为这些子系统设置特定的资源限制参数。
  • 层级结构 (Hierarchy)` :控制组以树形结构组织。子控制组默认继承父控制组的属性,但也可自定义。
  • 任务 (Tasks):任务对应进程或线程。任务可以加入某个控制组,从而受到该控制组所有子系统限制规则的约束。

📊 CGroup 的版本

CGroup 主要有两个版本:v1v2 。v2 旨在简化设计并统一资源配置,许多现代 Linux 发行版已默认使用 cgroup v2 (检查 /sys/fs/cgroup/cgroup.controllers 是否存在)。两者在操作和功能上有些差异,建议新项目优先考虑 v2。

🛠️ CGroup 的管理与使用

管理 CGroup 主要有两种方式:

  1. 通过虚拟文件系统 (VFS) 手动操作 :CGroup 通过一个虚拟文件系统(通常挂载在 /sys/fs/cgroup)暴露其接口。你可以通过创建目录、读写文件来创建控制组、设置参数和添加进程。

  2. 使用命令行工具 :如 cgroup-tools 包(例如 cgcreate, cgset, cgexec)提供了更方便的命令行操作。

    cgcreate

    cgdelete

    lscgroup

    cgset

    cgget

    systemd-cgls

    systemd-cgtop

常用命令示例

以下是一些使用 cgroup-tools 的常见命令示例(部分命令在 v2 中可能有所不同或需调整):

功能 命令示例 说明
创建控制组 sudo cgcreate -g cpu,memory:mygroup 创建名为 mygroup 的控制组,并关联 cpumemory 控制器。
设置 CPU 限制 sudo cgset -r cpu.max='50000 100000' mygroup (v2) 在 v2 中,设置 mygroup 在周期 100000us 内最多使用 50000us 的 CPU 时间(即 50% 的单核利用率)。
sudo cgset -r cpu.cfs_quota_us=50000 -r cpu.cfs_period_us=100000 mygroup (v1) 在 v1 中实现同上限制。
设置内存限制 sudo cgset -r memory.max=100M mygroup (v2) 限制 mygroup 最大内存使用为 100MB。
sudo cgset -r memory.limit_in_bytes=100M mygroup (v1) v1 中的内存限制设置。
在 CGroup 中运行进程 sudo cgexec -g cpu,memory:mygroup /path/to/command mygroup 控制组中启动一个进程。
将现有进程加入 CGroup sudo cgclassify -g cpu,memory:mygroup 1234 将 PID 为 1234 的进程加入到 mygroup 控制组。
`echo 1234 sudo tee /sys/fs/cgroup/mygroup/cgroup.procs`
删除控制组 sudo cgdelete mygroup 删除名为 mygroup 的控制组。
直接操作文件系统示例

你也可以直接与 /sys/fs/cgroup 下的文件交互:

bash 复制代码
# 创建控制组 (v1 示例,在cpu控制器下创建mygroup)
sudo mkdir /sys/fs/cgroup/cpu/mygroup

# 设置CPU限制 (v1)
echo 100000 | sudo tee /sys/fs/cgroup/cpu/mygroup/cpu.cfs_period_us
echo 50000 | sudo tee /sys/fs/cgroup/cpu/mygroup/cpu.cfs_quota_us # 限制50%

# 将进程加入控制组
echo 1234 | sudo tee /sys/fs/cgroup/cpu/mygroup/cgroup.procs

# 删除控制组
sudo rmdir /sys/fs/cgroup/cpu/mygroup

💻 C++ 中的 CGroup API 调用

CGroup 主要通过虚拟文件系统提供接口,因此 C++ 中操作 CGroup 本质上就是对文件系统进行读写操作 。你可以使用标准的 C++ 文件操作库(如 <fstream>)或 Linux 系统调用(如 open, write, read)。

下面是一个简单的例子,演示如何使用 C++ 将当前进程添加到指定 CGroup(这里以 v2 的 cgroup.procs 为例):

cpp 复制代码
#include <fstream>
#include <iostream>
#include <string>

bool add_self_to_cgroup(const std::string& cgroup_path) {
    // 获取当前进程的 PID
    pid_t pid = getpid();

    // 构建 cgroup.procs 文件的完整路径
    std::string procs_file_path = cgroup_path + "/cgroup.procs";

    // 以写入模式打开文件
    std::ofstream procs_file(procs_file_path);
    if (!procs_file.is_open()) {
        std::cerr << "Failed to open " << procs_file_path << std::endl;
        return false;
    }

    // 将 PID 写入文件
    procs_file << pid;
    if (procs_file.fail()) {
        std::cerr << "Failed to write PID to " << procs_file_path << std::endl;
        procs_file.close();
        return false;
    }

    procs_file.close();
    std::cout << "Successfully added PID " << pid << " to " << cgroup_path << std::endl;
    return true;
}

int main() {
    // 假设你要加入的 CGroup 路径 (需要根据实际情况修改)
    std::string my_cgroup_path = "/sys/fs/cgroup/my_cgroup";

    if (add_self_to_cgroup(my_cgroup_path)) {
        // 进程现在已经在 CGroup 中了,后续的资源使用将受到限制
        // ... 在这里执行你的受限制任务 ...
        std::cout << "Process is now within the cgroup. Running restricted task..." << std::endl;
        // 模拟一些工作
        volatile int i = 0;
        while (i < 100000000) { i++; }
    } else {
        return 1;
    }

    return 0;
}

重要说明

  • 这段代码需要以 root 权限运行,因为通常只有 root 用户才能修改 CGroup 配置。
  • 代码中的 CGroup 路径 (my_cgroup_path) 需要根据你的系统实际情况和要使用的控制器进行修改。对于 cgroup v2,通常挂载在 /sys/fs/cgroup,而你需要在其下创建或使用已有的子目录。
  • 这只是一个简单的示例,演示如何添加进程。要设置资源限制(如 CPU、内存),你还需要在运行此程序前,手动使用 cgsetecho 到相应文件等方式 ,提前为这个 CGroup 配置好限制参数(例如设置 my_cgroup/cpu.max)。
  • 更复杂的操作(如创建 CGroup、设置各种参数)同样可以通过 C++ 代码读写其他 CGroup 接口文件来实现。逻辑类似:构建正确的文件路径,然后写入符合格式要求的字符串。

⚠️ 注意事项

  1. 权限问题 :操作 CGroup 通常需要 root 权限
  2. 资源争抢 :CGroup 的 CPU 限制(如 cpu.shares)只在进程竞争 CPU 时生效。如果一个进程空闲,它仍然可以使用所有可用 CPU;只有当多个进程需要 CPU 时,份额才起作用。
  3. 版本差异 :务必注意你使用的 Linux 系统是 cgroup v1 还是 v2 ,因为它们的控制器、可调参数和文件系统结构可能不同。检查 /sys/fs/cgroup 的挂载情况。
  4. 系统服务管理 :许多现代 Linux 发行版使用 systemd ,它自身就使用 CGroup 来管理和隔离服务。你可以使用 systemctl set-property 命令来调整系统服务的资源限制。
  5. 错误处理:在实际的程序中,务必添加更健壮的错误处理。

💎 总结

CGroup 是 Linux 系统资源管理的利器。你可以通过:

  • 命令行工具 (如 cgroup-tools)快速上手和管理。
  • 直接操作 /sys/fs/cgroup 下的文件来灵活控制。
  • C++ 程序中使用文件操作 API 来以编程方式与 CGroup 交互,实现进程的资源限制。

希望这些介绍和示例能帮助你更好地理解和使用 Linux CGroup。

⚙️ 查看进程所属的 CGroup

每个运行中的进程在 /proc/ 目录下都有对应的子目录,其中 cgroup 文件明确记录了该进程属于哪个 CGroup。

操作命令

bash 复制代码
cat /proc/<PID>/cgroup

输出示例

bash 复制代码
cat /proc/2467/cgroup
0::/system.slice/example.service

这里的 0::cgroup v2 中表示系统使用的唯一层级。/system.slice/example.service 则指明了该进程属于 example.service 这个 systemd 单元对应的 CGroup。

对于 cgroup v1,输出会更复杂,会显示进程在各个控制器(如 cpu, memory)层级中的路径:

bash 复制代码
12:memory:/user.slice/user-1000.slice
9:blkio:/user.slice/user-1000.slice
8:net_cls,net_prio:/
7:cpu,cpuacct:/user.slice/user-1000.slice
6:perf_event:/
5:freezer:/
4:cpuset:/
3:pids:/user.slice/user-1000.slice
2:devices:/user.slice/user-1000.slice
1:name=systemd:/user.slice/user-1000.slice/session-3.scope

每行由冒号分隔的三部分组成:

  • 第一列 :层级ID(Hierarchy ID),对应 /proc/cgroups 中的信息。
  • 第二列:绑定的控制器(subsystem)列表,多个控制器用逗号隔开。
  • 第三列:该进程在此控制器层级中的路径(相对于CGroup挂载点的路径)。

若进程在容器中,此文件内容通常包含容器ID,例如:

bash 复制代码
0::/kubepods/besteffort/pod779e55c6-0467-4431-997f-25a71a8e8a8e/a9ccdd00512985cb6e6c8dff176cb3086a989e477200c9a1cfdf8749182fc1da

其中的长哈希串 a9ccdd00512985cb6e6c8dff176cb3086a989e477200c9a1cfdef8749182fc1da 通常就是容器ID。

通过进程名查找PID并查看CGroup

若不确定进程PID,可通过进程名先查找PID:

bash 复制代码
# 方法1:使用 ps 和 grep
ps -eo pid,comm | grep <进程名>

# 方法2:使用 pgrep(更简洁)
pgrep <进程名>

然后对找到的PID执行 cat /proc/<PID>/cgroup

也可写成一个简单的脚本:

bash 复制代码
#!/bin/bash
echo "查看进程 '$1' 的CGroup信息:"
for PID in $(pgrep "$1"); do
  echo "PID: $PID"
  cat /proc/$PID/cgroup
  echo "------"
done

保存为 find_cgroup.sh 后使用 bash find_cgroup.sh <进程名> 执行。

🔍 查看CGroup包含的进程

要查看一个CGroup包含了哪些进程,主要可通过查看CGroup文件系统中的应用文件来实现。

1. 通过 cgroup.procs 文件

在CGroup目录中,cgroup.procs 文件列出了该CGroup中的所有进程ID

操作命令

bash 复制代码
cat /sys/fs/cgroup/<控制器>/<CGroup路径>/cgroup.procs

例如

bash 复制代码
# 查看 system.slice/example.service 这个CGroup中的进程(假设在v2的unified层级下)
cat /sys/fs/cgroup/system.slice/example.service/cgroup.procs

# 查看 v1 中 memory 控制器下 /user.slice/user-1000.slice 的进程
cat /sys/fs/cgroup/memory/user.slice/user-1000.slice/cgroup.procs

2. 通过 tasks 文件(主要见于cgroup v1)

在cgroup v1中,tasks 文件列出了该CGroup中的所有线程ID。一个进程的所有线程可能分布在不同的CGroup中。

操作命令

bash 复制代码
cat /sys/fs/cgroup/<控制器>/<CGroup路径>/tasks

注意 :cgroup v2 中已移除了 tasks 文件,统一使用 cgroup.procs

3. 使用 cgclassify 命令

cgclassify 命令可用于将进程分类到指定CGroup,但结合 -g 参数也能用来查询特定进程所属的CGroup(此用法更侧重于查看进程所属CGroup,而非直接列出CGroup中的所有进程)。

bash 复制代码
sudo cgclassify -g <控制器>:<CGroup路径> <进程PID>

但更常用于查看进程所属CGroup信息的是 /proc/<PID>/cgroup 文件。

4. 使用 systemd-cgtop (systemd-cgls)

systemd-cgtop 命令可以像一个"顶级"命令一样,以交互方式按层级实时显示各CGroup的资源使用情况(如CPU、内存),同时也会显示出对应的CGroup名称。

操作命令

bash 复制代码
systemd-cgtop

# output
Path                                                                 Tasks   %CPU   Memory  Input/s Output/s
/system.slice                                                          85    5.0   500.0M        -        -
/system.slice/example.service                                           1   80.0    10.0M        -        -
/user.slice                                                            50    2.0   200.0M        -        -


ubuntu:/proc$ systemd-cgls
Control group /:
-.slice
├─user.slice 
│ └─user-1000.slice 
│   ├─user@1000.service 
│   │ ├─session.slice 
│   │ │ ├─org.gnome.SettingsDaemon.MediaKeys.service 
│   │ │ │ └─2614 /usr/libexec/gsd-media-keys
│   │ │ ├─org.gnome.SettingsDaemon.Smartcard.service 
│   │ │ │ └─2643 /usr/libexec/gsd-smartcard
│   │ │ ├─org.gnome.SettingsDaemon.Datetime.service 
│   │ │ │ └─2608 /usr/libexec/gsd-datetime
│   │ │ ├─xdg-document-portal.service 
│   │ │ │ ├─2048 /usr/libexec/xdg-document-portal
│   │ │ │ └─2059 fusermount3 -o rw,nosuid,nodev,fsname=portal,auto_unmount,subtype=portal>
│   │ │ ├─org.gnome.SettingsDaemon.Housekeeping.service 
│   │ │ │ └─2609 /usr/libexec/gsd-housekeeping
│   │ │ ├─org.gnome.Shell@x11.service 
│   │ │ │ ├─   2406 /usr/bin/gnome-shell
│   │ │ │ ├─   8869 cat
│   │ │ │ ├─   8870 cat
│   │ │ │ ├─  11977 /disk2/soft2/WindTerm_2.5.0_2nd/WindTerm
│   │ │ │ ├─  12025 /bin/bash -i -l
│   │ │ │ ├─  12040 /bin/bash -i -l
│   │ │ │ ├─  12047 /bin/bash -i -l
│   │ │ │ ├─  12120 /bin/bash -i -l
│   │ │ │ ├─  12176 docker start -i 106850ccf3bc
│   │ │ │ ├─  71449 gjs /usr/share/gnome-shell/extensions/ding@rastersoft.com/ding.js -E >
│   │ │ │ ├─ 434225 /bin/bash -i -l
│   │ │ │ ├─ 461638 /bin/bash -i -l
│   │ │ │ ├─ 479174 docker start -i aee853fd0881
│   │ │ │ ├─1767812 /bin/bash -i -l
│   │ │ │ ├─1767852 /bin/bash -i -l
│   │ │ │ ├─2658314 systemd-cgls
│   │ │ │ ├─2658315 pager
│   │ │ │ └─4055392 /bin/bash -i -l

这能帮你快速了解哪些CGroup正在消耗资源。

📊 快速对比:查看CGroup与进程归属的方法

下表总结了上述方法的主要用途和特点:

查询目标 主要方法/命令 关键文件或参数 说明
进程所属的CGroup cat /proc/<PID>/cgroup /proc/<PID>/cgroup 最直接的方法,同时适用于cgroup v1和v2。
cgclassify (查询) -g <控制器>:<CGroup路径> <PID> 更常用于分类进程,查询信息不如/proc直观。
CGroup包含的进程 cat /sys/fs/cgroup/.../cgroup.procs cgroup.procs 列出CGroup中的所有进程ID(v1和v2均适用)。
cat /sys/fs/cgroup/.../tasks (v1) tasks 列出CGroup中的所有线程ID(主要用于cgroup v1)。
监控CGroup资源与进程归属 systemd-cgtop - 交互式实时监控各CGroup资源使用情况,会显示CGroup名称。

💎 实用技巧与注意事项

  1. 区分CGroup版本 :你的系统可能使用 cgroup v1v2 ,两者在文件系统结构和一些文件上有差异(如v2移除了tasks文件)。检查 /sys/fs/cgroup 的挂载情况可以判断版本。
  2. 权限问题 :读取 /proc/<PID>/cgroup 通常不需要特殊权限(只要你有权访问该进程)。但查看或操作 /sys/fs/cgroup/ 下的文件,尤其是修改,通常需要 root 权限。
  3. 容器环境 :在容器环境中,/proc/<PID>/cgroup 文件的内容是识别进程属于哪个容器的重要依据,因为路径常包含容器ID。
  4. 使用 lscgroup 命令 :有些发行版可能安装了 cgroup-tools 包,其中包含 lscgroup 命令,可用于列出所有存在的CGroup。

掌握这些方法,你就能轻松地在进程和CGroup之间进行双向查找了。

相关推荐
搞一搞汽车电子5 小时前
S32K3平台eMIOS 应用说明
开发语言·驱动开发·笔记·单片机·嵌入式硬件·汽车
挺6的还6 小时前
25.线程概念和控制(二)
linux
您的通讯录好友6 小时前
conda环境导出
linux·windows·conda
大筒木老辈子6 小时前
Linux笔记---封装套接字
笔记
代码AC不AC7 小时前
【Linux】vim工具篇
linux·vim·工具详解
AlexMercer10127 小时前
[前端]1.html基础
前端·笔记·学习·html
楚肽生物小敏7 小时前
Cy3-Tyramide,Cyanine 3 Tyramide; 174961-75-2
笔记
码农hbk7 小时前
Linux signal 图文详解(三)信号处理
linux·信号处理
bug攻城狮7 小时前
Skopeo 工具介绍与 CentOS 7 安装指南
linux·运维·centos