cgroups子系统
- cpu子系统,用来限制进程的cpu使用率
- cpuacct子系统,可以统计cgroups中进程的使用报告
- cpuset子系统,为cgroups中的进程分配单独的cpu节点或者内存节点
- memory子系统,可以限制进程的memory的使用量
- blkio子系统,可以限制进程的块设备io
- devices子系统,可以控制进程能够访问某些设备
- net_cls子系统,可以标记cgroups中进程的网络数据包,然后使用tc模块对数据包进行控制
- freezer子系统,可以挂起或者恢复cgroups中的进程
- ns子系统,可以使不同 cgroups下的进程使用不同的namespace
在创建了cgroups的结构体之后,可以把进程加入到某一个节点的控制任务中,一个节点的控制列表中的所有进程都会受到当前节点的资源限制,同时某一个进程也可以被加入到不同的cgroups层级结构的节点中。
每一个进程的描述符都会存在一个指针指向了一个辅助的数据结构css_set(cgroups sub-system set)的进程会被加入到当前的css_set的进程链表中,一个进程之只隶属于一个css_set,一个css_set可以包含多个进程
然后css_set可以通过辅助数据结构和cgroups节点进行多对多的关联,值得注意的是css_set不能同时管理那cgroups层级结构下的多个节点,因为cgroups对同一种资源不允许有多个限制配置
一个css_set关联多个cgroups层级结构的节点时,表示需要对当前的css_set下的进程,表示需要对该css_Set下的进程进行多种资源控制,一个cgroups节点关联多个css_set,表明多个css_Set下的进程列表受到同一份资源的相同限制
cgroups是通过VFS把功能提供给用户态的进程,给用户态提供统一的API接口
VFS
通用文件模型中包含了四种元数据结构
- superblock object ,存放已经注册的文件系统信息,例如ext2,ext3等基础的磁盘文件系统,还有用来读写socket的socket文件系统,读写cgroups的cgroups文件系统
- inode object,存放具体文件的信息,对于一般的磁盘文件系统而言,inode节点存放的是文件在硬盘中的存储块信息,对于socket文件系统,inode会存放socket的相关属性,对于cgroups这样的特殊文件系统,inode会存放于cgroups节点相关的属性信息
- file object,一个文件对象表示进程内打开的一个文件,文件对象是存放在进程的文件描述符表中,同样这个文件中最重要的部分是file_operations的结构体,这个结构体描述了具体的文件系统的读写实现。当进程在某个文件描述符上执行读写操作的时候 对于普通的磁盘文件系统,file_operations就是普通的write/read操作,对于socket文件系统,file_operation就是send/recv等操作,对于cgroups特殊的文件系统,file_operation就是cgroups结构体的具体实现
- 目录项(dentry object),在每个文件系统中,内核在查找某个路径的文件时,会去为内核路径上的每个分量都生成一个目录项对象
//cgroups文件系统类型的结构体
static struct file_system_type cgroup_fs_type = {
.name = "cgroup",
.mount = cgroup_mount,
.kill_sb = cgroup_kill_sb,
}
//cgroups超级块对象定义的操作
static const struct super_operations cgroup_ops = {
.statfs = simple_statfs,
.drop_inode = generic_delete_inode,
.show_options = cgroup_show_options,
.remount_fs = cgroup_remount,
}
//inode对象和file对象定义的特殊含义
static const struct inode_operations cgroup_dir_inode_operations = {
.lookup = cgroup_lookup,
.mkdir = cgroup_mkdir,
.rmdir = cgroup_rmdir,
.rename = cgroup_rename,
};
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,
}
在Linux中,用户可以通过mount命令挂载cgroups文件系统
//比如挂载 cpuset, cpu, cpuacct, memory 4个subsystem到/cgroup/cpu_and_mem 目录下,就可以使用
mount -t cgroup -o remount,cpu,cpuset,memory cpu_and_mem /cgroup/cpu_and_mem
实际用例:
docker在实现不同的container之间的资源隔离与控制的时候就是通过cgroup,chroot,namespace等技术实现的,对于同一个container中的进程可以通过把进程PID添加到cgroups的一个字节点中实现资源的限制
//限制CPU使用资源为50%的例子
首先在 cpu 子系统下面创建了一个 halfapi 的子节点:cgcreate abc:abc -g cpu:halfapi
然后在配置文件中写入配置数据:echo 50000 > /cgroup/cpu/halfapi/cpu.cfs_quota_us
cpu.cfs_quota_us中的默认值是100000,写入50000表示只能使用50%的 cpu 运行时间。
最后在这个cgroups中启动这个任务:cgexec -g "cpu:/halfapi" php halfapi.php half >/dev/null 2>&1
在cgroups引入内核之前,想要实现CPU资源的控制,可以通过nice命令调整优先级,还可以通过cpulimit命令限制进程的CPU使用率