了解cgroups

什么是cgroup?

cgroup是control group的缩写,字面意思是控制组,控制什么? 什么组? 操作系统是资源管家,为进程分配各种资源,这些资源有CPU, 内存,块设备等。如何为一组进程(一个进程是一组进程的特例)对资源的使用进行量化记账和限制?cgroup就是解决这个问题的,提供一个统一的机制,各子系统根据这个机制实现自身控制策略。

进程组是如何组织的? 答案:层级 (hierarchy)。进程按层级组织,各子系统(subsystem)按照自身资源分配策略在层级树上进行资源分配。打个比方,每个人像是一个进程,有户口所在地如中国X省Y市,户籍就是进程组,有三个层级国省市,社会有各种资源比如教育子系统,假如我是Y市某个学校的学生,那么教育部门就会给这个学校分配老师和设施等资源。

为什么需要cgroup?

举个来自内核文档(Documentation/cgroup-v1/cgroups.txt)中的栗子。试想,一个大学的服务器有不同用户:学生,教授和系统任务。那这个服务器的资源可能需要这样安排。CPU:

       CPU :          "Top cpuset"
                       /       \
               CPUSet1         CPUSet2
                  |               |
               (Professors)    (Students)

               In addition (system tasks) are attached to topcpuset (so 
               that they can run anywhere) with a limit of 20% 

内存:

Memory : Professors (50%), Students (30%), system (20%)

磁盘:

Disk : Professors (50%), Students (30%), system (20%)

网络:

 Network : WWW browsing (20%), Network File System (60%), others (20%)
                               / \
               Professors (15%)  students (5%)

比如浏览器firefox走的WWW服务,管理员可以写脚本程序知道哪类用户教授还是学生在使用firefox, 然后做如下控制:

# echo browser_pid > /sys/fs/cgroup/<restype>/<userclass>/tasks

假如管理员体察民情,到了晚上,想给学生给多网络资源,好让学生可以玩网络游戏放松一下。管理可以在网络子系统里新建一个资源组,放宽流量限制,暂时把学生启动的进程放到新建的资源组里:

       # echo pid > /sys/fs/cgroup/network/<new_class>/tasks
       (after some time)
       # echo pid > /sys/fs/cgroup/network/<orig_class>/tasks

层级,子系统,cgroup和进程之间的关系?

这个文档有配图有文字讲的比较清楚:Relationships Between Subsystems, Hierarchies, Control Groups and Tasks

  1. 一个层级可以附加一个或多个子系统。例如,层级cpu_mem_cg有两个子系统CPU和内存。
         /cpu_mem_cg   |    <-------------CPU
              +-----/cg1       |   <-------------Memory
              +-----/cg2       |
  1. 一个子系统(如CPU)不可以同时附加到两个不同的层级中。例如,CPU子系统已经附加到了层级cpu_cg中,就不能再附加到另一个层级cpu_mem_cg中。
  2. 每个层级在新建时,都有一个default cgroup, 也叫root cgroup, 系统所有进程会被自然地加入root cgroup。在同一个层级中,一个进程只能属于一个cgroup中,例如cpu_mem_cg层级有两个cgroup: cg1和cg2,进程A不能同在cg1和cg2中。当然,一个进程可以被添加到多个cgroup中,只要这些cgroup在不同的层级当中,比如层级cpu_cg->cg1和层级mem_cg->cg1,进程A可以同时被添加到两个cg1中。
  3. 子进程创建之初,继承父进程的层级关系。但之后,关系可以删除,或改变。

cgroup是怎么实现的?

虽然2015年Tejun重写了cgroup v2, 但是cgroup v1版的文档还是有参考意义。主流发行版还没有用那么新的内核,应该还都在使用cgroup v1。这里简单摘录一下cgroup-v1文关于实现的文档。

cgroup在内核中是这样做的:

  • 每个task有个指针指向css_set。css,即cgroup subsystem state。
  • css_set有一个指针数组,每个指针指向cgroup_subsys_state对象。每个对象代表一个子系统,可顺着css_set -> cgroup_subsys_state指针找到进程所在的cgroup。
  • cgroup hierarchy filesystem 可以mount上,用户通过操作文件系统的方式使用cgroup。
  • 你可以查看哪些进程(PID)被添加到了某个cgroup中。 cgroup的实现需要在内核各自系统中插入一些hook,比如:在init/main.c中,初始化root cgroups 和 css_set; 在fork()/exit()中,把任务附加/或从css_set中移除。

cgroup实现了类似procfs内存文件系统,即cgroup文件系统。挂载时,默认情况下, cgroup文件系统会把所有子系统添加到层级中。用户可以通过挂载参数如mount -t cgroup -o cpu cgroup /sys/cgroup/cpu来选择附加到层级中的子系统。

其实内核文档对实现讲的并不清楚,可参考这个博客Cgroup框架分析

blkio子系统

cgroups 子系统有多种, 如cpu, memory, 更全介绍参考Cgroup简介-概述。笔者暂时只对blkio子系统感兴趣。blkio资源控制模式有两种: weight 和 bandwidth limit, 即使权重,或带宽(iops)限制。权重的方式只有在IO调度器为cfq时才有用,但是目前blk-mq设备不支持任何调度器。

IO相关的还有cgroup writeback, 因为回写跟内存子系统和blkio子系统都有关系。值得一提的是该功能需要文件系统的支持,目前ext4和btrfs可以支持。

这部分后面需要研究下限流算法和代码。

猜你喜欢

转载自my.oschina.net/u/2475751/blog/1579871