android c 多线程,Android CGroup实际情况调研

内容概要:对于Android线程所属的CGroup,一些资料认为是通过线程优先级来进行划分的。但经实践,发现部分Android版本与该观点并不一致,那么实际情况又是怎样的呢?本篇文章就该问题进行了探讨。

一、CGroup简要介绍

在Linux中,不同线程分配cpu时间片的策略首先是基于线程优先级的,线程优先级越高,越容易分配到cpu。但是这样就产生了低优先级线程一直都被抢占cpu时间的问题,为解决该问题,Linux 2.6.23版本中引入了CFS策略,该策略不但会参考单个线程的优先级,还会追踪每个线程已经获取到的time slice数量,如果高优先级的线程已经执行了很长时间,但低优先级的线程一直在等待,后续系统会保证低优先级的线程也能获取更多的CPU时间。但这就产生了新的问题:优先级高的线程并不一定总能在争取时间片上有绝对的优势,反映在Android中,就会出现UI线程被后台线程抢占cpu时间的问题。所以在Linux 2.6.24中又引入了cgroups的概念,让从属于特定CGroup的线程能够占据更多的时间片而不被低线程抢占,从而提升了总体效率

[1]。

二、Android中不同版本所属CGroup调研

在Android中,存在两类特别重要的CGroup,一类是foreground group,UI线程属于这一类。另一类是background group,工作线程属于这一类。那么你可能要问,线程所属foreground group和background group到底怎样划分呢?查阅API文档以及相关资料

[2],当我们使用setThreadPriority,nice值大于等于THREAD_PRIORITY_BACKGROUND将属于background group,其余属于foreground group。

但是,这种说法正确嘛?让我们来做个实验,首先通过以下代码设置线程优先级。

private static final ThreadFactory sDBThreadFactory = new ThreadFactory() {

private final AtomicInteger mCount = new AtomicInteger(1);

public Thread newThread(@NonNull Runnable r){

WorkerRunnable wr = new WorkerRunnable(r, Process.THREAD_PRIORITY_BACKGROUND);

return new Thread(wr, "Async DB Thread #" + mCount.getAndIncrement());

}

};

private static class WorkerRunnable implements Runnable{

Runnable runnable;

int priority;

String tag;

public WorkerRunnable(Runnable runnable, int priority){

this(runnable, priority, "WorkerRunnable");

}

public WorkerRunnable(Runnable runnable, int priority, String tag){

this.runnable = runnable;

this.priority = priority;

this.tag = tag;

}

@Override

public void run(){

android.os.Process.setThreadPriority(priority);

runnable.run();

}

@Override

public String toString(){

return tag + ":" + runnable;

}

}

在Android5.1.0中当线程优先级为Process.THREAD_PRIORITY_BACKGROUND的Async DB Thread所属的进程处于前台时,其cgroup却为fg,当所属进程进入到后台时,其cgroup又为bg:

dcd5e296c2889c700bbe6616e3767f8c.png

d382131afeb67b0be6ddc35787c85c15.png

而在Android 4.1中,无论是否处于前台,其cgroup都为bg:

252bd64888ccff45a627bc9d8f6cac3f.png

看来,实际情况是和版本有关,那么线程的cgroup是具体怎样设置的呢?查阅相关源代码

[3],找到是在sched_policy.c的get_sched_policy方法进行相关设置的,这里以Android4.1为例,其他版本逻辑基本一致,除了Android7.0

[4],增加了一层宏定义判断#ifdef USE_CPUSETS。

int get_sched_policy(int tid, SchedPolicy *policy)

{

#ifdef HAVE_GETTID

if (tid == 0) {

tid = gettid();

}

#endif

pthread_once(&the_once, __initialize);

if (__sys_supports_schedgroups) {

char grpBuf[32];

if (getSchedulerGroup(tid, grpBuf, sizeof(grpBuf)) < 0)

return -1;

if (grpBuf[0] == '\0') {

*policy = SP_SYSTEM;

} else if (!strcmp(grpBuf, "apps/bg_non_interactive")) {

*policy = SP_BACKGROUND;

} else if (!strcmp(grpBuf, "apps")) {

*policy = SP_FOREGROUND;

} else {

errno = ERANGE;

return -1;

}

} else {

int rc = sched_getscheduler(tid);

if (rc < 0)

return -1;

else if (rc == SCHED_NORMAL)

*policy = SP_FOREGROUND;

else if (rc == SCHED_BATCH)

*policy = SP_BACKGROUND;

else {

errno = ERANGE;

return -1;

}

}

return 0;

}

上述代码基本逻辑概要如下:

b25da2dd19e60cd19ebe4edd6cfd692c.png

该方法首先查看变量__sys_supports_schedgroups是否为true,

if (!access("/dev/cpuctl/tasks", F_OK)) {

__sys_supports_schedgroups = 1;

} 也就是查看/dev/cpuctl/tasks是否存在:当/dev/cpuctl/tasks存在时,access返回0,满足条件__sys_supports_schedgroups为1。

那么Android5.1满足什么样的条件呢?实际上,在Android5.1的init.rc

[5]中会创建了该tasks文件,但4.1.1

[6]不存在该文件。那么5.1.0系统就使用getSchedulerGroup得到cgroup,也就是查找/proc/线程id/cgroup文件中的2:cpu:/一行的内容。

当整个进程在前台时,其2:cpu:/的值为空,而处于后台时,则为bg_non_interactive。

055cc137fadb8c20430267d6bbcd8d58.png

而/proc//cgroup 文件中的2:cpu:的值又是怎样写进去的呢?参考Linux源码cgroup.c中的处理

[7]和相关资料

[8],发现创建的子线程也是同样用的所属进程的cgroup。

接下来是Android4.1.1,他直接使用sched_getscheduler,也就是我们之前sched_setscheduler设置的policy:

sched_setscheduler(tid, (policy == SP_BACKGROUND) ? SCHED_BATCH : SCHED_NORMAL, ¶m); 而sched_setscheduler则是由set_sched_policy调用:

if (gDoSchedulingGroup) {

if (prio >= ANDROID_PRIORITY_BACKGROUND) {

set_sched_policy(androidGetTid(), SP_BACKGROUND);

} else if (prio > ANDROID_PRIORITY_AUDIO) {

set_sched_policy(androidGetTid(), SP_FOREGROUND);

} else {

// defaults to that of parent, or as set by requestPriority()

}

}

也就是通过线程的优先级来设置所属CGroup。

上面提到的/dev/cpuctl/tasks和在Android7.0中引入的/dev/cpuset/等文件的创建在init.rc中进行定义,不同的版本有不同的定义,这里查阅不同版本的源码,做一下Android不同版本的CGroup的实际情况的总结:

Android 4.0 Ice Cream Sandwich,根据线程优先级设置cgroup

[9]

Android 4.1/4.2/4.3 Jelly Bean,根据线程优先级设置cgroup

[6]

Android 4.4 KitKat,根据线程优先级设置cgroup

[10]

Android 5.0/5.1 Lollipop,取所属进程的cgroup值

[5]

Android 6.0 Marshmallow,取所属进程的cgroup值

[11]

Android 7.0 Nougat,取所属进程的cgroup值

[12]

本文来自网易实践者社区,经作者冯越新授权发布。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值