1 原理解读
一般来说这个需求主要是为了应用启动时设定绑核相关操作,但是如果没有三方应用的源码想要让其绑定再其他核心上就要修改framework源码了。framework源码修改的原理是:在zygote创建应用子进程(Fork操作)时做白名单处理,针对不同的应用APP进程进行可以有不同的绑核策略。关于Zygote的初始化以及创建APP进程的流程这里就不多说了,最终创建进程回调用到这里:com_android_internal_os_Zygote.cpp中的
com_android_internal_os_Zygote_nativeForkAndSpecialize方法。代码实现如下:
com_android_internal_os_Zygote_nativeForkAndSpecialize(...){
//...
if (pid == 0) {
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, capabilities, capabilities,
mount_external, se_info, nice_name, false, is_child_zygote == JNI_TRUE,
instruction_set, app_data_dir, is_top_app == JNI_TRUE, pkg_data_info_list,
allowlisted_data_info_list, mount_data_dirs == JNI_TRUE,
mount_storage_dirs == JNI_TRUE);
}
//...
}
2 修改方案(Android Q R S)
修改文件为:AOSP/frameworks/base/core/jni/com_android_internal_os_Zygote.cpp,这里主要给出了3个应用xxx1、xxx2、xxx3,绑核策略分别为7,456,234,根据此需求,具体修改内容为:
// Utility routine to specialize a zygote child process.
static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, jint runtime_flags,
jobjectArray rlimits, jlong permitted_capabilities,
jlong effective_capabilities, jint mount_external,
jstring managed_se_info, jstring managed_nice_name,
bool is_system_server, bool is_child_zygote,
jstring managed_instruction_set, jstring managed_app_data_dir,
bool is_top_app, jobjectArray pkg_data_info_list,
jobjectArray allowlisted_data_info_list, bool mount_data_dirs,
bool mount_storage_dirs) {
const char* process_name = is_system_server ? "system_server" : "zygote";
auto fail_fn = std::bind(ZygoteFailure, env, process_name, managed_nice_name, _1);
auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
auto se_info = extract_fn(managed_se_info);
auto nice_name = extract_fn(managed_nice_name);
auto instruction_set = extract_fn(managed_instruction_set);
auto app_data_dir = extract_fn(managed_app_data_dir);
// Keep capabilities across UID change, unless we're staying root.
if (uid != 0) {
EnableKeepCapabilities(fail_fn);
}
SetInheritable(permitted_capabilities, fail_fn);
DropCapabilitiesBoundingSet(fail_fn);
//...
const char* se_info_ptr = se_info.has_value() ? se_info.value().c_str() : nullptr;
const char* nice_name_ptr = nice_name.has_value() ? nice_name.value().c_str() : nullptr;
if (selinux_android_setcontext(uid, is_system_server, se_info_ptr, nice_name_ptr) == -1) {
fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
is_system_server, se_info_ptr, nice_name_ptr));
}
// Make it easier to debug audit logs by setting the main thread's name to the
// nice name rather than "app_process".
if (nice_name.has_value()) {
SetThreadName(nice_name.value());
+ //xxx1应用绑定核心7
+ if(strncmp(nice_name.value().c_str(),"com.xxx.xxx1",sizeof("com.xxx.xxx1"))==0){
+ cpu_set_t mask;
+ CPU_ZERO(&mask);
+ CPU_SET(7, &mask);
+ int ret = sched_setaffinity(0, sizeof(mask), &mask);
+ if (ret != 0) {
+ ALOGE("xxx1,setSchedAffinity call failure,ret:%d,(%s)", ret,nice_name.value().c_str());
+ }else{
+ ALOGE("xxx1,setSchedAffinity call success,ret==0,(%s)", nice_name.value().c_str());
+ }
+ }
+ //xxx2应用绑定核心4、5、6
+ if(strncmp(nice_name.value().c_str(),"com.xxx.xxx2",sizeof("com.xxx.xxx2"))==0){
+ cpu_set_t mask;
+ CPU_ZERO(&mask);
+ CPU_SET(6, &mask);
+ CPU_SET(5, &mask);
+ CPU_SET(4, &mask);
+ int ret = sched_setaffinity(0, sizeof(mask), &mask);
+ if (ret != 0) {
+ ALOGE("xxx2,setSchedAffinity call failure,ret:%d,(%s)", ret,nice_name.value().c_str());
+ }else{
+ ALOGE("xxx2,setSchedAffinity call success,ret==0,(%s)", nice_name.value().c_str());
+ }
+ }
+ //xxx3应用绑定核心2、3、4
+ if(strncmp(nice_name.value().c_str(),"com.xxx.xxx3",sizeof("com.xxx.xxx3"))==0){
+ cpu_set_t mask;
+ CPU_ZERO(&mask);
+ CPU_SET(4, &mask);
+ CPU_SET(3, &mask);
+ CPU_SET(2, &mask);
+ int ret = sched_setaffinity(0, sizeof(mask), &mask);
+ if (ret != 0) {
+ ALOGE("xxx3,setSchedAffinity call failure,ret:%d,(%s)", ret,nice_name.value().c_str());
+ }else{
+ ALOGE("xxx3,setSchedAffinity call success,ret==0,(%s)", nice_name.value().c_str());
+ }
+ }
} else if (is_system_server) {
SetThreadName("system_server");
}
// Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
UnsetChldSignalHandler();
if (is_system_server) {
env->CallStaticVoidMethod(gZygoteClass, gCallPostForkSystemServerHooks, runtime_flags);
if (env->ExceptionCheck()) {
fail_fn("Error calling post fork system server hooks.");
}
// TODO(b/117874058): Remove hardcoded label here.
static const char* kSystemServerLabel = "u:r:system_server:s0";
if (selinux_android_setcon(kSystemServerLabel) != 0) {
fail_fn(CREATE_ERROR("selinux_android_setcon(%s)", kSystemServerLabel));
}
}
if (is_child_zygote) {
initUnsolSocketToSystemServer();
}
env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
is_system_server, is_child_zygote, managed_instruction_set);
// Reset the process priority to the default value.
setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_DEFAULT);
if (env->ExceptionCheck()) {
fail_fn("Error calling post fork hooks.");
}
}