[TOC]
cgroups学习笔记
一、名词解释
- hierarchy 类似文件书树的树形结构,树上的节点表示cgroup,hierarchy与一个或多个subsystem,即资源控制器关联,子节点的资源不能超过父节点的资源限制
- cgroup 树形结构上的节点,表示共享资源限制的一组task
- task cgroup中的进程
- subsystem 资源控制器,控制不同系统硬件资源
- blkio 限制对磁盘,固态硬盘,usb等存储设备的访问速率
- cpu 限制cpu时间片占比
- cpuacct 生成cgroup中进程(task)的cpu资源使用情况
- cpuset 将独立的cpu和内存结点分配给cgroup
- devices 控制cgroup对设备的访问权限
- freezer 挂起或恢复cgroup中进程
- memory 限制cgroup的内存使用限制并生成使用情况报告
- net_cls 给网络包打赏标签classid,用于区分不同cgroup的网络包
- net_prio 动态设置网络流量的优先级
- ns namespace subsystem,后续单独讲到
- perf_event 识别task所属的cgroup,并用于性能分析
二、hierarchy、cgroup、subsystem、task的规则
规则 | 示意图 |
---|---|
同一个hierarchy可以关联多个subsystem | |
一个subsystem不能同时关联到两个hierarchy上,前提是其中任意一个已经关联了一个subsystem | |
一个task可以同时是两个hierarchy中的cgroup的成员,但不能同时是同一个hierarchy中两个cgroup的成员 | |
进程fork出的子进程继承原进程的cgroup设置,fork完成后可独立修改 |
上述规则带来的结果:
- 由于一个task在一个hierarchy内只能属于一个cgroup,也就意味着这个hierarchy关联的资源控制器对于这个task只有一种限制方式
- 可以将多个subsystem组合在一起对一个hierarchy起作用
- 可以将一个hierarchy关联的多个subsystem拆分出去并关联其他的hierarchy,类似的也可以进行合并
- 可以对一个task在所有subsystem管理的资源上进行指定的配置
三、cgroups的使用
通过安装libcgroup可以方便的配置cgroups。cgconfig服务利用/etc/cgconfig.conf配置文件和/etc/cgconfig.d/文件夹中的配置文件控制hierarchy的创建,subsystem的关联,cgroup的创建。
hierarchy的挂载示例如下:
1 | mount { |
2 | cpuset = /cgroup/cpuset; |
3 | cpu = /cgroup/cpu; |
4 | cpuacct = /cgroup/cpuacct; |
5 | memory = /cgroup/memory; |
6 | devices = /cgroup/devices; |
7 | freezer = /cgroup/freezer; |
8 | net_cls = /cgroup/net_cls; |
9 | blkio = /cgroup/blkio; |
10 | } |
cgroup创建的示例如下:
1 | group daemons { |
2 | cpuset { |
3 | cpuset.mems = 0; |
4 | cpuset.cpus = 0; |
5 | } |
6 | } |
7 | group daemons/sql { |
8 | perm { |
9 | task { |
10 | uid = root; |
11 | gid = sqladmin; |
12 | } admin { |
13 | uid = root; |
14 | gid = root; |
15 | } |
16 | } |
17 | cpuset { |
18 | cpuset.mems = 0; |
19 | cpuset.cpus = 0; |
20 | } |
21 | } |
lssubsys命令可以列出所有subsystem以及其目前关联的hierarchy。
使用mount命令控制subsystem与hierarchy的关联,包括新增,删除等。
使用unmount可以删掉hierarchy,使用cgclear可以在hierarchy仍然有cgroup的情况下将其删除。
其他命令不再赘述,可参考参考资料。
四、subsystem调参
由于subsystem数量较多,下面主要写一下cpu和memory这两个。
4.1 cpu
假设现有一个叫/cgroup/cpu的hierarchy,关联了cpu subsyatem。
限制一个cgroup使用25%cpu,另一个cgroup使用75%cpu,示例如下:
1 | ~]# echo 250 > /cgroup/cpu/blue/cpu.shares |
2 | ~]# echo 750 > /cgroup/cpu/red/cpu.shares |
限制一个cgroup使用整个cpu示例如下:
1 | ~]# echo 10000 > /cgroup/cpu/red/cpu.cfs_quota_us |
2 | ~]# echo 10000 > /cgroup/cpu/red/cpu.cfs_period_us |
限制一个cgroup使用单个cpu的10%示例如下
1 | ~]# echo 10000 > /cgroup/cpu/red/cpu.cfs_quota_us |
2 | ~]# echo 100000 > /cgroup/cpu/red/cpu.cfs_period_us |
在多核机器上允许一个cgroup使用两个cpu示例如下:
1 | ~]# echo 200000 > /cgroup/cpu/red/cpu.cfs_quota_us |
2 | ~]# echo 100000 > /cgroup/cpu/red/cpu.cfs_period_us |
4.2 memory
下面的示例展示在一个限制了memory使用上限的cgroup中的进程如何被oom killer杀死并发送通知的。
创建一个cgroup并关联memory subsystem
1
~]# mount -t cgroup -o memory memory /cgroup/memory
2
~]# mkdir /cgroup/memory/blue
设置cgroup中的进程使用内存的上线为100m
1
~]# echo 104857600 > memory.limit_in_bytes
进入cgroup目录确认oom killer已开启
1
~]# cd /cgroup/memory/blue
2
blue]# cat memory.oom_control
3
oom_kill_disable 0
4
under_oom 0
将当前shell进程移动至cgroup中的tasks文件中,这样在这个shell中启动的程序就自动加入了这个cgroup
1
blue]# echo $$ > tasks
启动一个测试程序,测试程序尝试分配大量内存超出限制。一旦达到cgroup的内存上线就会被oom killer杀死。
1
blue]# ~/mem-hog
2
Killed
测试程序如下:
1
2
3
4
5
6
7
8
9
10
int main(int argc, char *argv[])
11
{
12
char *p;
13
14
again:
15
while ((p = (char *)malloc(GB)))
16
memset(p, 0, GB);
17
18
while ((p = (char *)malloc(MB)))
19
memset(p, 0, MB);
20
21
while ((p = (char *)malloc(KB)))
22
memset(p, 0,
23
KB);
24
25
sleep(1);
26
27
goto again;
28
29
return 0;
30
}
关闭oom killer,然后再启动测试程序,测试程序用完内存后会一直暂停,直到有新的可用内存出现
1
blue]# echo 1 > memory.oom_control
2
blue]# ~/mem-hog
尽管测试程序没有被杀死,但cgroup的under_oom状态已经表明达到了内存上限。重新启动oom killer会将测试进程立刻杀死
1
~]# cat /cgroup/memory/blue/memory.oom_control
2
oom_kill_disable 1
3
under_oom 1
为了获得oom的通知,创建一个接收通知的程序oom_notification
1
2
3
4
5
6
7
8
9
10
static inline void die(const char *msg)
11
{
12
fprintf(stderr, "error: %s: %s(%d)\n", msg, strerror(errno), errno);
13
exit(EXIT_FAILURE);
14
}
15
16
static inline void usage(void)
17
{
18
fprintf(stderr, "usage: oom_eventfd_test <cgroup.event_control> <memory.oom_control>\n");
19
exit(EXIT_FAILURE);
20
}
21
22
23
24
int main(int argc, char *argv[])
25
{
26
char buf[BUFSIZE];
27
int efd, cfd, ofd, rb, wb;
28
uint64_t u;
29
30
if (argc != 3)
31
usage();
32
33
if ((efd = eventfd(0, 0)) == -1)
34
die("eventfd");
35
36
if ((cfd = open(argv[1], O_WRONLY)) == -1)
37
die("cgroup.event_control");
38
39
if ((ofd = open(argv[2], O_RDONLY)) == -1)
40
die("memory.oom_control");
41
42
if ((wb = snprintf(buf, BUFSIZE, "%d %d", efd, ofd)) >= BUFSIZE)
43
die("buffer too small");
44
45
if (write(cfd, buf, wb) == -1)
46
die("write cgroup.event_control");
47
48
if (close(cfd) == -1)
49
die("close cgroup.event_control");
50
51
for (;;) {
52
if (read(efd, &u, sizeof(uint64_t)) != sizeof(uint64_t))
53
die("read eventfd");
54
55
printf("mem_cgroup oom event received\n");
56
}
57
58
return 0;
59
}
再另一个控制台运行接收通知的程序oom_notification ,再在另一个控制台运行测试程序,可以看到在oom_notification 程序的std output中展示字符串”mem_cgroup oom event received“
1
~]$ ./oom_notification /cgroup/memory/blue/cgroup.event_control /cgroup/memory/blue/memory.oom_control
1
blue]# ~/mem-hog
五、使用场景
- 使用blkio subsyatem提升数据量io的优先级
- 使用net_prio subsystem修改网络流量的优先级
- 给不同的用户组按比例分配cpu和内存