Android中關於cpu/cpuset/schedtune的應用都是基於進程優先級的,根據不同優先級划分進程類型。AMS(ActivityManagerService)和PMS(PackageManagerService)等通過class Process設置進程優先級、調度策略等;android/osProcess JNI通過調用libcutils.so/libutils.so執行getpriority/setpriority/sched_setscheduler/sched_getschedler系統調用或者直接操作CGroup文件節點以達到設置優先級,限制進程CPU資源的目的。
根據優先級,通過設置CGroup的cpu/cpuset/stune控制進程獲得CPU執行時間、可調度CPU范圍等,以達到對不同優先級進程的控制。
Android關於cpu/cpuset/schedtune的框架結構
進程優先級和調度策略從上到下貫穿其中,但是在不同的層級的名稱有一些變化。下面逐一介紹。
class Process以及android/os/Process JNI
frameworks/base/core/java/android/os/Process.java
其他服務通過class Process來設置進程優先級、調度側率等。
class Process中優先級划分:
public static final int THREAD_PRIORITY_DEFAULT = 0; 應用的默認優先級 /* public static final int THREAD_PRIORITY_LOWEST = 19; 線程的最低優先級 public static final int THREAD_PRIORITY_BACKGROUND = 10; 后台線程的默認優先級 public static final int THREAD_PRIORITY_FOREGROUND = -2; 前台進程的標准優先級 public static final int THREAD_PRIORITY_DISPLAY = -4; 系統用於顯示功能的優先級 public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8; 系統用於重要顯示功能的優先級 public static final int THREAD_PRIORITY_AUDIO = -16; 音頻線程默認優先級 public static final int THREAD_PRIORITY_URGENT_AUDIO = -19; 重要音頻線程默認優先級 |
調度策略划分:
public static final int SCHED_OTHER = 0; 默認調度策略,對應CFS調度類 public static final int SCHED_FIFO = 1; FIFO調度策略,對應RT調度類 public static final int SCHED_RR = 2; RR調度策略,對應RT調度類 public static final int SCHED_BATCH = 3; 批調度策略,對應CFS調度類 public static final int SCHED_IDLE = 5; idle調度策略 |
class Process相關API,主要用於:
public static final native void setThreadPriority(int tid, int priority) public static final native void setThreadScheduler(int tid, int policy, int priority) public static final native void setThreadPriority(int tid, int priority) public static final native int getThreadPriority(int tid) public static final native int getThreadScheduler(int tid) public static final native void setThreadGroup(int tid, int group) public static final native void setProcessGroup(int pid, int group) |
frameworks/base/core/jni/android_util_Process.cpp
對應JNINativeMethod如下:
static const JNINativeMethod methods[] = { |
scheduler相關API直接調用sched_setscheduler/sched_getscheduler。
libcutils.so/libutils.so
在介紹這個函數之前先介紹一下此處所使用的優先級定義,可以看出和class Process中是完全的對應關系:
ANDROID_PRIORITY_LOWEST = 19, /* use for background tasks */ /* most threads run at normal priority */ /* threads currently running a UI that the user is interacting with */ /* the main UI thread has a slightly more favorable priority */ /* ui service treads might want to run at a urgent display (uncommon) */ /* all normal audio threads */ /* service audio threads (uncommon) */ /* should never be used in practice. regular process might not ANDROID_PRIORITY_DEFAULT = ANDROID_PRIORITY_NORMAL, |
還需要在研究一下,Sched Policy中使用的優先級映射關系:
/* Keep in sync with THREAD_GROUP_* in frameworks/base/core/java/android/os/Process.java */ |
Threads.cpp中定義了androidSetThreadPriority用於設置線程的優先級。
int androidSetThreadPriority(pid_t tid, int pri) if (pri >= ANDROID_PRIORITY_BACKGROUND) { 如果priority大於等於BACKGROUND,則設置為BACKGROUND類型的調度策略。 if (rc) { if (setpriority(PRIO_PROCESS, tid, pri) < 0) { 設置優先級 return rc; |
set_cpuset_policy根據SchedPolicy類型將tid寫入cpuset和schedtune子系統中。
有下面的函數可以得出cpuset、schedtune和不同類型SchedPolicy之間的對應關系:
/dev/cpuset/foreground/tasks SP_FOREGROUND SP_AUDIO_APP SP_AUDIO_SYS
/dev/cpuset/background/tasks SP_BACKGROUND
/dev/cpuset/system-background/tasks SP_SYSTEM
/dev/cpuset/top-app/tasks SP_TOP_APP
/dev/stune/top-app/tasks SP_TOP_APP
/dev/stune/foreground/tasks SP_FOREGROUND SP_AUDIO_APP SP_AUDIO_SYS
/dev/stune/background/tasks SP_BACKGROUND
int set_cpuset_policy(int tid, SchedPolicy policy) int fd = -1; if (add_tid_to_cgroup(tid, fd) != 0) { #ifdef USE_SCHEDBOOST return 0; |
set_sched_policy設置cpu/schedtune兩個子系統,子系統節點和SchedPolicy類型對應如下:
/dev/cpuctl/tasks SP_FOREGROUND SP_AUDIO_APP SP_AUDIO_SYS
/dev/cpuctl/bg_non_interactive/tasks SP_BACKGROUND
/dev/stune/top-app/tasks SP_TOP_APP
/dev/stune/foreground/tasks SP_FOREGROUND SP_AUDIO_APP SP_AUDIO_SYS
/dev/stune/background/tasks SP_BACKGROUND
int set_sched_policy(int tid, SchedPolicy policy) #if POLICY_DEBUG snprintf(statfile, sizeof(statfile), "/proc/%d/stat", tid); int fd = open(statfile, O_RDONLY | O_CLOEXEC); for (p = statline; *p != '('; p++); strncpy(thread_name, p, (q-p)); if (__sys_supports_schedgroups) { 是否使能schedtune CGroup
#ifdef USE_SCHEDBOOST param.sched_priority = 0; if (__sys_supports_timerslack) { return 0; |
上面的一系列轉換可以用下圖表示:
system\core\libcutils\Sched_policy.c中,對SP_*系列SchedPolicy轉換成使用不同cpuctl、cpuset、stune句柄,將對應的pid、tid寫入tasks中。
SP_BACKGROUND對應SCHED_BACH調度策略,其他對應SCHED_NORMAL。
SCHED_NORMAL:默認的調度策略,在舊版中為SCHED_OTHER。SCHED_BATCH:針對批處理進程。SCHED_IDLE:使用此調度側率的進程優先級最低。
SCHED_NORMAL和SCHED_BATCH區別只是再喚醒時有區別,喚醒較頻繁的進程不適合SCHED_BATCH。
如果使能__sys_supports_schedgroups,就不會調用sched_setscheduler去設置SchedulePolicy。
SCHED_NORMAL和SCHED_BACH區別
SP_BACKGROUND對應SCHED_BACH調度策略,其他對應SCHED_NORMAL。
SCHED_NORMAL:默認的調度策略,在舊版中為SCHED_OTHER。SCHED_BATCH:針對批處理進程。SCHED_IDLE:使用此調度側率的進程優先級最低。
SCHED_NORMAL和SCHED_BATCH區別只是再喚醒時有區別,喚醒較頻繁的進程不適合SCHED_BATCH。
如果使能__sys_supports_schedgroups,就不會調用sched_setscheduler去設置SchedulePolicy。
Android中的一個應用
cpu子系統:
# Create cgroup mount points for process groups mkdir /dev/cpuctl mount cgroup none /dev/cpuctl cpu chown system system /dev/cpuctl chown system system /dev/cpuctl/tasks chmod 0666 /dev/cpuctl/tasks write /dev/cpuctl/cpu.shares 1024 write /dev/cpuctl/cpu.rt_runtime_us 800000 write /dev/cpuctl/cpu.rt_period_us 1000000 mkdir /dev/cpuctl/bg_non_interactive chown system system /dev/cpuctl/bg_non_interactive/tasks chmod 0666 /dev/cpuctl/bg_non_interactive/tasks # 5.0 % write /dev/cpuctl/bg_non_interactive/cpu.shares 52 write /dev/cpuctl/bg_non_interactive/cpu.rt_runtime_us 700000 write /dev/cpuctl/bg_non_interactive/cpu.rt_period_us 1000000 |
cpuset子系統:
# sets up initial cpusets for ActivityManager mkdir /dev/cpuset mount cpuset none /dev/cpuset # this ensures that the cpusets are present and usable, but the device's # init.rc must actually set the correct cpus mkdir /dev/cpuset/foreground write /dev/cpuset/foreground/cpus 0 write /dev/cpuset/foreground/mems 0 mkdir /dev/cpuset/foreground/boost write /dev/cpuset/foreground/boost/cpus 0 write /dev/cpuset/foreground/boost/mems 0 mkdir /dev/cpuset/background write /dev/cpuset/background/cpus 0 write /dev/cpuset/background/mems 0 # system-background is for system tasks that should only run on # little cores, not on bigs # to be used only by init, so don't change system-bg permissions mkdir /dev/cpuset/system-background write /dev/cpuset/system-background/cpus 0 write /dev/cpuset/system-background/mems 0 mkdir /dev/cpuset/top-app write /dev/cpuset/top-app/cpus 0 write /dev/cpuset/top-app/mems 0 # change permissions for all cpusets we'll touch at runtime chown system system /dev/cpuset chown system system /dev/cpuset/foreground chown system system /dev/cpuset/foreground/boost chown system system /dev/cpuset/background chown system system /dev/cpuset/system-background chown system system /dev/cpuset/top-app chown system system /dev/cpuset/tasks chown system system /dev/cpuset/foreground/tasks chown system system /dev/cpuset/foreground/boost/tasks chown system system /dev/cpuset/background/tasks chown system system /dev/cpuset/system-background/tasks chown system system /dev/cpuset/top-app/tasks # set system-background to 0775 so SurfaceFlinger can touch it chmod 0775 /dev/cpuset/system-background chmod 0664 /dev/cpuset/foreground/tasks chmod 0664 /dev/cpuset/foreground/boost/tasks chmod 0664 /dev/cpuset/background/tasks chmod 0664 /dev/cpuset/system-background/tasks chmod 0664 /dev/cpuset/top-app/tasks chmod 0664 /dev/cpuset/tasks |
schedtune子系統:
# Create energy-aware scheduler tuning nodes mkdir /dev/stune mount cgroup none /dev/stune schedtune mkdir /dev/stune/foreground chown system system /dev/stune chown system system /dev/stune/foreground chown system system /dev/stune/tasks chown system system /dev/stune/foreground/tasks chmod 0664 /dev/stune/tasks chmod 0664 /dev/stune/foreground/tasks |