docker底层之cgroup

cgroup的实现相对namespace要复杂一些,网上也有一些代码分析,大家对代码分析的兴趣估计也不大,所以这里就不放代码分析了,主要对其使用进行说明,么么哒。

Cgroup是linux内核集成的资源控制机制,cgroup与用户态交互通过特殊文件系统cgroup文件系统,进行交互,所有设置或者查看cgroup的动作都可以通过cgroup文件系统下的文件完成,因此除了编译内核的时候需要打开特定的选项之外,还需在系统启动之后挂载cgroup文件系统方能使用cgroup的控制功能。几个重要概念:

Ø  cgroup: 一组进程的行为控制。要对某个进程进行资源限制,就将进程添加到cgroup中,一个cgroup可以有多个进程。

Ø  hierarchy: 一组cgroup的集合,可以理解成cgroup的根,cgroup是hierarchy的结点。一个hierarchy的资源限制为1,代表拥有系统所有资源,cgroup资源限制小于1,分配系统资源。

Ø  subsystem: cgroup可以进行多种资源限制,某种资源限制就是一个subsystem,所有的subsystem构成了资源限制的所有资源种类。

三者之间的主要关系:

Ø  创建新hierarchy时,系统中的所有任务都是那个hierarchy的根cgroup的初始成员。

Ø  一个subsystem最多只能附加到一个hierarchy。

Ø  一个hierarchy可以附加多个subsystem。

Ø  一个进程可以是多个cgroup的成员,但是这些cgroup必须在不同的hierarchy下。

Ø  创建子进程时,子进程自动成为父进程所在 cgroup 的成员。可根据需要将该子进程移动到不同的 cgroup 中。

Cgroup包含了11个子系统,分别是:

Ø  Blkio:控制块设备的访问,比如带宽等。

Ø  cpu :控制进程占用cpu的多少。

Ø  Cpuacct: 记录cgroup 中进程使用的 CPU 情况。

Ø  Cpuset:为 cgroup 中的进程分配CPU和内存节点。

Ø  Devices:控制进程对设备的访问。

Ø  Freezer:挂起或者恢复 cgroup 中的进程。

Ø  Memory:设定 cgroup 中进程的内存限制,统计使用的内存资源。

Ø  net_cls:使用等级识别符(classid)标记网络数据包,使得Linux 流量控制程序(tc)识别具体 cgroup 中的数据包。

Ø  Ns:命名空间子系统,默认创建cgroup的时候会创建命名空间,新的内核不支持。

Ø  Debug:用于调试。

Ø  Perf:按cgroup进行性能统计。

Cgroup与用户态的交互通过cgroup文件系统完成,cgroup的每一控制箱在cgroup文件系统下都会对应一个文件,通过对文件的读写,来实现资源的控制。cgroup文件系统的定义:

static struct file_system_type cgroup_fs_type = {

.name = "cgroup",

.get_sb = cgroup_get_sb,

.kill_sb = cgroup_kill_sb,

};

定义了两个函数指针,定义了一个文件系统必须实现了的两个操作get_sb,kill_sb,即获得超级块和释放超级块。这两个操作会在使用mount系统调用挂载cgroup文件系统时使用。

cgroup 超级块的定义:

static const struct super_operations cgroup_ops = {

.statfs = simple_statfs,

.drop_inode = generic_delete_inode,

.show_options = cgroup_show_options,

.remount_fs = cgroup_remount,

};

cgroup 索引块定义:

static const struct inode_operations cgroup_dir_inode_operations = {

.lookup = simple_lookup,

.mkdir = cgroup_mkdir,

.rmdir = cgroup_rmdir,

.rename = cgroup_rename,

};

在cgroup文件系统中,使用mkdir创建cgroup或者用rmdir删除cgroup时,就会调用相应的函数指针指向的函数。

cgroup 文件操作定义:

static const struct file_operations cgroup_file_operations = {

.read = cgroup_file_read,

.write = cgroup_file_write,

.llseek = generic_file_llseek,

.open = cgroup_file_open,

.release = cgroup_file_release,

};

对cgroup目录下的控制文件进行操作时,会调用该操作函数。而这些函数将会根据访问的不同的文件调用其对应的操作函数。cgroup文件系统为cgroups控制文件定义了一个cftype的结构体。cftype的定义:

struct cftype {

char name[MAX_CFTYPE_NAME];

int private; /*

mode_t mode;

size_t max_write_len;

 

int (*open)(struct inode *inode, struct file *file);

ssize_t (*read)(struct cgroup *cgrp, struct cftype *cft,

struct file *file,

char __user *buf, size_t nbytes, loff_t *ppos);

u64 (*read_u64)(struct cgroup *cgrp, struct cftype *cft);

s64 (*read_s64)(struct cgroup *cgrp, struct cftype *cft);

int (*read_map)(struct cgroup *cont, struct cftype *cft,

struct cgroup_map_cb *cb);

int (*read_seq_string)(struct cgroup *cont, struct cftype *cft,

       struct seq_file *m);

 

ssize_t (*write)(struct cgroup *cgrp, struct cftype *cft,

 struct file *file,

 const char __user *buf, size_t nbytes, loff_t *ppos);

int (*write_u64)(struct cgroup *cgrp, struct cftype *cft, u64 val);

int (*write_s64)(struct cgroup *cgrp, struct cftype *cft, s64 val);

int (*write_string)(struct cgroup *cgrp, struct cftype *cft,

    const char *buffer);

int (*trigger)(struct cgroup *cgrp, unsigned int event);

 

int (*release)(struct inode *inode, struct file *file);

int (*register_event)(struct cgroup *cgrp, struct cftype *cft,

struct eventfd_ctx *eventfd, const char *args);/*

void (*unregister_event)(struct cgroup *cgrp, struct cftype *cft,

struct eventfd_ctx *eventfd);

};

cftype中除了定义文件的名字和相关权限标记外,还定义了对文件进行操作的函数指针。不同的文件可以有不同的操作,因为不同的文件涉及到的是内核不同的子系统不同的功能。

 

 

1.1.1.      Cpu

cpuset 子系统为 cgroup 分配独立 CPU 和内存节点。

typedef enum {

    CS_CPU_EXCLUSIVE,

    CS_MEM_EXCLUSIVE,

    CS_MEM_HARDWALL,

    CS_MEMORY_MIGRATE, /*进行内存迁移*/

    CS_SCHED_LOAD_BALANCE, /*该set下的cpu进行负载均衡*/

    CS_SPREAD_PAGE, /*和后面的两个标志共同设置平均使用允许的内存节点*/

    CS_SPREAD_SLAB,

} cpuset_flagbits_t;

如果系统支持热插拔,那么热插拔之后如果cpuset的cpu集合或者内存节点集合为空,该cpuset关联的进程将移动到上层非空cpuset中。

同一层的task_group和进程被当成同样的调度实体来选择,当被选到的是task_group时,则对task_group的孩子节点重复这个过程,直到选到一个运行的进程。当设置一个cgroup的shares值时,该cgroup当作一个整体和剩下的进程或其他cgroup分享cpu时间。比如,在根cgroup下建立cgroup A,将其shares值设1024,再建立cgroup B,将其shares设为2048,再将一些进程分别加入到这两个cgroup中,则长期调度的结果应该是A:B:C=1:2:1(C是系统中未加入到A或B的进程)。

Cpu子系统则为cgroup分配时间比例,cpuacct则统计cgroup中进程使用的时间。

项目

功能

说明

Cpuset.cpus

限制进程能够使用的cpu节点

 

Cpuset.mems

限制进程能够使用的内存节点

 

cpu.shares

假设cgroup A的tasks的cpu.shares值为1,cgroup B的tasks的cpu.shares值为2,则cgroup B的进程占用的cpu时间是cgroup A上进程的2倍

 

cpu.rt_runtime_us

以​​​​​​​微​​​​​​​秒​​​​​​​为​​​​​​​单​​​​​​​位​​​​​​​指​​​​​​​定​​​​​​​在​​​​​​​某​​​​​​​个​​​​​​​时​​​​​​​间​​​​​​​段​​​​​​​中​​​​​​​ cgroup 中​​​​​​​的​​​​​​​实时任​​​​​​​务​​​​​​​对​​​​​​​ CPU 资​​​​​​​源​​​​​​​的​​​​​​​最​​​​​​​长​​​​​​​连​​​​​​​续​​​​​​​访​​​​​​​问​​​​​​​时​​​​​​​间​​​​​​​

 

cpu.rt_period_us

以​​​​​​​微​​​​​​​秒​​​​​​​为​​​​​​​单​​​​​​​位​​​​​​​指​​​​​​​定​​​​​​​在​​​​​​​某​​​​​​​个​​​​​​​时​​​​​​​间​​​​​​​段​​​​​​​中​​​​​​​ cgroup 对​​​​​​​ CPU 资​​​​​​​源​​​​​​​访​​​​​​​问​​​​​​​重​​​​​​​新​​​​​​​分​​​​​​​配​​​​​​​的​​​​​​​频​​​​​​​率​​​​​​​

 

cpuset.cpu_exclusive

是否共享该cgroup指定的cpu,设置为1一个cpu只能出现在一个cgroup里面

 

cpuset.mem_exclusive

是否共享该cgroup指定的memory node 

 

cpuset.sched_load_balance

是否对cgroup的所有CPU做负载平衡

 

cpuset.sched_relax_domain_level 

包​​​​​​​含​​​​​​​ -1 到​​​​​​​小​​​​​​​正​​​​​​​数​​​​​​​间​​​​​​​的​​​​​​​整​​​​​​​数​​​​​​​,它​​​​​​​代​​​​​​​表​​​​​​​内​​​​​​​核​​​​​​​应​​​​​​​尝​​​​​​​试​​​​​​​平​​​​​​​衡​​​​​​​负​​​​​​​载​​​​​​​的​​​​​​​ CPU 宽​​​​​​​度​​​​​​​范​​​​​​​围。

-1

为​​​​​​​负​​​​​​​载​​​​​​​平​​​​​​​衡​​​​​​​使​​​​​​​用​​​​​​​系​​​​​​​统​​​​​​​默​​​​​​​认​​​​​​​值​​​​​​​

0

不​​​​​​​执​​​​​​​行​​​​​​​直​​​​​​​接​​​​​​​负​​​​​​​载​​​​​​​平​​​​​​​衡​​​​​​​;负​​​​​​​载​​​​​​​平​​​​​​​衡​​​​​​​只​​​​​​​是​​​​​​​阶​​​​​​​段​​​​​​​性​​​​​​​的​​​​​​​

1

在​​​​​​​同​​​​​​​一​​​​​​​核​​​​​​​中​​​​​​​的​​​​​​​跨​​​​​​​线​​​​​​​程​​​​​​​直​​​​​​​接​​​​​​​负​​​​​​​载​​​​​​​平​​​​​​​衡​​​​​​​

2

在​​​​​​​同​​​​​​​一​​​​​​​软​​​​​​​件​​​​​​​包​​​​​​​中​​​​​​​的​​​​​​​跨​​​​​​​线​​​​​​​程​​​​​​​直​​​​​​​接​​​​​​​负​​​​​​​载​​​​​​​平​​​​​​​衡​​​​​​​

3

在​​​​​​​同​​​​​​​一​​​​​​​节​​​​​​​点​​​​​​​或​​​​​​​者​​​​​​​刀​​​​​​​片​​​​​​​中​​​​​​​的​​​​​​​跨​​​​​​​线​​​​​​​程​​​​​​​直​​​​​​​接​​​​​​​负​​​​​​​载​​​​​​​平​​​​​​​衡​​​​​​​

4

在​​​​​​​不​​​​​​​使​​​​​​​用​​​​​​​统​​​​​​​一​​​​​​​内​​​​​​​存​​​​​​​访​​​​​​​问​​​​​​​(NUMA)构​​​​​​​架​​​​​​​中​​​​​​​跨​​​​​​​多​​​​​​​个​​​​​​​ CPU 的​​​​​​​直​​​​​​​接​​​​​​​负​​​​​​​载​​​​​​​平​​​​​​​衡​​​​​​​

5

在​​​​​​​使​​​​​​​用​​​​​​​统​​​​​​​一​​​​​​​内​​​​​​​存​​​​​​​访​​​​​​​问​​​​​​​(NUMA)构​​​​​​​架​​​​​​​中​​​​​​​跨​​​​​​​多​​​​​​​个​​​​​​​ CPU 的​​​​​​​直​​​​​​​接​​​​​​​负​​​​​​​载​​​​​​​平​​​​​​​衡​​​​​​​

 

cpuset.memory_migrate

cpuset.mems 中​​​​​​​的​​​​​​​值​​​​​​​更​​​​​​​改​​​​​​​时​​​​​​​是​​​​​​​否​​​​​​​应​​​​​​​该​​​​​​​将​​​​​​​内​​​​​​​存​​​​​​​中​​​​​​​的​​​​​​​页​​​​​​​迁​​​​​​​移​​​​​​​到​​​​​​​新​​​​​​​节​​​​​​​点​​​​​​​的​​​​​​​标​​​​​​​签​​​​​​​(0 或​​​​​​​者​​​​​​​ 1)

 

cpuset.mem_hardwall

是​​​​​​​否​​​​​​​应​​​​​​​将​​​​​​​内​​​​​​​存​​​​​​​页​​​​​​​面​​​​​​​的​​​​​​​内​​​​​​​核​​​​​​​分​​​​​​​配​​​​​​​限​​​​​​​制​​​​​​​在​​​​​​​为​​​​​​​这​​​​​​​个​​​​​​​ cpuset 指​​​​​​​定​​​​​​​的​​​​​​​内​​​​​​​存​​​​​​​节​​​​​​​点​​​​​​​

 

cpuset.memory_pressure

cgroup产生的平均内存压力

 

cpuset.memory_pressure_enabled

是否计算cgroup中的内存压力

 

cpuset.memory_spread_page

是否将文件系统缓存平均分配到cgroup的内存节点上

 

cpuset.memory_spread_slab

是否将用于文件输入输出缓冲平均分配到cgroup的内存节点上

 

新的实现增加了对cpu上限的设置,其值设置方法和命名都和cpu.rt_runtime_us和cpu.rt_period_us差不多,这个文档早的时候写的,这里偷个懒就不补充了。

1.1.2.      Mem

memory 子系统可以设定 cgroup 中进程使用的内存限制,并自动统计进程使用的内存资源。memory子系统通过linux的resource counter机制实现。

resource counter是内核为子系统提供的一种资源管理机制。包括了用于记录资源的数据结构和相关函数。Resource counter定义了一个res_counter的结构体来管理特定资源,定义如下:

struct res_counter {

unsigned long long usage;

unsigned long long max_usage;

unsigned long long limit;

unsigned long long soft_limit;

unsigned long long failcnt;/*

spinlock_t lock;

struct res_counter *parent;

};

Usage用于记录当前已使用的资源,max_usage用于记录使用过的最大资源量,limit用于设置资源的使用上限,进程组不能使用超过这个限制的资源,soft_limit用于设定一个软上限,进程组使用的资源可以超过这个限制,failcnt用于记录资源分配失败的次数,管理可以根据这个记录,调整上限值。Parent指向父节点,这个变量用于处理层次性的资源管理。

memory子系统定义了一个叫mem_cgroup的结构体来管理cgroup相关的内存使用信息,定义如下:

struct mem_cgroup {

struct cgroup_subsys_state css;

struct res_counter res;

struct res_counter memsw;

struct mem_cgroup_lru_info info;

spinlock_t reclaim_param_lock;

int prev_priority;

int last_scanned_child;

bool use_hierarchy;

atomic_t oom_lock;

atomic_t refcnt;

unsigned intswappiness;

int oom_kill_disable;

bool memsw_is_minimum;

struct mutex thresholds_lock;

struct mem_cgroup_thresholds thresholds;

struct mem_cgroup_thresholds memsw_thresholds;

struct list_head oom_notify;

unsigned long move_charge_at_immigrate;

struct mem_cgroup_stat_cpu *stat;

};

mem_cgroup中包含了两个res_counter成员,分别用于管理memory资源和memory+swap资源,如果memsw_is_minimum为true,则res.limit=memsw.limit,即当进程组使用的内存超过memory的限制时,不能通过swap来缓解。

Cgroup通过在分配内存,换入换出的时候进行计数来管理cgroup中内存。内核线程如果属于某个cgroup,其使用内存同样受到cgroup控制。页高速缓存的内存也在cgroup控制之内。

项目

功能

说明

Memory.stat

当前cgroup中的内存实时状况

 

Memory.usage_in_bytes

当前cgroup中所有进程使用的物理内存之和

单位bytes

Memory.memsw.usage_in_bytes

当前cgroup中所有进程使用的物理内存和swap之和

 

Memory.max.usage_in_bytes

当前cgroup使用物理内存最大值

 

Memory.memsw.max_usage_in_bytes

当前cgroup使用物理内存和swap之和的最大值

 

Memory.limit_in_bytes

设置当前cgroup的物理内存的上限值(包括页高速缓存)

 

Memory.memsw.limit_in_bytes

对物理内存和swap之和的限制

 

Memory.failcnt

物理内存值达到所设置上限值的次数

 

Memory.memsw.failcnt

物理内存和swap之和达到所设置上限值的次数

 

Memory.force_empty

释放cgroup使用的所有内存

只有在cgroup中没有进程时才使用

Memory.swappines

交换值,用于计算交换倾向经验值

 

Memory.use_hierarchy

指定内存回收是否按一个hierarchy来进行,如果设置为1,内存子系统从其中一个超过限额的子cgroup中回收内存,如果设置为0,则只是从当前cgroup中的进程回收

 

Memory.oom_control

内存超过限制并且回收失败后事后通过oom_killer机制处理

 

Memory.soft_limit_in_bytes

内存软限制

 

Memory.move_charge_at_immigrate

页迁移计数控制

 

Memory.numa.stat

每个节点的内存控制信息

 

Memory.tcp.limit_in_bytes

当前cgroup能使用的tcp内存总大小

 

Memory.tcp.usage_in_bytes

当前cgroup所有进程使用的tcp内存量

 

Memory_kmem.tcp.filcnt

当前cgroup中所有进程tcp缓存之和超过限制的次数

 

Memory.keme.tcp.max_usage_in_bytes

当前cgroup中tcp使用缓存的峰值

 

 

1.1.3.      Device

devices只控制块设备和字符设备的访问。有inode的设备文件通过在open中对比权限来控制cgroup中的进程能够访问的devices。inode不存在的设备就必须通过mknod中对比权限来控制进程对设备的访问。

项目

功能

说明

Devices.allow

允许cgroup中进程访问的设备名

 

Devices.deny

禁止cgroup中进程访问的设备名

 

Devices.list

Cgroup中进程访问的设备

 

 

1.1.4.      Net

网络的资源控制通过kernel的tc实现。

项目

功能

说明

Net_cls.classid

流量控制中cgroup分类器的id号

 

Net_prio.ifpriomap

设置某个设备上的优先级

 

Net_prio.prioidx

全网唯一的一个标识,只读

 

 

1.1.5.      Blkio

cgroup的blkio子系统管理块设备访问,有2种限制模式:

Ø  throttle,限制每个进程能使用的iops或者bps。

Ø  weight,限制每个进程能使用的iops的比例,必须通过CFQ调度器来实现。可以通过修改/sys/block/sda/queue/scheduler文件完成。io带宽相对控制只是针对受控对象都很繁忙的时候控制其上限速度,在某个cgroup比较闲的时候,不再满足这个规律,其他cgroup可能消耗掉更多的带宽。

要使用blkio的weight限制需要注意:

Ø  必须direct io, 如果buffered io因为最终写IO的进程不是发起IO的进程,结果会有很大的偏差。

Ø  调度器必须是CFQ。

在CFQ调度器中,所有的异步请求都属于根cgroup,不受子cgroup的限制,因此在子cgroup中对异步io进行资源限制没有意义。

项目

功能

说明

blkio.weight

Io访问能力的相对权重

 

blkio.weight_device

特定设备的io带宽的相对权重

 

blkio.throttle.read_bps_device

特定设备的读bps上限值

 

blkio.throttle.write_bps_device

特定设备的写bps上限值

 

blkio.throttle.read_iops_device 

特定设备的读iops上限值

 

blkio.throttle.write_iops_device

特定设备的写iops上限值

 

blkio.io_merged

请​​​​​​​求​​​​​​​合​​​​​​​并​​​​​​​到I/O操​​​​​​​作​​​​​​​请​​​​​​​求​​​​​​​的​​​​​​​次​​​​​​​数

 

blkio.io_queued

Cgroup I/O操​​​​​​​作​​​​​​​排​​​​​​​队​​​​​​​的​​​​​​​请​​​​​​​求​​​​​​​次​​​​​​​数​​​​​​​

 

blkio.io_service_bytes

转​​​​​​​换​​​​​​​到​​​​​​​具​​​​​​​体​​​​​​​设​​​​​​​备​​​​​​​或​​​​​​​者​​​​​​​由​​​​​​​具​​​​​​​体​​​​​​​设​​​​​​​备​​​​​​​中​​​​​​​转​​​​​​​换​​​​​​​出​​​​​​​的​​​​​​​字​​​​​​​节​​​​​​​数

 

blkio.io_serviced

在​​​​​​​具​​​​​​​体​​​​​​​设​​​​​​​备​​​​​​​中​​​​​​​执​​​​​​​行​​​​​​​的I/O操​​​​​​​作​​​​​​​数

 

blkio.io_service_time

在​​​​​​​具​​​​​​​体​​​​​​​设​​​​​​​备​​​​​​​中​​​​​​​的I/O操​​​​​​​作​​​​​​​请​​​​​​​求​​​​​​​发​​​​​​​送​​​​​​​和​​​​​​​请​​​​​​​求​​​​​​​完​​​​​​​成​​​​​​​之​​​​​​​间​​​​​​​的​​​​​​​时​​​​​​​间​​​​​​​

 

blkio.io_wait_time

所有io操作在队列中等待的时间总和

 

blkio.reset_stats

重​​​​​​​新​​​​​​​设​​​​​​​定​​​​​​​在​​​​​​​其​​​​​​​它​​​​​​​伪​​​​​​​文​​​​​​​件​​​​​​​中​​​​​​​记​​​​​​​录​​​​​​​的​​​​​​​统​​​​​​​计​​​​​​​数​​​​​​​据

 

blkio.sectors

转​​​​​​​换​​​​​​​到​​​​​​​具​​​​​​​体​​​​​​​设​​​​​​​备​​​​​​​或​​​​​​​者​​​​​​​由​​​​​​​具​​​​​​​体​​​​​​​设​​​​​​​备​​​​​​​转​​​​​​​换​​​​​​​出​​​​​​​的​​​​​​​扇​​​​​​​区​​​​​​​数

 

blkio.throttle.io_service_bytes

转​​​​​​​换​​​​​​​到​​​​​​​具​​​​​​​体​​​​​​​设​​​​​​​备​​​​​​​或​​​​​​​者​​​​​​​由​​​​​​​具​​​​​​​体​​​​​​​设​​​​​​​备​​​​​​​中​​​​​​​转​​​​​​​换​​​​​​​出​​​​​​​的​​​​​​​字​​​​​​​节​​​​​​​数

 

blkio.throttle.io_serviced

在​​​​​​​具​​​​​​​体​​​​​​​设​​​​​​​备​​​​​​​中​​​​​​​执​​​​​​​行​​​​​​​的​​​​​​​I/O操​​​​​​​作​​​​​​​数

 

blkio.time

对​​​​​​​具​​​​​​​体​​​​​​​设​​​​​​​备​​​​​​​的​​​​​​​I/O访​​​​​​​问​​​​​​​时​​​​​​​间​​​​​​​

 

blkio.avg_queue_size

I/O操​​​​​​​作​​​​​​​的​​​​​​​平​​​​​​​均​​​​​​​队​​​​​​​列​​​​​​​大​​​​​​​小

 

blkio.group_wait_time

每个队列等待获取时间片的等待时间总和​​​​

 

blkio.empty_time

不包括等待时间在内的处理时间总和

 

blkio.idle_time

Io调度程序等待更好的请求时空转的时间

 

blkio.dequeue

​​​​​​​I/O操​​​​​​​作​​​​​​​请​​​​​​​求​​​​​​​被​​​​​​​具​​​​​​​体​​​​​​​设​​​​​​​备​​​​​​​从​​​​​​​队​​​​​​​列​​​​​​​中​​​​​​​移​​​​​​​除​​​​​​​的​​​​​​​次​​​​​​​数​​​​​​​

 


你可能感兴趣的:(Linux,内核,linux,文件系统,lxc,linux,cgroup,tcp/ip协议栈,docker)