cgroups - Linux control groups
早些时候简单了解过cgroup( Linux中cgroup的初级方法 ),当时了解地太浅了,想要做一些或调查一些和cgroup有关的问题时,感觉无法下手。 需要深入学习一下cgroup。Linux手册中有对cgroup的介绍,这篇笔记中内容主要来自这份文档:man 7 cgroups, cgroups - Linux control groups 。
术语
这篇笔记有可能是第一篇详细、全面介绍了cgroup的中文资料,有必要约定术语、统一口径,这样可以减少交流障碍。
process 是“ 进程 ”, task 是“ 线程 ”。
subsystem 或者 resource controllers 是cgroup中某一类资源的管理器,例如管理cpu资源cpu controller,管理内存的memory controller,统一称呼为“ 控制器 ”。
controller要用使用 mount -t cgroup 样式的命令挂载到一个目录中,这个操作称呼为“ 挂载 ”。
从linux kernel 4.14开始,cgroup v2 引入了 thread mode (线程模式),controller被分为 domain controller 和 threaded controller 两类,前者称呼为“ 进程控制器 ”,后者称呼为“ 线程控制器 ”。
从使用的角度看,cgroup就是一个目录树,目录中可以创建子目录,这些目录称呼为“ cgroup 目录 ”,在一些场景中为了体现层级关系,还会有“ cgroup 子目录 ”的叫法。
每个目录中有一些用来设置对应controller的文件,这些文件称呼为“ 控制器的文件接口 ”。
cgroup v2引入了thread mode(线程模式)之后,cgroup目录有了类型之分:原先的只面对进程的cgroup目录是 domain cgroup ,称呼为“ 进程(子)目录 ”;新增的面对线程的cgroup目录称为 threaded cgroup ,称呼为“ 线程子目录 ”。
一句话介绍cgroup:把一个cgroup目录中的资源划分给它的子目录,子目录可以把资源继续划分给它的子目录,为子目录分配的资源之和不能超过父目录,进程或者线程可以使用的资源受到它们委身的目录的限制。
版本
cgroup有v1和v2两个版本,这是一个 非常重要 的信息。
v1版本是最早的实现,当时resource controllers的开发各自为政,导致controller间存在不一致,并且controller的嵌套挂载使cgroup的管理非常复杂。
Linux kernel 3.10 开始提供v2版本cgroup( Linux Control Group v2 )。开始是试验特性,隐藏在挂载参数 -o __DEVEL__sane_behavior 中,直到 Linuxe Kernel 4.5.0 的时候,cgroup v2才成为正式特性。
cgroup v2希望完全取代cgroup v1,但是为了兼容,cgroup v1没有被移除。
cgroup v2实现的controller是cgroup v1的子集,可以同时使用cgroup v1和cgroup v2,但一个controller不能既在cgroup v1中使用,又在cgroup v2中使用。
cgroups version 1
cgroup v1中,controller可以独立挂载到一个cgroup目录中,也可以和其它controller联合挂载到同一个cgroup目录,cgroup v2也是采用挂载的方式,但是有一些不同(见后文)。
在cgroup v1中,task也就是线程可以被划分到不同的cgroup组中,在一些场景中,这样做是有问题的。
例如在memory controller中,所有task使用的都是同样的内存地址空间,为它们设置不同memory cgroup是没有意义的。(cgroup v2最初将task功能去掉了,后来引入了 thread mode 来限制线程占用的资源)
cgroups v1:controller 挂载
controller以 tmpfs 文件系统的样式挂载到任意目录,通常将其挂载到/sys/fs/cgroup目录。
linux系统通常已经将多个controller挂载在/sys/fs/cgroup目录中了,下面的例子用另一个目录演示。
将多个controller挂载到同一个目录,如下:
mkdir -p /tmp/cgroup/cpu,cpuacct
mount -t cgroup -o cpu,cpuacct none /tmp/cgroup/cpu,cpuacct
-t cgroup 指定挂载类型, -o 指定挂载的controller(可以有多个,用“,”间隔)。
单独挂载cpu时,提示“已经挂载或者/tmp/cgroup/cpu is busy,暂时不清楚是怎么回事,可能是有一些controller不允许重复挂载。
$ mkdir /tmp/cgroup/cpu
$ mount -t cgroup -o cpu none /tmp/cgroup/cpu
mount: none is already mounted or /tmp/cgroup/cpu busy
挂载后,可以在挂载目录中看到controller的文件接口:
$ ls -F /tmp/cgroup/cpu,cpuacct/
cgroup.clone_children cpuacct.stat cpu.cfs_quota_us cpu.stat kube-proxy/ tasks
cgroup.event_control cpuacct.usage cpu.rt_period_us docker/ notify_on_release user.slice/
cgroup.procs cpuacct.usage_percpu cpu.rt_runtime_us kubelet/ release_agent
cgroup.sane_behavior cpu.cfs_period_us cpu.shares kubepods/ system.slice/
可以一次挂载所有的controller:
mount -t cgroup -o all cgroup /tmp/cgroup
还可以不挂载任何controller:
mount -t cgroup -o none,name=somename none /some/mount/point
没有挂载controller的cgroup可以用来跟踪进程,例如在进程消失导致cgroup为空时,cgroup的通知回调会被触发。
cgroups v1:controller 卸载
直接用umount卸载:
umount /sys/fs/cgroup/pids
卸载的时候要注意