Cgroup之cpuacct子系统
Contents
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的总时间
|
|
查看用户态和内核态的CPU时间
|
|
查看cpuacct.usage_all
|
|
重置统计值
|
|
注意:需要有相应的权限才能重置。
查看stat
|
|
Usage 和 Stat 到底有什么区别呢?
|
|
我们可以看到,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)
|
|
除了css外,其他两个成员都是__percpu
类型。
cpuusge
记录每个cpu
使用的时间, 单位为纳秒cpustat
记录每个cpu
使用的用户和系统CPU
时间,单位为USER_HZ
结构体 struct cpuacct_usage
数据结构定义如下: kernel/sched/cpuacct.c(line 10-25)
|
|
结构体 struct kernel_cpustat
数据结构定义如下: include/linux/kernel_stat.h(line 14-36)
|
|
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 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(p)
是否为真,更新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(p)
是否为真,更新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
值: 通过两次读取cpuacct.useage
得到- 墙上时间
delta
: 通过两地读取/proc/stat
的第一行得到,由于该行的值为墙上时间*cpu核数,所以该值应该再除以cpu
核数。