前言
app_process可是个好东西,由它启的进程虽然也是运行在手机上,但却是独立运行在linux上的,不受android的管控,所以我们可以利用framework.jar做很多事情。
1.源码位置&编译
源码位于 frameworks/base/cmds/app_process/app_main.cpp
相关源码位置
frameworks/base/core/jni/AndroidRuntime.cpp
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
编译:source, lunch完在以上目录mma 或者 croot , make app_process
2.简要启动流程
2.1 主线-初识AppRuntime
android的根进程zygote还有system_server就是app_process启动的,这里主要写一下tool这个方向的启动流程
输入app_process的启动命令后,会执行app_main的main方法。
2.1.1 AppRuntime的创建
main方法首先创建了AppRuntime的对象,AppRuntime是AndroidRuntime的子类
int main(int argc, char* const argv[])
{
...
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
...
2.1.2 AppRuntime类
AppRuntime有以下几个方法,tool的启动流程里 我们主要关注setClassNameAndArgs,onVmCreated,onStarted这三个方法,这里先简单的列一下该类和方法。
class AppRuntime : public AndroidRuntime
{
func 1.
void setClassNameAndArgs(const String8& className, int argc, char * const *argv)
{
...
}
func 2.
virtual void onVmCreated(JNIEnv* env)
{
...
}
func 3.
virtual void onStarted()
{
...
}
func 4.
virtual void onZygoteInit()
{
...
}
func5.
virtual void onExit(int code)
{
...
}
2.1.3 父类构造
这时候会走到AndroidRuntime的构造, init_android_graphics会初始化android的UI图形化,然后赋值gCurRuntime为当前类对象。这里后面会用到
AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength) :
mExitWithoutCleanup(false),
mArgBlockStart(argBlockStart),
mArgBlockLength(argBlockLength)
{
init_android_graphics();
// Pre-allocate enough space to hold a fair number of options.
mOptions.setCapacity(20);
assert(gCurRuntime == NULL); // one per process
gCurRuntime = this;
}
2.2 支线-skia的初始化
Skia是google的图形化的库,android,chrome,flutter的渲染都离不开它。
所以这里浅浅的跟一下。首先看init_android_graphics。
我们可以看见引入了 android/graphics/jni_runtime.h这个头文件。
void init_android_graphics() {
SkGraphics::Init();
}
这里导入了SkGraphics.h并调用了SkGraphics的Init方法。
SkGraphics.h位于external/skia/include/core/SkGraphics.h
这里真正调用到的是external/skia/src/core/SkGraphics.cpp的Init方法
void SkGraphics::Init() {
// SkGraphics::Init() must be thread-safe and idempotent.
SkCpu::CacheRuntimeFeatures();
SkOpts::Init();
}
第一个方法会去读cpu的信息 这里是cpuid相关的文档https://www.sandpile.org/x86/cpuid.htm
static uint32_t read_cpu_features() {
uint32_t features = 0;
uint32_t abcd[4] = {0,0,0,0};
cpuid(abcd);
if (abcd[3] & (1<<25)) { features |= SkCpu:: SSE1; }
if (abcd[3] & (1<<26)) { features |= SkCpu:: SSE2; }
if (abcd[2] & (1<< 0)) { features |= SkCpu:: SSE3; }
if (abcd[2] & (1<< 9)) { features |= SkCpu::SSSE3; }
if (abcd[2] & (1<<19)) { features |= SkCpu::SSE41; }
if (abcd[2] & (1<<20)) { features |= SkCpu::SSE42; }
if ((abcd[2] & (3<<26)) == (3<<26) // XSAVE + OSXSAVE
&& (xgetbv(0) & (3<<1)) == (3<<1)) { // XMM and YMM state enabled.
if (abcd[2] & (1<<28)) { features |= SkCpu:: AVX; }
if (abcd[2] & (1<<29)) { features |= SkCpu::F16C; }
if (abcd[2] & (1<<12)) { features |= SkCpu:: FMA; }
cpuid7(abcd);
if (abcd[1] & (1<<5)) { features |= SkCpu::AVX2; }
if (abcd[1] & (1<<3)) { features |= SkCpu::BMI1; }
if (abcd[1] & (1<<8)) { features |= SkCpu::BMI2; }
if (abcd[1] & (1<<9)) { features |= SkCpu::ERMS; }
if ((xgetbv(0) & (7<<5)) == (7<<5)) { // All ZMM state bits enabled too.
if (abcd[1] & (1<<16)) { features |= SkCpu::AVX512F; }
if (abcd[1] & (1<<17)) { features |= SkCpu::AVX512DQ; }
if (abcd[1] & (1<<21)) { features |= SkCpu::AVX512IFMA; }
if (abcd[1] & (1<<26)) { features |= SkCpu::AVX512PF; }
if (abcd[1] & (1<<27)) { features |= SkCpu::AVX512ER; }
if (abcd[1] & (1<<28)) { features |= SkCpu::AVX512CD; }
if (abcd[1] & (1<<30)) { features |= SkCpu::AVX512BW; }
if (abcd[1] & (1<<31)) { features |= SkCpu::AVX512VL; }
}
}
return features;
}
这里就不细跟这条支线了,感兴趣的可以按对应路径继续跟一下。
2.3 主线-Jvm参数设置
紧接着在main函数对参数做了判断和处理 首先是jvm的参数判断和传递
const char* spaced_commands[] = { "-cp", "-classpath" };
// Allow "spaced commands" to be succeeded by exactly 1 argument (regardless of -s).
bool known_command = false;
int i;
for (i = 0; i < argc; i++) {
if (known_command == true) {
runtime.addOption(strdup(argv[i]));
// The static analyzer gets upset that we don't ever free the above
// string. Since the allocation is from main, leaking it doesn't seem
// problematic. NOLINTNEXTLINE
ALOGV("app_process main add known option '%s'", argv[i]);
known_command = false;
continue;
}
for (int j = 0;
j < static_cast<int>(sizeof(spaced_commands) / sizeof(spaced_commands[0]));
++j) {
if (strcmp(argv[i], spaced_commands[j]) == 0) {
known_command = true;
ALOGV("app_process main found known command '%s'", argv[i]);
}
}
if (argv[i][0] != '-') {
break;
}
if (argv[i][1] == '-' && argv[i][2] == 0) {
++i; // Skip --.
break;
}
runtime.addOption(strdup(argv[i]));
// The static analyzer gets upset that we don't ever free the above
// string. Since the allocation is from main, leaking it doesn't seem
// problematic. NOLINTNEXTLINE
ALOGV("app_process main add option '%s'", argv[i]);
}
runtime.addOption 是AndroidRuntime的方法 会把vm相关的参数放进Vector然后传给vm
void AndroidRuntime::addOption(const char* optionString, void* extraInfo)
{
JavaVMOption opt;
opt.optionString = optionString;
opt.extraInfo = extraInfo;
mOptions.add(opt);
}
这里调用了mOptions.add,mOptions是定义在AndroidRuntime.h里的。
Vector<JavaVMOption> mOptions;
2.4 主线-zygote,system_server的判断
Zygote , SystemServer都是app_process启动的。
对于其他参数 这里主要针对启动的类型做了以下判断
bool zygote = false;
bool startSystemServer = false;
bool application = false;
String8 niceName;
String8 className;
++i; // Skip unused "parent dir" argument.
while (i < argc) {
const char* arg = argv[i++];
if (strcmp(arg, "--zygote") == 0) {
zygote = true;
niceName = ZYGOTE_NICE_NAME;
} else if (strcmp(arg, "--start-system-server") == 0) {
startSystemServer = true;
} else if (strcmp(arg, "--application") == 0) {
application = true;
} else if (strncmp(arg, "--nice-name=", 12) == 0) {
niceName = (arg + 12);
} else if (strncmp(arg, "--", 2) != 0) {
className = arg;
break;
} else {
--i;
break;
}
}
2.5 主线-其他判断
这部分代码主要是其他判断,参数的整合以及进程名的设置
Vector<String8> args;
if (!className.empty()) {
// We're not in zygote mode, the only argument we need to pass
// to RuntimeInit is the application argument.
//
// The Remainder of args get passed to startup class main(). Make
// copies of them before we overwrite them with the process name.
args.add(application ? String8("application") : String8("tool"));
runtime.setClassNameAndArgs(className, argc - i, argv + i);
if (!LOG_NDEBUG) {
String8 restOfArgs;
char* const* argv_new = argv + i;
int argc_new = argc - i;
for (int k = 0; k < argc_new; ++k) {
restOfArgs.append("\"");
restOfArgs.append(argv_new[k]);
restOfArgs.append("\" ");
}
ALOGV("Class name = %s, args = %s", className.c_str(), restOfArgs.c_str());
}
} else {
// We're in zygote mode.
maybeCreateDalvikCache();
if (startSystemServer) {
args.add(String8("start-system-server"));
}
char prop[PROP_VALUE_MAX];
if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {
LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",
ABI_LIST_PROPERTY);
return 11;
}
String8 abiFlag("--abi-list=");
abiFlag.append(prop);
args.add(abiFlag);
// In zygote mode, pass all remaining arguments to the zygote
// main() method.
for (; i < argc; ++i) {
args.add(String8(argv[i]));
}
}
if (!niceName.empty()) {
runtime.setArgv0(niceName.c_str(), true /* setProcName */);
}
2.6 进程的启动
2.6.1 启动的入口
在main方法里的最后会对zygote和非zygote做不同的启动 并把刚才的参数扔进去
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (!className.empty()) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
}
2.6.2 AndroidRuntime的start方法
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
***
onVmCreated(env);
***
onVmCreated(env);
***
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;
stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL);
strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
assert(strArray != NULL);
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr);
for (size_t i = 0; i < options.size(); ++i) {
jstring optionsStr = env->NewStringUTF(options.itemAt(i).c_str());
assert(optionsStr != NULL);
env->SetObjectArrayElement(strArray, i + 1, optionsStr);
}
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
char* slashClassName = toSlashClassName(className != NULL ? className : "");
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
}
}
free(slashClassName);
}
这里主要做了两件事
2.6.2.1 AppRuntime onVmCreated
这里会调用onVmCreated,父类这里是空实现,会走到AppRuntime的onVmCreated。
virtual void onVmCreated(JNIEnv* env)
{
if (mClassName.empty()) {
return; // Zygote. Nothing to do here.
}
/*
* This is a little awkward because the JNI FindClass call uses the
* class loader associated with the native method we're executing in.
* If called in onStarted (from RuntimeInit.finishInit because we're
* launching "am", for example), FindClass would see that we're calling
* from a boot class' native method, and so wouldn't look for the class
* we're trying to look up in CLASSPATH. Unfortunately it needs to,
* because the "am" classes are not boot classes.
*
* The easiest fix is to call FindClass here, early on before we start
* executing boot class Java code and thereby deny ourselves access to
* non-boot classes.
*/
char* slashClassName = toSlashClassName(mClassName.c_str());
mClass = env->FindClass(slashClassName);
if (mClass == NULL) {
ALOGE("ERROR: could not find class '%s'\n", mClassName.c_str());
}
free(slashClassName);
mClass = reinterpret_cast<jclass>(env->NewGlobalRef(mClass));
}
这里主要就是通过JNIEnv的指针拿到对应的进程入口字节码对象,并赋值给AppRuntime的成员mClass。
最后在启动的时候要用到该成员
2.6.3 RuntimeInit的处理
入口会调用AndroidRuntime的start方法 启动RuntimeInit. 下面是RuntimeInit的main方法
public static final void main(String[] argv) {
preForkInit();
if (argv.length == 2 && argv[1].equals("application")) {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application");
redirectLogStreams();
} else {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting tool");
}
commonInit();
/*
* Now that we're running in interpreted code, call back into native code
* to run the system.
*/
nativeFinishInit();
if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
}
preForkInit主要做了启动前的准备工作
如果这里是你的进程并未传入--application 所以不会走redirectLogStreams的逻辑,实际redirectLogStreams也只是做了一些日志相关的处理。
这时候会调用到commonInit,然后jni调用nativeFinishInit
下面是commonInit方法
protected static final void commonInit() {
if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");
/*
* set handlers; these apply to all threads in the VM. Apps can replace
* the default handler, but not the pre handler.
*/
LoggingHandler loggingHandler = new LoggingHandler();
RuntimeHooks.setUncaughtExceptionPreHandler(loggingHandler);
Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));
/*
* Install a time zone supplier that uses the Android persistent time zone system property.
*/
RuntimeHooks.setTimeZoneIdSupplier(() -> SystemProperties.get("persist.sys.timezone"));
/*
* Sets handler for java.util.logging to use Android log facilities.
* The odd "new instance-and-then-throw-away" is a mirror of how
* the "java.util.logging.config.class" system property works. We
* can't use the system property here since the logger has almost
* certainly already been initialized.
*/
LogManager.getLogManager().reset();
new AndroidConfig();
/*
* Sets the default HTTP User-Agent used by HttpURLConnection.
*/
String userAgent = getDefaultUserAgent();
System.setProperty("http.agent", userAgent);
/*
* Wire socket tagging to traffic stats.
*/
TrafficStats.attachSocketTagger();
initialized = true;
}
做了异常处理,AndroidConfig(其实还是log相关)等等。
2.6.4 回归AndroidRuntime
nativeFinishInit会jni调用到AndroidRuntime的方法
tatic void com_android_internal_os_RuntimeInit_nativeFinishInit(JNIEnv* env, jobject clazz)
{
gCurRuntime->onStarted();
}
可以回看2.1.3的最后标粗的代码,这里的gCurRuntime就是this,也就是在main方法里声明的AppRuntime的类对象
2.6.5 AppRuntime onStart
所以来看一下AppRuntime重写的onStart方法
virtual void onStarted()
{
sp<ProcessState> proc = ProcessState::self();
ALOGV("App process: starting thread pool.\n");
proc->startThreadPool();
AndroidRuntime* ar = AndroidRuntime::getRuntime();
ar->callMain(mClassName, mClass, mArgs);
IPCThreadState::self()->stopProcess();
hardware::IPCThreadState::self()->stopProcess();
}
2.6.5.1 startThreadPool
这里主要是调用了ProcessState的方法,这里主要是创建了一个binder线程。详细的代码在frameworks/native/libs/binder/ProcessState.cpp
void ProcessState::startThreadPool()
{
std::unique_lock<std::mutex> _l(mLock);
if (!mThreadPoolStarted) {
if (mMaxThreads == 0) {
// see also getThreadPoolMaxTotalThreadCount
ALOGW("Extra binder thread started, but 0 threads requested. Do not use "
"*startThreadPool when zero threads are requested.");
}
mThreadPoolStarted = true;
spawnPooledThread(true);
}
}
2.6.5.2 真正的进程启动
这里接着调用到了父类AndroidRuntime的callMain方法,传入onVmCreated里获取的进程入口字节码对象
status_t AndroidRuntime::callMain(const String8& className, jclass clazz,
const Vector<String8>& args)
{
JNIEnv* env;
jmethodID methodId;
ALOGD("Calling main entry %s", className.c_str());
env = getJNIEnv();
if (clazz == NULL || env == NULL) {
return UNKNOWN_ERROR;
}
methodId = env->GetStaticMethodID(clazz, "main", "([Ljava/lang/String;)V");
if (methodId == NULL) {
ALOGE("ERROR: could not find method %s.main(String[])\n", className.c_str());
return UNKNOWN_ERROR;
}
/*
* We want to call main() with a String array with our arguments in it.
* Create an array and populate it.
*/
jclass stringClass;
jobjectArray strArray;
const size_t numArgs = args.size();
stringClass = env->FindClass("java/lang/String");
strArray = env->NewObjectArray(numArgs, stringClass, NULL);
for (size_t i = 0; i < numArgs; i++) {
jstring argStr = env->NewStringUTF(args[i].c_str());
env->SetObjectArrayElement(strArray, i, argStr);
}
env->CallStaticVoidMethod(clazz, methodId, strArray);
return NO_ERROR;
}
这里就是真正启动进程的地方,也很简单,顺序就是先获取JNIEnv的指针,然后根据在vmCreated里赋值的mClass成员找到对应进程的main方法,传入参数,启动main方法。
至此,进程就被真正的调用起来了。
如有疏漏欢迎指正,后续会跟进并补出app_process启动zygote的流程
2223

被折叠的 条评论
为什么被折叠?



