Cgroup之cpuacct子系统

cpuacct子系统(CPU accounting)会自动生成报告来显示cgroup中任务所使用的CPU资源,其中包括子群组任务。报告有两大类:

usage: 统计cgroup中进程使用CPU的时间,单位为纳秒。
stat: 统计cgroup中进程使用CPU的时间,单位为USER_HZ。
注意:本文中引用的内核代码版本为v5.2

统计文件示例
usage*
cpuacct.usage : 报告一个cgroup中所有任务(包括其子孙层级中的所有任务)使用CPU的总时间(纳秒),该文件时可以写入0值的,用来进行重置统计信息。
cpuacct.usage_percpu: 报告一个cgroup中所有任务(包括其子孙层级中的所有任务)在每个CPU使用CPU的时间(纳秒)。
cpuacct.usage_user: 报告一个cgroup中所有任务(包括其子孙层级中的所有任务)使用用户态CPU的总时间(纳秒)。
cpuacct.usage_percpu_user 报告一个cgroup中所有任务(包括其子孙层级中的所有任务)在每个CPU上使用用户态CPU的时间(纳秒)。
cpuacct.usage_sys: 报告一个cgroup中所有任务(包括其子孙层级中的所有任务)使用内核态CPU的总时间(纳秒)。
cpuacct.usage_percpu_sys:报告一个cgroup中所有任务(包括其子孙层级中的所有任务)在每个CPU上使用内核态CPU的时间(纳秒)。
cpuacct.usage_all:详细输出文件cpuacct.usage_percpu_user和cpuacct.usage_percpu_sys的内容。
stat
cpuacct.stat:报告cgroup的所有任务(包括其子孙层级中的所有任务)使用的用户和系统CPU时间,方式如下:
user——用户模式中任务使用的CPU时间
system——系统模式中任务使用的CPU时间
其单位为USER_HZ
示例
查看使用cpu的总时间
root@2d1230403171:~# cat /sys/fs/cgroup/cpuacct/cpuacct.usage
3907906171712
root@2d1230403171:~# cat /sys/fs/cgroup/cpuacct/cpuacct.usage_percpu
982052931576 496801928082 1119303415003 1309764865266
查看用户态和内核态的CPU时间
root@2d1230403171:~# cat /sys/fs/cgroup/cpuacct/cpuacct.usage_user
3907908045318
root@2d1230403171:~# cat /sys/fs/cgroup/cpuacct/cpuacct.usage_percpu_user
982051860949 496800539857 1119297079712 1309763110228
root@2d1230403171:~# cat /sys/fs/cgroup/cpuacct/cpuacct.usage_sys
0
root@2d1230403171:~# cat /sys/fs/cgroup/cpuacct/cpuacct.usage_percpu_user
982052487842 496801115881 1119301915660 1309764696892
查看cpuacct.usage_all
root@2d1230403171:~# cat /sys/fs/cgroup/cpuacct/cpuacct.usage_all
cpu user system
0 982052931576 0
1 496802375225 0
2 1119304751435 0
3 1309766189582 0
重置统计值
root@2d1230403171:~# echo 0 > /sys/fs/cgroup/cpuacct/cpuacct.usage
注意:需要有相应的权限才能重置。

查看stat
root@2d1230403171:~# cat /sys/fs/cgroup/cpuacct/cpuacct.stat
user 389913
system 403
Usage 和 Stat 到底有什么区别呢?
root@2d1230403171:~# cd /sys/fs/cgroup/cpuacct
root@2d1230403171:/sys/fs/cgroup/cpuacct# cat cpuacct.stat ; cat cpuacct.usage_user; cat cpuacct.usage_sys
user 124526
system 468815
4070860587371
354066574154
我们可以看到,stat中的user时间加上的system时间和cpuacct.usage_user的时间加上cpuacct.usage_sys的时间不相等。到底哪个比较精确呢?

cpuacct.usage 统计了所有 CPU 核的累加使用时间,单位是纳秒。在 cpuacct.stat 中统计了该控制组中进程用户态和内核态的CPU使用量,其单位是USER_HZ。

注意,相比 cpuacct.stat 来说,cpuacct.usage 的值会更加精确一些。

内核实现
结构体struct cpuacct
cpuacct的内核实现中,对cpu时间的统计结果都存放到数据结构struct cpuacct中,数据结构定义如下: kernel/sched/cpuacct.c(line 27-33)

/* track CPU usage of a group of tasks and its child groups /
struct cpuacct {
struct cgroup_subsys_state css;
/
cpuusage holds pointer to a u64-type object on every CPU */
struct cpuacct_usage __percpu *cpuusage;
struct kernel_cpustat __percpu *cpustat;
};
除了css外,其他两个成员都是__percpu类型。

cpuusge 记录每个cpu使用的时间, 单位为纳秒
cpustat 记录每个cpu使用的用户和系统CPU时间,单位为USER_HZ
结构体 struct cpuacct_usage
数据结构定义如下: kernel/sched/cpuacct.c(line 10-25)

/* Time spent by the tasks of the CPU accounting group executing in … /
enum cpuacct_stat_index {
CPUACCT_STAT_USER, /
… user mode /
CPUACCT_STAT_SYSTEM, /
… kernel mode */

CPUACCT_STAT_NSTATS,

};

static const char * const cpuacct_stat_desc[] = {
[CPUACCT_STAT_USER] = “user”,
[CPUACCT_STAT_SYSTEM] = “system”,
};

struct cpuacct_usage {
u64 usages[CPUACCT_STAT_NSTATS];
};
结构体 struct kernel_cpustat
数据结构定义如下: include/linux/kernel_stat.h(line 14-36)

/*

  • ‘kernel_stat.h’ contains the definitions needed for doing
  • some kernel statistics (CPU usage, context switches …),
  • used by rstatd/perfmeter
    */

enum cpu_usage_stat {
CPUTIME_USER,
CPUTIME_NICE,
CPUTIME_SYSTEM,
CPUTIME_SOFTIRQ,
CPUTIME_IRQ,
CPUTIME_IDLE,
CPUTIME_IOWAIT,
CPUTIME_STEAL,
CPUTIME_GUEST,
CPUTIME_GUEST_NICE,
NR_STATS,
};

struct kernel_cpustat {
u64 cpustat[NR_STATS];
};
cpuacct.stat中的统计时间主要来源于该结构体,其中

user时间包括:CPUTIME_USER + CPUTIME_NICE
system时间包括:CPUTIME_IRQ + CPUTIME_SOFTIRQ + CPUTIME_SYSTEM
变量root_cpuacct
定义如下: kernel/sched/cpuacct.c(line 51-55) 和 include/linux/kernel_stat.h

struct kernel_cpustat {
u64 cpustat[NR_STATS];
};

DECLARE_PER_CPU(struct kernel_cpustat, kernel_cpustat);

static DEFINE_PER_CPU(struct cpuacct_usage, root_cpuacct_cpuusage);
static struct cpuacct root_cpuacct = {
.cpustat = &kernel_cpustat,
.cpuusage = &root_cpuacct_cpuusage,
};

通过上面的数据结构分析,我们可以画出struct cpuacct的结构示意图:

/sys/fs/cgroup/cpuacct下所有统计文件就是通过cpuacct结构体中的统计值来输出信息的。

而cpu时间信息的更新则由如下函数完成( include/linux/cgroup.h(line 753-763) )。

cpuacct_charge
用于更新cpuusage( kernel/sched/cpuacct.c(line 333-353) ), 该函数更新所有的cpuacct cgroup,包括根root cpuacct cgroup。

cpuacct_account_field
用于更新cpustat( kernel/sched/cpuacct.c(line 355-368) ),该函数更新所有的cpuacct cgroup,但不包括root cpuacct cgroup。

那么哪些函数会调用cpuacct_charge呢?
如下函数会去调用cpuacct_charge:

update_curr(struct cfs_rq *cfs_rq)->cgroup_account_cputime->cpuacct_charge
update_curr_rt(struct rq *rq)->cgroup_account_cputime->cpuacct_charge
update_curr_dl(struct rq *rq)->cgroup_account_cputime->cpuacct_charge
put_prev_task_stop(struct rq *rq, struct task_struct *prev)->cgroup_account_cputime->cpuacct_charge
那么哪些函数会调用cpuacct_account_field呢?
如下函数会去调用cpuacct_account_field:

account_process_tick->account_user_time->task_group_account_field->cgroup_account_cputime_field->cpuacct_account_field
irqtime_account_process_tick->account_user_time->task_group_account_field->cgroup_account_cputime_field->cpuacct_account_field
vtime_user_exit->account_user_time->task_group_account_field->cgroup_account_cputime_field->cpuacct_account_field

account_process_tick->account_system_time->account_system_index_time->task_group_account_field->cgroup_account_cputime_field->cpuacct_account_field

irqtime_account_process_tick->account_system_index_time->task_group_account_field->cgroup_account_cputime_field->cpuacct_account_field

__vtime_account_system->account_system_time->account_system_index_time->task_group_account_field->cgroup_account_cputime_field->cpuacct_account_field

除此之外,还有如下函数会更新cupstat

account_idle_time
account_steal_time
account_guest_time
总结一下
更新cpustat的接口有如下几个:

account_user_time
account_system_time
irqtime_account_process_tick
account_idle_time
account_steal_time
account_guest_time
分析更新cpustat的接口实现
account_user_time
代码地址为: kernel/sched/cputime.c(line 112-132)

该接口根据task_nice§是否为真,更新CPUTIME_NICE或者CPUTIME_NICE

account_system_time
代码地址为: kernel/sched/cputime.c(line 178-201)

该接口根据不同的情况可能更新CPUTIME_IRQ 或者 CPUTIME_SOFTIRQ 或者 CPUTIME_SYSTEM

注意,该接口还有可能通过接口account_guest_time进行时间的更新。

account_idle_time
代码地址为: kernel/sched/cputime.c(line 214-227)

该接口更新了CPUTIME_IDLE或者CPUTIME_IOWAIT,只更新了root cpuacct,即idle时间不是cgroup aware的。

account_steal_time
代码地址为: kernel/sched/cputime.c(line 203-212)

该接口更新了CPUTIME_STEAL,只更新了root cpuacct,即steal时间不是cgroup aware的。

account_guest_time
代码地址为: kernel/sched/cputime.c(line 134-156)

该接口根据task_nice§是否为真,更新CPUTIME_NICE and CPUTIME_GUEST_NICE或者CPUTIME_USER and CPUTIME_GUEST, 只更新了root cpuacct,即guest时间不是cgroup aware的。

其它相关问题
root cpuacct的数据来源?
root cpuacct cgroup中usage来源于变量root_cpuacct_cpuusage,在cpuacct_charge中会更新它的值。
root cpuacct cgroup中cpustat来源于变量kernel_cpustat,cpuacct_account_field并不会更新它,而是有系统上其它部分去更新。
/proc/stat中cpu相关统计信息来自哪里?
/proc/stat中cpu相关统计数据来自于变量kernel_cpustat,这个跟root cpuacct cgroup的数据来源是一样的。
docker如何计算容器的cpu利用率?
docker容器的的cpu利用率计算公司如下:

(cpuuasge的delta值/墙上时间delta) * 100%

cpuuasge的delta值: 通过两次读取cpuacct.useage得到
墙上时间delta: 通过两地读取/proc/stat的第一行得到,由于该行的值为墙上时间*cpu核数,所以该值应该再除以cpu核数。

转载https://www.dazhuanlan.com/2019/10/17/5da81a4059807/

<think>好的,我现在需要解决用户遇到的“mounting cpu_group failed: Invalid argument”错误。首先,我要理解这个错误发生的背景。用户可能在尝试挂载cgroup子系统,特别是CPU子系统时遇到了问题。根据用户提供的引用内容,例如引用[2]中提到v4环境中如果存在/cgroup/cpu/路径会导致服务重启失败,这可能与cgroup的版本有关。 首先,我需要确定用户使用的cgroup版本。因为cgroup v1和v2在挂载方式上有差异。在v1中,每个子系统(如cpu、memory)需要单独挂载,而v2使用单一统一层次结构。如果用户混合使用两种版本的挂载方式,可能会导致冲突,从而出现无效参数的错误。 接下来,检查挂载点的路径是否正确。引用[3]提到创建cpu subsys的cgroups,可能在v1中需要正确指定子系统类型。如果挂载命令中子系统类型错误,比如误写为cpu_group而不是cpu,就会导致失败。用户提到的错误信息中的“cpu_group”可能是一个拼写错误,正确的应该是cpu。 然后,考虑内核是否支持对应的子系统。如果内核编译时未启用cgroup CPU子系统,挂载时会失败。用户可以通过检查/proc/cgroups文件中的cpu条目是否存在,并确认是否已启用。引用[3]中提到从/proc/cgroups查询子系统,所以这一步很重要。 另外,挂载路径的权限问题也可能导致错误。确保挂载点目录存在,并且有适当的权限。如果目录不存在,需要先创建;如果权限不足,可能需要使用sudo或者调整目录权限。 还有可能与其他已挂载的cgroup冲突。例如,在v2中已经挂载了统一层次结构,再尝试挂载v1的子系统会导致错误。引用[2]指出在v4环境中存在旧路径可能导致失败,所以需要清理旧的挂载点。 最后,检查挂载命令的语法是否正确。正确的v1挂载命令应该是“mount -t cgroup -o cpu,cpuacct none /sys/fs/cgroup/cpu,cpuacct”。如果参数顺序或选项错误,系统会返回无效参数错误。 综合这些可能性,我需要逐步引导用户检查cgroup版本、子系统状态、挂载点、权限以及命令语法,从而找到具体原因并解决。</think>### 导致 "mounting cpu_group failed: Invalid argument" 的常见原因及解决方案 #### 1. **cgroup 版本不兼容** - **原因**: 系统可能已默认使用 cgroup v2,但尝试以 v1 的方式挂载子系统(如单独挂载 `cpu`)。cgroup v2 要求以统一层级结构挂载,不支持单独挂载子系统[^2]。 - **验证方法**: ```bash cat /proc/filesystems | grep cgroup ``` 若输出包含 `cgroup2`,则系统默认使用 v2。 - **修复方案**: - **方法1**: 切换到 cgroup v1 在启动参数中添加 `systemd.unified_cgroup_hierarchy=0`,例如修改 `/etc/default/grub` 中的 `GRUB_CMDLINE_LINUX`: ```bash GRUB_CMDLINE_LINUX="systemd.unified_cgroup_hierarchy=0" ``` 更新配置后重启系统。 - **方法2**: 清理冲突挂载点 若存在残留的旧挂载路径(如 `/cgroup/cpu`),需先卸载并删除目录: ```bash umount /cgroup/cpu 2>/dev/null rm -rf /cgroup/cpu ``` #### 2. **子系统名称错误** - **原因**: 使用错误子系统名称(如 `cpu_group` 而非 `cpu`)。cgroup v1 的 CPU 子系统名为 `cpu`,需搭配 `cpuacct` 使用[^3]。 - **修复方案**: 正确挂载命令应为: ```bash mount -t cgroup -o cpu,cpuacct none /sys/fs/cgroup/cpu,cpuacct ``` #### 3. **内核未启用 CPU 子系统** - **原因**: 内核编译时未启用 `CONFIG_CGROUP_SCHED` 或 `CONFIG_CGROUP_CPUACCT`。 - **验证方法**: ```bash cat /proc/cgroups | grep cpu ``` 若输出中 `cpu` 和 `cpuacct` 的 `enabled` 列为 `0`,则子系统未启用。 - **修复方案**: 重新编译内核并启用以下选项: ```config CONFIG_CGROUP_SCHED=y CONFIG_CGROUP_CPUACCT=y ``` #### 4. **挂载路径权限问题** - **原因**: 挂载目标目录不存在或权限不足。 - **修复方案**: ```bash mkdir -p /sys/fs/cgroup/cpu,cpuacct chmod 755 /sys/fs/cgroup/cpu,cpuacct ``` #### 5. **与其他 cgroup 配置冲突** - **原因**: 系统服务(如 `systemd` 或 `cgconfig`)已管理 cgroup 配置,导致手动挂载冲突。 - **修复方案**: 停止相关服务后再挂载: ```bash systemctl stop cgconfig.service mount -t cgroup -o cpu,cpuacct none /sys/fs/cgroup/cpu,cpuacct ``` --- ### 操作验证流程 1. **检查 cgroup 版本** ```bash stat -fc %T /sys/fs/cgroup/ ``` - 输出 `cgroup2fs` 表示 v2,`tmpfs` 表示 v1。 2. **验证子系统状态** ```bash grep -E 'cpu|cpuacct' /proc/cgroups ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值