0%

cgroups学习笔记

[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 Rule 1
一个subsystem不能同时关联到两个hierarchy上,前提是其中任意一个已经关联了一个subsystem Rule 2—The numbered bullets represent a time sequence in which the subsystems are attached.
一个task可以同时是两个hierarchy中的cgroup的成员,但不能同时是同一个hierarchy中两个cgroup的成员 Rule 3
进程fork出的子进程继承原进程的cgroup设置,fork完成后可独立修改 Rule 4—The numbered bullets represent a time sequence in which the task forks.

上述规则带来的结果:

  • 由于一个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杀死并发送通知的。

  1. 创建一个cgroup并关联memory subsystem

    1
    ~]# mount -t cgroup -o memory memory /cgroup/memory
    2
    ~]# mkdir /cgroup/memory/blue
  2. 设置cgroup中的进程使用内存的上线为100m

    1
    ~]# echo 104857600 > memory.limit_in_bytes
  3. 进入cgroup目录确认oom killer已开启

    1
    ~]# cd /cgroup/memory/blue
    2
    blue]# cat memory.oom_control
    3
    oom_kill_disable 0
    4
    under_oom 0
  4. 将当前shell进程移动至cgroup中的tasks文件中,这样在这个shell中启动的程序就自动加入了这个cgroup

    1
    blue]# echo $$ > tasks
  5. 启动一个测试程序,测试程序尝试分配大量内存超出限制。一旦达到cgroup的内存上线就会被oom killer杀死。

    1
    blue]# ~/mem-hog
    2
    Killed

    测试程序如下:

    1
    #include <stdio.h>
    2
    #include <stdlib.h>
    3
    #include <string.h>
    4
    #include <unistd.h>
    5
    6
    #define KB (1024)
    7
    #define MB (1024 * KB)
    8
    #define GB (1024 * MB)
    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
    }
  6. 关闭oom killer,然后再启动测试程序,测试程序用完内存后会一直暂停,直到有新的可用内存出现

    1
    blue]# echo 1 > memory.oom_control
    2
    blue]# ~/mem-hog
  7. 尽管测试程序没有被杀死,但cgroup的under_oom状态已经表明达到了内存上限。重新启动oom killer会将测试进程立刻杀死

    1
    ~]# cat /cgroup/memory/blue/memory.oom_control
    2
    oom_kill_disable 1
    3
    under_oom 1
  8. 为了获得oom的通知,创建一个接收通知的程序oom_notification

    1
    #include <sys/types.h>
    2
    #include <sys/stat.h>
    3
    #include <fcntl.h>
    4
    #include <sys/eventfd.h>
    5
    #include <errno.h>
    6
    #include <string.h>
    7
    #include <stdio.h>
    8
    #include <stdlib.h>
    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
    #define BUFSIZE 256
    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
    }
  9. 再另一个控制台运行接收通知的程序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和内存

参考资料

https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/resource_management_guide/index