• Android/Linux下CGroup框架分析及其使用


    1 cgroup介绍

    CGroup是control group的简称,它为Linux kernel提供一种任务聚集和划分的机制,可以限制、记录、隔离进程组(process groups)所使用的资源(cpu、memory、I/O等)。CGroup也是LXC为实现虚拟化所使用的资源管理手段。CGroup本身是提供将进程进行分组化管理的功能和接口的基础结构,I/O或内存的分配控制等具体的资源管理功能是通过这个功能来实现的。这些具体的资源管理功能称为CGroup子系统。

    CGroup子系统包含如下:

    子系统

    主要功能

    blkio

    设置限制每个块设备的输入输出控制。

    cpu

    使用调度程序为CGroup任务提供CPU的访问。

    cpuacct

    产生CGroup任务的CPU资源报告,CPU Accounting Controller。

    cpuset

    如果是多核CPU,这个子系统就会为CGroup任务分配单独的CPU和内存。

    devices

    允许或拒绝CGroup任务对设备的访问。

    freezer

    暂停或恢复CGroup任务。

    hugetlb

    允许限制CGroup 的HubeTLB使用

    memory

    设置每个CGroup的内存限制以及产生内存资源报告。

    net_cls

    标记每个网络包以供CGroup方便使用。

    net_prio

    提供接口以供动态调节程序的网络传输优先级。

    perf_event

    增加了对没group的检测跟踪的能力,即可以检测属于某个特定的group的所有线程以及运行在特定CPU上的线程。

    1.1 subsystem、task、hierarchy介绍及其关系

    任务task,在CGroup中,任务就是系统的一个进程。

    CGroup就是一组按照某种标准进行划分的进程。CGroup中的资源控制都是以control group为单位实现。一个进程可以加入到某个控制族群,也可以一个进程组迁移到另一个控制族群。

    subusystem即子系统,是CGroup中可添加删除的模块,是CGroup下对资源进行的一种封装,比如内存资源、CPU资源、网络资源等。一个子系统就是一个资源控制器,鼻子痛必须附加(attach)到一个层级上才能起作用,一个子系统附加到某个层级后,这个层级上的所有控制组全都受到这个子系统的控制。

    hierarchy可以认为是一系列cgroups的集合。hierarchy是这个集合的根。

    相互关系:

    每次在系统中创建新层级时,该系统中的所有任务都是那个层级的默认 cgroup(我们称之为 root cgroup,此 cgroup 在创建层级时自动创建,后面在该层级中创建的 cgroup 都是此

    cgroup 的后代)的初始成员;

    一个子系统最多只能附加到一个层级;

    一个层级可以附加多个子系统;

    一个任务可以是多个 cgroup 的成员,但是这些 cgroup 必须在不同的层级;

    系统中的进程(任务)创建子进程(任务)时,该子任务自动成为其父进程所在 cgroup 的成员。然后可根据需要将该子任务移动到不同的 cgroup 中,但开始时它总是继承其父任务的 cgroup。

    clip_image002

    图表 1CGroup层级图

    1.2 mount介绍

    更详细信息参考:

    linux内核mount系统调用源码分析http://blog.csdn.net/wugj03/article/details/41958029/

    linux系统调用mount全过程分析http://blog.csdn.net/skyflying2012/article/details/9748133

    在系统启动时,mount需要的CGroup子系统:

    mount cgroup none /dev/cpuctl cpu

    在用户空间将mount命令转换成系统调用sys_mount:

    asmlinkage long sys_mount(char __user *dev_name, char __user *dir_name,

    char __user *type, unsigned long flags,

    void __user *data);

    从sys_mount到具体文件系统的.mount调用流程如下:

    sys_mount(fs/namespace.c)

      -->do_mount(kernel_dev, dir_name, kernel_type, flags, (void *)data_pate)

        -->do_new_mount (&path, type_page, flags, mnt_flags,dev_name, data_page)

          --> vfs_kern_mount(type, flags, name, data)

            --> mount_fs(type, flags, name, data)

              --> type->mount(type, flags, name, data)

                --> cgroup_mount(fs_type, flags, unused_dev_name, data)

    struct file_system_type {

    const char *name; 文件系统名称

    int fs_flags;

    struct dentry *(*mount) (struct file_system_type *, int,

    const char *, void *); 挂载文件系统的调用。

    void (*kill_sb) (struct super_block *); 卸载文件系统的调用

    struct module *owner; VFS内部调用时使用

    struct file_system_type * next;

    struct hlist_head fs_supers;

    struct lock_class_key s_lock_key;

    struct lock_class_key s_umount_key;

    struct lock_class_key s_vfs_rename_key;

    struct lock_class_key s_writers_key[SB_FREEZE_LEVELS];

    struct lock_class_key i_lock_key;

    struct lock_class_key i_mutex_key;

    struct lock_class_key i_mutex_dir_key;

    }

    2 代码分析

    2.1 核心

    2.1.1 框架结构图

    CGoup核心主要创建一系列sysfs文件,用户空间可以通过这些节点控制CGroup各子系统行为。各子系统模块根据参数,在执行过程中或调度进程道不同CPU上,或控制CPU占用时间,或控制IO带宽等等。另,在每个进程的proc文件系统中都有一个cgroup,显示该进程对应的CGroup各子系统信息。

    image

    如果CGroup需要early_init,start_kernel调用cgroup_init_early在系统启动时进行CGroup初始化。

    int __init cgroup_init_early(void)

    {

    static struct cgroup_sb_opts __initdata opts;

    struct cgroup_subsys *ss;

    int i;

    init_cgroup_root(&cgrp_dfl_root, &opts);

    cgrp_dfl_root.cgrp.self.flags |= CSS_NO_REF;

    RCU_INIT_POINTER(init_task.cgroups, &init_css_set);

    for_each_subsys(ss, i) {

    ss->id = i;

    ss->name = cgroup_subsys_name[i];

    if (ss->early_init)

    cgroup_init_subsys(ss, true);

    }

    return 0;

    }

    CGroup的起点是start_kernel->cgroup_init,进入CGroup的初始化,主要注册cgroup文件系统和创建、proc文件,初始化不需要early_init的子系统。

    int __init cgroup_init(void)

    {

    for_each_subsys(ss, ssid) { 遍历所有子系统,初始化,根据属性配置不同文件节点

    if (ss->early_init) { early_init是能的已经在cgroup_init_early中初始化

    struct cgroup_subsys_state *css =

    init_css_set.subsys[ss->id];

    css->id = cgroup_idr_alloc(&ss->css_idr, css, 1, 2,

    GFP_KERNEL);

    BUG_ON(css->id < 0);

    } else {

    cgroup_init_subsys(ss, false);

    }

    list_add_tail(&init_css_set.e_cset_node[ssid],

    &cgrp_dfl_root.cgrp.e_csets[ssid]);

    cgrp_dfl_root.subsys_mask |= 1 << ss->id;

    if (cgroup_legacy_files_on_dfl && !ss->dfl_cftypes)

    ss->dfl_cftypes = ss->legacy_cftypes;

    if (!ss->dfl_cftypes)

    cgrp_dfl_root_inhibit_ss_mask |= 1 << ss->id;

    根据不同类型,创建不同节点列表。

    if (ss->dfl_cftypes == ss->legacy_cftypes) {

    WARN_ON(cgroup_add_cftypes(ss, ss->dfl_cftypes));

    } else {

    WARN_ON(cgroup_add_dfl_cftypes(ss, ss->dfl_cftypes));

    WARN_ON(cgroup_add_legacy_cftypes(ss, ss->legacy_cftypes));

    }

    if (ss->bind)

    ss->bind(init_css_set.subsys[ssid]);

    }

    err = sysfs_create_mount_point(fs_kobj, "cgroup"); 创建挂载节点/sys/fs/cgroup

    if (err)

    return err;

    err = register_filesystem(&cgroup_fs_type); 注册cgroup_fs_type文件系统。

    if (err < 0) {

    sysfs_remove_mount_point(fs_kobj, "cgroup");

    return err;

    }

    proc_create("cgroups", 0, NULL, &proc_cgroupstats_operations); 创建/proc/cgroups节点

    return 0;

    }

    /proc/<pid>/cgroup指向proc_cgroup_show,用于显示此pid所对应的cgroup路径信息:

    static const struct pid_entry tid_base_stuff[] = {

    #ifdef CONFIG_CGROUPS

    ONE("cgroup", S_IRUGO, proc_cgroup_show),

    #endif

    }

    CGroup的debug接口在CONFIG_CGROUP_DEBUG使能后打开:

    struct cgroup_subsys debug_cgrp_subsys = {

    .css_alloc = debug_css_alloc,

    .css_free = debug_css_free,

    .legacy_cftypes = debug_files,

    };

    cgroup_fs_type作为mount命令的参数,其中cgroup_mount为各subsystem进行mount工作。

    image

    2.1.2 核心结构体分析

    struct cgroup_subsys {

    struct cgroup_subsys_state *(*css_alloc)(struct cgroup_subsys_state *parent_css);

    创建cgroup_subsys_state结构体,和css_free相对。

    int (*css_online)(struct cgroup_subsys_state *css); 在subsystem相关资源分配完之后,进行online相关操作,和css_offline相对。

    void (*css_offline)(struct cgroup_subsys_state *css);

    void (*css_released)(struct cgroup_subsys_state *css);

    void (*css_free)(struct cgroup_subsys_state *css);

    void (*css_reset)(struct cgroup_subsys_state *css);

    void (*css_e_css_changed)(struct cgroup_subsys_state *css);

    int (*allow_attach)(struct cgroup_subsys_state *css,

    struct cgroup_taskset *tset);

    int (*can_attach)(struct cgroup_subsys_state *css,

    struct cgroup_taskset *tset);

    void (*cancel_attach)(struct cgroup_subsys_state *css,

    struct cgroup_taskset *tset);

    void (*attach)(struct cgroup_subsys_state *css,

    struct cgroup_taskset *tset);

    allow_attach和can_attach都是在将task附着到cgroup之前进行检查,如果失败则停止附着过程。

    cancel_attach是在can_attache成功之后,但是attache又失败的情况下调用。

    void (*fork)(struct task_struct *task); 将一个task写入cgroup

    void (*exit)(struct cgroup_subsys_state *css, 将一个task移出cgroup

    struct cgroup_subsys_state *old_css,

    struct task_struct *task);

    void (*bind)(struct cgroup_subsys_state *root_css);

    int disabled;

    int early_init; 是否需要进行early_init,如果需要会在cgroup_init_early中提前进行初始化。

    bool broken_hierarchy;

    bool warned_broken_hierarchy;

    /* the following two fields are initialized automtically during boot */

    int id;

    #define MAX_CGROUP_TYPE_NAMELEN 32

    const char *name;

    /* link to parent, protected by cgroup_lock() */

    struct cgroup_root *root;

    /* idr for css->id */

    struct idr css_idr;

    /*

    * List of cftypes. Each entry is the first entry of an array

    * terminated by zero length name.

    */

    struct list_head cfts;

    /*

    * Base cftypes which are automatically registered. The two can

    * point to the same array.

    */

    struct cftype *dfl_cftypes; /* for the default hierarchy */

    struct cftype *legacy_cftypes; /* for the legacy hierarchies */

    CGroup子系统的节点,这些节点都是在xxx_cgrp_subsys中定义的。

    /*

    * A subsystem may depend on other subsystems. When such subsystem

    * is enabled on a cgroup, the depended-upon subsystems are enabled

    * together if available. Subsystems enabled due to dependency are

    * not visible to userland until explicitly enabled. The following

    * specifies the mask of subsystems that this one depends on.

    */

    unsigned int depends_on;

    }

    legacy hierarchy类型的接口文件:

    static struct cftype cgroup_legacy_base_files[] = {

    {

    .name = "cgroup.procs",

    },

    {

    .name = "cgroup.clone_children",

    .read_u64 = cgroup_clone_children_read,

    .write_u64 = cgroup_clone_children_write,

    },

    {

    .name = "cgroup.sane_behavior",

    .flags = CFTYPE_ONLY_ON_ROOT,

    .seq_show = cgroup_sane_behavior_show,

    },

    {

    .name = "tasks",

    },

    {

    .name = "notify_on_release",

    .read_u64 = cgroup_read_notify_on_release,

    .write_u64 = cgroup_write_notify_on_release,

    },

    {

    .name = "release_agent",

    },

    { } /* terminate */

    }

    cgroup.procs:属于该分组的PID列表,仅包括多线程进程的线程leader的TID,这点和tasks不同。

    cgroup.clone_children:仅适用于cpuset。如果使能,创建子cpuset时,就会拷贝父cpust的配置。

    cgroup.sane_behavior:未实现。

    tasks:属于该分组的线程TID列表。

    notify_on_release:设置是否执行release_agent,为1时使能。

    release_agent:删除分组时执行的命令,这个文件只存在于根分组。

    2.2 子系统

    CGroup子系统都定义在cgroup_subsys.h中;kernel/cgroup.c包含此头文件,定义了两个结构体数组cgroup_subsys和cgroup_subsys_name。CGroup core对于各子系统的引用都是通过cgroup_subsys这个数组。

    /* generate an array of cgroup subsystem pointers */

    #define SUBSYS(_x) [_x ## _cgrp_id] = &_x ## _cgrp_subsys,

    static struct cgroup_subsys *cgroup_subsys[] = {

    #include <linux/cgroup_subsys.h>

    };

    #undef SUBSYS

    /* array of cgroup subsystem names */

    #define SUBSYS(_x) [_x ## _cgrp_id] = #_x,

    static const char *cgroup_subsys_name[] = {

    #include <linux/cgroup_subsys.h>

    };

    #undef SUBSYS

    2.2.1 blkio

    block/blk-cgroup.c中定义了blkio子系统结构体:

    struct cgroup_subsys io_cgrp_subsys = {
        .css_alloc = blkcg_css_alloc,
        .css_offline = blkcg_css_offline,
        .css_free = blkcg_css_free,
        .can_attach = blkcg_can_attach,
        .bind = blkcg_bind,
        .dfl_cftypes = blkcg_files,
        .legacy_cftypes = blkcg_legacy_files,
        .legacy_name = "blkio",
    #ifdef CONFIG_MEMCG
        /*
         * This ensures that, if available, memcg is automatically enabled
         * together on the default hierarchy so that the owner cgroup can
         * be retrieved from writeback pages.
         */
        .depends_on = 1 << memory_cgrp_id,
    #endif
    };

    由于默认init.rc没有加载,可以添加创建相关以便研究:

    mkdir /dev/blkio  0700 root system
    mount cgroup none /dev/blkio blkio

    由于cfq_iosched.c中cfq_init注册了blkcg_policy_cfq,配置节点就变成如下列表。

    CFQ,Complete Fairness Queueing。CFQ调度器主要目的提供一种进程间公平分配磁盘带宽的调度方法。

    深度阅读:http://lxr.free-electrons.com/source/Documentation/block/cfq-iosched.txt

    blkio.io_merged
    blkio.io_merged_recursive
    blkio.io_queued
    blkio.io_queued_recursive
    blkio.io_service_bytes
    blkio.io_service_bytes_recursive
    blkio.io_service_time
    blkio.io_service_time_recursive
    blkio.io_serviced
    blkio.io_serviced_recursive
    blkio.io_wait_time
    blkio.io_wait_time_recursive
    blkio.leaf_weight
    blkio.leaf_weight_device
    blkio.reset_stats
    blkio.sectors
    blkio.sectors_recursive
    blkio.time
    blkio.time_recursive
    blkio.weight
    blkio.weight_device

     

    2.2.2 cpu

    kernel/sched/core.c定义了cpu_cgrp_subsys结构体,需要进行early_init。

    struct cgroup_subsys cpu_cgrp_subsys = {

    .css_alloc = cpu_cgroup_css_alloc,

    .css_free = cpu_cgroup_css_free,

    .css_online = cpu_cgroup_css_online,

    .css_offline = cpu_cgroup_css_offline,

    .fork = cpu_cgroup_fork,

    .can_attach = cpu_cgroup_can_attach,

    .attach = cpu_cgroup_attach,

    .allow_attach = subsys_cgroup_allow_attach,

    .exit = cpu_cgroup_exit,

    .legacy_cftypes = cpu_files,

    .early_init = 1,

    };

    这里重点分析一下cpu_files,在实际使用中打开了shares/rt_runtime_us/rt_perios_us。

    其中shares是针对CFS进程的,rt_runtime_us/rt_perios_us是针对RT进程的。

    另外cfs_perios_us/cfs_quota_us是设置CFS进程占用的CPU带宽。

    static struct cftype cpu_files[] = {
    #ifdef CONFIG_FAIR_GROUP_SCHED
        {
            .name = "shares",
            .read_u64 = cpu_shares_read_u64,
            .write_u64 = cpu_shares_write_u64,
        },
    #endif
    #ifdef CONFIG_CFS_BANDWIDTH
        {
            .name = "cfs_quota_us",
            .read_s64 = cpu_cfs_quota_read_s64,
            .write_s64 = cpu_cfs_quota_write_s64,
        },
        {
            .name = "cfs_period_us",
            .read_u64 = cpu_cfs_period_read_u64,
            .write_u64 = cpu_cfs_period_write_u64,
        },
        {
            .name = "stat",
            .seq_show = cpu_stats_show,
        },
    #endif
    #ifdef CONFIG_RT_GROUP_SCHED
        {
            .name = "rt_runtime_us",
            .read_s64 = cpu_rt_runtime_read,
            .write_s64 = cpu_rt_runtime_write,
        },
        {
            .name = "rt_period_us",
            .read_u64 = cpu_rt_period_read_uint,
            .write_u64 = cpu_rt_period_write_uint,
        },
    #endif
        { }    /* terminate */
    }

    下面是Android一个配置,根目录下面对应的是前台应用,bg_non_interactive对应的是后台应用:

    /dev/cpuctl/cpu.shares  1024

    /dev/cpuctl/cpu.rt_runtime_us 950000

    /dev/cpuctl/cpu.rt_period_us  1000000

    /dev/cpuctl/bg_non_interactive/cpu.shares  52

    /dev/cpuctl/bg_non_interactive/cpu.rt_runtime_us 10000

    /dev/cpuctl/bg_non_interactive/cpu.rt_period_us  1000000

    cpu.shares:保存了整数值,用来设置cgroup分组任务获得CPU时间的相对值。举例来说,cgroup A和cgroup B的cpu.share值都是1024,那么cgroup A 与cgroup B中的任务分配到的CPU时间相同,如果cgroup C的cpu.share为512,那么cgroup C中的任务获得的CPU时间是A或B的一半。

    从上面的数据可以看出,默认分组与bg_non_interactive分组cpu.share值相比接近于20:1。由于Android中只有这两个cgroup,也就是说默认分组中的应用可以利用95%的CPU,而处于bg_non_interactive分组中的应用则只能获得5%的CPU利用率。

    52/(1024+52)=4.8%

    cpu.rt_runtime_us:主要用来设置cgroup获得CPU资源的周期,单位为微妙。

    cpu.rt_period_us:主要是用来设置cgroup中的任务可以最长获得CPU资源的时间,单位为微秒。设定这个值可以访问某个cgroup独占CPU资源。最长的获取CPU资源时间取决于逻辑CPU的数量。比如cpu.rt_runtime_us设置为200000(0.2秒),cpu.rt_period_us设置为1000000(1秒)。在单个逻辑CPU上的获得时间为每秒为0.2秒。 2个逻辑CPU,获得的时间则是0.4秒。

    从上面数据可以看出,默认分组中单个逻辑CPU下每一秒内可以获得0.95秒执行时间。bg_non_interactive分组下单个逻辑CPU下每一秒内可以获得0.01秒。

    疑问点:在shares和rt_period_us/cfs_period_us之间时间究竟如何分配?是先按照shares分组分配CPU时间,然后组内优先RT,剩下来给CFS?还是先RT进程按照shares分配,剩下来给CFS按照shares分配。分配时间shares在先还是RT/CFS之间在先?

    cpu.cfs_runtime_us

    cpu.cfs_quota_us

    一个测试脚本:

    #!/usr/bin/env python

    # coding=utf-8

    i = 0

    while True:

        i = i + 1

    1.执行loop.py,python loop.py &。

    2.top监控,可以看出CUP占用率在100%。

    image

    3.echo pid > tasks,将loop.py进程加入cpu组。

    4.echo 10000 > cpu.cfs_quota_us,在cpu.cfs_period_us为100000的情况下占用率应该为10%.

    image

    5.下面依次为5000、50000对应占用率为5%、50%的情况。

    image

    image

    2.2.3 cpuacct

    kernel/sched/cpuacct.c中定义cpuacct_cgrp_subsys子系统结构体:

    struct cgroup_subsys cpuacct_cgrp_subsys = {
        .css_alloc    = cpuacct_css_alloc,
        .css_free    = cpuacct_css_free,
        .legacy_cftypes    = files,
        .early_init    = 1,
    };

    其中legacy_cftypes的files是cpuacct子系统的精髓:

    static struct cftype files[] = {
        {
            .name = "usage",
            .read_u64 = cpuusage_read,
            .write_u64 = cpuusage_write,
        },
        {
            .name = "usage_percpu",
            .seq_show = cpuacct_percpu_seq_show,
        },
        {
            .name = "stat",
            .seq_show = cpuacct_stats_show,
        },
        { }    /* terminate */
    };

    要理解每个统计信息的含义就绕不开struct cpuacct这个结构体。

    usage:是所有usage_percpu之和。

    usage_percpu:是每个CPU的使用量。

    stat:分别统计user和system两种类型的,user对应CPUTIME_USER、CPUTIME_NICE,system对应CPUTIME_SYSTEM、CPUTIME_IRQ、CPUTIME_SOFTIRQ。

    struct cpuacct {
        struct cgroup_subsys_state css;  通过此成员可以和子系统关联起来
        /* cpuusage holds pointer to a u64-type object on every cpu */
        u64 __percpu *cpuusage;  per-CPU类型成员,记录每个cpu被使用程度
        struct kernel_cpustat __percpu *cpustat;  per-CPU类型成员,分类统计CPU使用时间,粒度更细,包括:
    };

    enum cpu_usage_stat {
        CPUTIME_USER,
        CPUTIME_NICE,
        CPUTIME_SYSTEM,
        CPUTIME_SOFTIRQ,
        CPUTIME_IRQ,
        CPUTIME_IDLE,
        CPUTIME_IOWAIT,
        CPUTIME_STEAL,
        CPUTIME_GUEST,
        CPUTIME_GUEST_NICE,
        NR_STATS,
    };

    下面HiKey的一个瞬间的cpuacct值:

    cpuacct.stat
    cpuacct.usage
    cpuacct.usage_percpu

    user 2312 system 2662
    57822767793
    11525417930 10088633594 7132090309 7864757745 4475737297 5532987302 8750543618 2452703748

    我们将其转换成每个CPU用量的百分比,结合cpuset的设置。可以得出结论:

    cpu0的任务最重,所有类型的进程都可能在cpu0上调度。,cluster0要比cluster1更多的被使用。

    cpu7最少被使用,因为只有top-app才会使用。

    /dev/cpuset/cpus  0-7
    /dev/cpuset/background/cpus  0
    /dev/cpuset/foreground/cpus  0-6
    /dev/cpuset/system-background/cpus  0-3
    /dev/cpuset/top-app/cpus  0-7

    image

    2.2.4 cpuset

    cpuset是一个用来分配限制CPU和Memory资源的CGroup子系统。cpuset使用sched_setaffinity系统调用来设置tasks的CPU亲和性,使用mbind和set_mempolicy包含Memory策略中的Memory Nodes。调度器不会在cpuset之外的CPU上面调度tasks,页分配器也不会在mems_allowed之外的内存中分配。

    cpuset提供了一种灵活配置CPU和Memory资源的机制。Linux中已经有配置CPU资源的cpu子系统和Memory资源的memory子系统。

    kernel/cpuset.c中定义了子系统cpuset结构体如下:

    struct cgroup_subsys cpuset_cgrp_subsys = {

    .css_alloc = cpuset_css_alloc,

    .css_online = cpuset_css_online,

    .css_offline = cpuset_css_offline,

    .css_free = cpuset_css_free,

    .can_attach = cpuset_can_attach,

    .cancel_attach = cpuset_cancel_attach,

    .attach = cpuset_attach,

    .bind = cpuset_bind,

    .legacy_cftypes = files,

    .early_init = 1,

    };

    cpu_exclusive cpu资源是否专用?

    cpus 当前cpuset的CPU列表。

    effective_cpus 有效的CPU列表

    effective_mems 有效的memory

    mem_exclusive memory资源是否专用?

    mem_hardwall

    memory_migrate 如果置位,则将页面移到cpusets节点

    memory_pressure 测量当前cpuset的paging压力

    memory_spread_page if set, spread page cache evenly on allowed nodes

    memory_spread_slab if set, spread slab cache evenly on allowed nodes

    mems 当前cpuset的Memory Nodes列表

    sched_load_balance 当前cpuset是否进行负载均衡

    sched_relax_domain_level the searching range when migrating tasks

    如果设置了cpu/memory专用,除了直接父子,其他cpuset不可以使用相应的CPU或者Memory Nodes。

    其他项的详细解释见:Documentation/cgroups/cpust.txt。

    cpuset结构体中设置的cpus_allowed、mems_allowed、effective_cpus、effective_mems都会在写入cpus、mems节点是更行到当前cpuset下的task相关的。

    其他情况还包括CPU hotplug的时候动态更新cpus_allowed信息。

    cpuset_write_resmask

      -->update_cpumask

      -->update_nodemask

        -->update_cpumasks_hier

        -->update_nodemasks_hier

          -->update_tasks_cpumask 遍历当前cpuset下所有的task的cpus_allowed

          -->update_tasks_nodemask 更新当前cpuset下所有task的mems_allowed

            -->set_cpus_allowed_ptr 将cpuset的cpumask赋给task->cpus_allowed,将task转移到合适的CPU;如果CPU被拔出,则将其迁移到其它被允许的CPU上。

            --> cpuset_migrate_mm 将memory区域从一个node迁移到另一个

              --> do_migrate_pages 在两个node之间移动页

    struct cpuset {

    struct cgroup_subsys_state css;

    unsigned long flags; /* "unsigned long" so bitops work */

    /* user-configured CPUs and Memory Nodes allow to tasks */

    cpumask_var_t cpus_allowed; 和task_struct->cpus_allowed相对应

    nodemask_t mems_allowed; 和task_struct->mems_allowed相对应

    /* effective CPUs and Memory Nodes allow to tasks */

    cpumask_var_t effective_cpus;

    nodemask_t effective_mems;

    default hierarchy:effective_mask=configured_mask&parent’s effective_mask

    legacy hierarchy: user-configured masks = effective masks

    nodemask_t old_mems_allowed;

    struct fmeter fmeter; /* memory_pressure filter */

    int attach_in_progress;

    /* for custom sched domain */

    int relax_domain_level;

    }

    cpuset在Android的应用主要差异就是不同组配置不同的cpus,根据进程类型细分。

    可以看出优先级越高的进程可以占用的cpu越多。

    /dev/cpuset/cpus  0-7
    /dev/cpuset/background/cpus  0
    /dev/cpuset/foreground/cpus  0-6
    /dev/cpuset/system-background/cpus  0-3
    /dev/cpuset/top-app/cpus  0-7

    只有根节点的mem_exclusive使能,其他都未使能。

    /dev/cpuset/mem_exclusive  1
    /dev/cpuset/background/mem_exclusive  0
    /dev/cpuset/foreground/mem_exclusive  0
    /dev/cpuset/system-background/mem_exclusive  0
    /dev/cpuset/top-app/mem_exclusive  0

    2.2.5 devices

    security/device_cgroup.c定义devices子系统结构体:

    struct cgroup_subsys devices_cgrp_subsys = {
        .css_alloc = devcgroup_css_alloc,
        .css_free = devcgroup_css_free,
        .css_online = devcgroup_online,
        .css_offline = devcgroup_offline,
        .legacy_cftypes = dev_cgroup_files,
    };

    2.2.6 hugetlb

    mm/hugetlb_cgroup.c

    struct cgroup_subsys hugetlb_cgrp_subsys

    2.2.7 memory

    mm/memcontrol.c中定义了memory子系统memory_cgrp_subsys如下:

    struct cgroup_subsys memory_cgrp_subsys = {

    .css_alloc = mem_cgroup_css_alloc,

    .css_online = mem_cgroup_css_online,

    .css_offline = mem_cgroup_css_offline,

    .css_free = mem_cgroup_css_free,

    .css_reset = mem_cgroup_css_reset,

    .can_attach = mem_cgroup_can_attach,

    .cancel_attach = mem_cgroup_cancel_attach,

    .attach = mem_cgroup_move_task,

    .allow_attach = mem_cgroup_allow_attach,

    .bind = mem_cgroup_bind,

    .dfl_cftypes = memory_files,

    .legacy_cftypes = mem_cgroup_legacy_files,

    .early_init = 0,

    };

    memory_files和mem_cgroup_legacy_files的解释如下:

    cgroup.event_control  event_fd的接口

    memory.failcnt 显示内存(进程内存+页面缓存) 达到限制值的次数

    memory.force_empty 强制释放分配给分组的内存

    memory.kmem.failcnt 显示内存(进程内存+页面缓存)+交换区到达限制值的次数

    memory.kmem.limit_in_bytes

    memory.kmem.max_usage_in_bytes 显示记录的内存(进程内存+页面缓存)+交换区使用量的最大值

    memory.kmem.slabinfo

    memory.kmem.tcp.failcnt

    memory.kmem.tcp.limit_in_bytes

    memory.kmem.tcp.max_usage_in_bytes

    memory.kmem.tcp.usage_in_bytes

    memory.kmem.usage_in_bytes

    memory.limit_in_bytes 显示当前内存(进程内存+页面缓存)的使用量的限制值

    memory.max_usage_in_bytes 显示记录的内存使用量的最大值

    memory.memsw.failcnt

    memory.memsw.limit_in_bytes 显示当前内存(进程内存+页面缓存)+交换区使用量的限制值

    memory.memsw.usage_in_bytes 显示当前内存(进程内存+页面缓存)+交换区使用量的使用值

    memory.memsw.max_usage_in_bytes

    memory.memsw.usage_in_bytes 显示当前内存(进程内存+页面缓存)+交换区使用量

    memory.move_charge_at_immigrate

    memory.oom_control

    memory.pressure_level  设置内存压力通知

    memory.soft_limit_in_bytes

    memory.stat 输出统计信息

    memory.swappiness 设置、显示针对分组的swappiness

    memory.usage_in_bytes 显示当前内存(进程内存+页面缓存)的使用量

    memory.use_hierarchy 设置、显示层次结构的使用

    Android下的一个实例,显示/dev/memcfg/apps并未使用:

    Item /dev/memcfg /dev/memcfg/apps

    memory.failcnt
    memory.force_empty
    memory.kmem.failcnt
    memory.kmem.limit_in_bytes
    memory.kmem.max_usage_in_bytes
    memory.kmem.slabinfo
    memory.kmem.tcp.failcnt
    memory.kmem.tcp.limit_in_bytes
    memory.kmem.tcp.max_usage_in_bytes
    memory.kmem.tcp.usage_in_bytes
    memory.kmem.usage_in_bytes
    memory.limit_in_bytes
    memory.max_usage_in_bytes
    memory.memsw.failcnt
    memory.memsw.limit_in_bytes
    memory.memsw.max_usage_in_bytes
    memory.memsw.usage_in_bytes
    memory.move_charge_at_immigrate
    memory.oom_control
    memory.pressure_level
    memory.soft_limit_in_bytes
    memory.stat
    memory.swappiness
    memory.usage_in_bytes
    memory.use_hierarchy

    0
    cat: memory.force_empty: Invalid argument
    0
    9223372036854771712
    0
    slabinfo - version: 2.1
    0
    2251799813685247
    0
    0
    0
    9223372036854771712
    0
    0
    9223372036854771712
    0
    644067328
    0
    oom_kill_disable 0 nder_oom 0
    cat: memory.pressure_level: Invalid argument
    9223372036854771712
    cache 446771200 …
    60
    644067328
    0

    0
    cat: memory.force_empty: Invalid argument
    0
    9223372036854771712
    0
    slabinfo - version: 2.1
    0
    9223372036854771712
    0
    0
    0
    9223372036854771712
    0
    0
    9223372036854771712
    0
    0
    0
    oom_kill_disable 0 under_oom 0
    cat: memory.pressure_level: Invalid argument
    9223372036854771712
    cache 0
    60
    0
    0

    两者的memory.stat如下:

    /dev/memcfg /dev/memcfg/apps

    cache 446832640
    rss 197902336
    rss_huge 2097152
    mapped_file 321081344
    dirty 0
    writeback 0
    swap 0
    pgpgin 252747
    pgpgout 96874
    pgfault 335718
    pgmajfault 3715
    inactive_anon 1814528
    active_anon 201084928
    inactive_file 362172416
    active_file 79249408
    unevictable 262144
    hierarchical_memory_limit 9223372036854771712
    hierarchical_memsw_limit 9223372036854771712
    total_cache 446832640
    total_rss 197902336
    total_rss_huge 2097152
    total_mapped_file 321081344
    total_dirty 0
    total_writeback 0
    total_swap 0
    total_pgpgin 252747
    total_pgpgout 96874
    total_pgfault 335718
    total_pgmajfault 3715
    total_inactive_anon 1814528
    total_active_anon 201084928
    total_inactive_file 362172416
    total_active_file 79249408
    total_unevictable 262144

    cache 0
    rss 0
    rss_huge 0
    mapped_file 0
    dirty 0
    writeback 0
    swap 0
    pgpgin 0
    pgpgout 0
    pgfault 0
    pgmajfault 0
    inactive_anon 0
    active_anon 0
    inactive_file 0
    active_file 0
    unevictable 0
    hierarchical_memory_limit 9223372036854771712
    hierarchical_memsw_limit 9223372036854771712
    total_cache 0
    total_rss 0
    total_rss_huge 0
    total_mapped_file 0
    total_dirty 0
    total_writeback 0
    total_swap 0
    total_pgpgin 0
    total_pgpgout 0
    total_pgfault 0
    total_pgmajfault 0
    total_inactive_anon 0
    total_active_anon 0
    total_inactive_file 0
    total_active_file 0
    total_unevictable 0

    2.2.8 net_cls

    net/core/netclassid_cgroup.c

    struct cgroup_subsys net_cls_cgrp_subsys

    2.2.9 net_prio

    net/core/netprio_cgroup.c

    struct cgroup_subsys net_prio_cgrp_subsys

    2.2.10 net_perf

    kernel/event/core.c

    struct cgroup_subsys perf_event_cgrp_subsys

    2.2.11 perf_event

    kernel/events/core.c

    struct cgroup_subsys perf_event_cgrp_subsys

    3 CGroup在Android中的应用

    # Mount cgroup mount point for cpu accounting

    mount cgroup none /acct cpuacct

    schedtune是ARM/Linaro为了EAS新增的一个子系统,主要用来控制进程调度选择CPU以及boost触发。

    这部分涉及到EAS、Android进程调度策略等相关知识,另起一篇专门介绍《Android中关于cpu/cpuset/schedtune的应用》。

    https://git.linaro.org/people/john.stultz/android-dev.git

    branch:remotes/origin/android-hikey-linaro-4.4-EASv5.2+aosp

    # Create energy-aware scheduler tuning nodes

    mkdir /dev/stune

    mount cgroup none /dev/stune schedtune

    # root memory control cgroup, used by lmkd

    mkdir /dev/memcg 0700 root system

    mount cgroup none /dev/memcg memory

    # Create cgroup mount points for process groups

    mkdir /dev/cpuctl

    mount cgroup none /dev/cpuctl cpu

    # sets up initial cpusets for ActivityManager
    mkdir /dev/cpuset
    mount cpuset none /dev/cpuset

    以上sysfs节点在Android中都有对应的HAL层库文件,其中cpuctl/cpuset/stune对应libcutils.so,代码在system/core/libcutils中,主要根据进程的不同分类,进行CPU、memory资源控制;memory子系统对应lmkd系统服务,代码在system/core/lmkd,主要在内存紧张情况下,杀死低优先级进程,以达到释放内存的目的。

    针对libcutils.so会在《Android中关于cpu/cpuset/schedtune的应用》,lmkd会在《Android中基于CGroup的memory子系统HAL层分析-lmkd》。

  • 相关阅读:
    中国剩余定理及拓展
    20191128-1 总结
    获取动态图
    弹球游戏设计
    作业要求 20191121-1 每周例行报告
    作业要求 20191114-1 每周例行报告
    对现组内成员的感谢
    作业要求 20191107-1 每周例行报告
    20191031-1 每周例行报告
    作业要求 20191024-1每周例行报告
  • 原文地址:https://www.cnblogs.com/arnoldlu/p/6208443.html
Copyright © 2020-2023  润新知