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》。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM