再学安卓 - SystemServer

前言

在正式开始之前,让我们来尝试理解一下,操作系统复制一个进程(fork)到底是一个什么样的操作,对于程序的执行流程会产生什么影响。先看一下这个简单的代码

c 复制代码
#include <unistd.h>
#include <stdio.h> 
int main(int argc, char* const argv[]) { 
	pid_t pid;
	pid = fork();
	if (pid == 0) {
		printf("I'm child, my id is %d\n", getpid());
		printf("Now in child process\n");
	} else if(pid > 0) {
		printf("I'm parent, my id is %d\n", getpid());
		printf("Now in parent process\n");
	} else {
		printf("error occurred!");
	}

	return 0;
}

我们在Ubuntu下面编译一下以上代码并执行

sh 复制代码
// 编译
gcc fork_test.c -o bin_fork_test

// 执行
./bin_fork_test

// 输出结果(多执行几次,输出结果的顺序可能会不同,这取决于系统的进程调度)
I'm parent, my id is 4567
I'm child, my id is 4568
Now in parent process
Now in child process

乍一看,if-else语句中的两个分支都被执行了,这怎么可能?物理学不存在了?

其实不然,当我们执行fork函数时,内核会将父进程的执行环境几乎完全一样的拷贝一份存放在新的空间中。也就是说fork函数之后,子进程就已经ReadyToGo,等待系统调度。

对于上面这个简单程序来讲,系统中就有两个进程且都执行到第5行完的地方。接下来,第6行,在子进程中pid变量将为0,在父进程中pid如果大于0,则创建成功且为子进程的ID,小于0则表示创建失败。这就是我们前面提到的程序流程"分叉"了。当然,父进程和子进程谁先执行第6行,这就取决于系统先调度谁。

有了这一点fork基础,我们可以总结:一旦代码中出现判断pid是否等于0,大于0时候,很有可能是上一行直接或间接的有fork操作,if else 分支都可能会执行,且顺序随机,后面的逻辑值得加倍注意。

forkSystemServer()

上一篇中我们讲到Zygote流程到forkSystemServer函数就开始分叉了,Zygote和SystemServer各走各的,在新建进程时通过Socket通信。那么本篇我们就从这个函数内部开始:

java 复制代码
// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
private static Runnable forkSystemServer(String abiList, String socketName,
										ZygoteServer zygoteServer) {
	... ...
	// ① 执行fork
	pid = Zygote.forkSystemServer(  
        parsedArgs.mUid, parsedArgs.mGid,  
        parsedArgs.mGids,  
        parsedArgs.mRuntimeFlags,  
        null,  
        parsedArgs.mPermittedCapabilities,  
        parsedArgs.mEffectiveCapabilities);
    ... ...
    // ② 从下面这一行开始"分叉"
    if (pid == 0) {
	    ... ...
	    return handleSystemServerProcess(parsedArgs);
    }
    return null;
}

① 这一行最终会调用系统函数fork(),调用链如下:Zygote.forkSystemServer -> Zygote.nativeForkSystemServer -> zygote::ForkCommon -> fork()。调用路径上会出现一些hook函数,比如:preFork、postFork之类的,还有一些关键的日志输出,可以浏览一下,这是以后排查问题的基础,混个脸熟,对于快速定位问题很有帮助。

② 熟悉的pid判断条件,那么Zygote返回null,SystemServer继续handle something。

看看handleSystemServerProcess的返回值,这个Runnable里面到底包裹了什么?

java 复制代码
// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {
	... ...
	// 创建并设置类加载器
	ClassLoader cl = getOrCreateSystemServerClassLoader();  
	if (cl != null) {  
	    Thread.currentThread().setContextClassLoader(cl);  
	}

	return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,  
	        parsedArgs.mDisabledCompatChanges,  
	        parsedArgs.mRemainingArgs, cl);
}
java 复制代码
// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
// 强烈注意:此函数在创建普通APP应用时也会调用。为什么知道,因为我加了日志观察到的
public static Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,  
        String[] argv, ClassLoader classLoader) {
	... ...
	// 设置启动线程池
	ZygoteInit.nativeZygoteInit();
	return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv, classLoader);
}
java 复制代码
// frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,  
        String[] argv, ClassLoader classLoader) {
    ... ...
    return findStaticMain(args.startClass, args.startArgs, classLoader);
}
java 复制代码
// frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
protected static Runnable findStaticMain(String className, String[] argv,  
        ClassLoader classLoader) {
	// 熟悉的味道,通过反射,找到className类main函数,以此为参数创建了MethodAndArgsCaller
	cl = Class.forName(className, true, classLoader);
	... ...
	m = cl.getMethod("main", new Class[] { String[].class });
	... ...
	return new MethodAndArgsCaller(m, argv);
}
java 复制代码
// frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
static class MethodAndArgsCaller implements Runnable {
	public void run() {
		... ...
		mMethod.invoke(null, new Object[] { mArgs });
		... ...
	}
}

最终,runnable里面包裹的是一句反射调用,调用的是指定类的main函数,就这么简单,感觉追踪了个寂寞😂。

那么我们可以猜测,对于SystemServer进程来讲,className肯定是SystemServer类,调用它的main方法把SystemServer的逻辑run起来,因此,再大胆一点,这个main方法里面必定有个"死循环"。

下面一步我们该看SystemServer的main函数了,那么它在哪里?SystemServer的完整ClassName是什么?我们严谨一点,打个日志看看。

java 复制代码
// frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,  
        String[] argv, ClassLoader classLoader) {
    ... ...
    Log.v(TAG, "args.startClass: " + args.startClass);  
	for (int i=0; i<args.startArgs.length;i++ ) {  
	    Log.v(TAG, "args.startArgs[" + i + "]: " + args.startArgs[i]);  
	}
    return findStaticMain(args.startClass, args.startArgs, classLoader);
}

结果如下:

cmd 复制代码
11-29 15:30:08.342   578   578 V AndroidRuntime: args.startClass: com.android.server.SystemServer
11-29 15:30:14.482   797   797 V AndroidRuntime: args.startClass: android.app.ActivityThread
11-29 15:30:14.482   797   797 V AndroidRuntime: args.startArgs[0]: seq=1
11-29 15:30:14.662   835   835 V AndroidRuntime: args.startClass: android.app.ActivityThread
11-29 15:30:14.662   835   835 V AndroidRuntime: args.startArgs[0]: seq=2
11-29 15:30:15.053   930   930 V AndroidRuntime: args.startClass: android.app.ActivityThread
11-29 15:30:15.053   930   930 V AndroidRuntime: args.startArgs[0]: seq=3
... ...

com.android.server.SystemServer这是我们想要的结果,同时,还有新发现,android.app.ActivityThread这太熟悉了,又一个入口类,大名鼎鼎的ActivityThread,我们的普通APP都是从这个类开始的。日志说明,从系统的角度看,SystemServer和普通APP进程的启动差别并不大,SystemServer仅仅是一个更为特殊的普通进程而已。

SystemServer.run()

java 复制代码
public final class SystemServer implements Dumpable {
	... ...
	public static void main(String[] args) {  
	    new SystemServer().run();  
	}

	private void run() {
		... ...
		// 启动基础服务,其中包括ActivityManagerService、PackageManagerService等
		startBootstrapServices(t);
		// 启动核心服务,其中包括NativeTombstoneManagerService等
		startCoreServices(t);
		// 启动InputManagerService、WindowManagerService等
		startOtherServices(t);
		startApexServices(t);
		... ...
		// 进入loop循环
		Looper.loop();
	}
}

刚才我们的大胆猜测得到了印证,loop()函数会启动一个"死循环",等待处理消息。作为应用开发工程师,Looper大家应该都不陌生了,又是一个值得单独讲一期的角色😂。

总结

总体来说,SystemServer的启动流程不算复杂,但它承担的任务很繁重,启动了一大堆的Service,还要负责跟Zygote沟通,最终通过我们喜闻乐见的Looper方式循环处理消息。好了,我们现在可以将上一篇结尾的概括图再扩充一下。

相关推荐
chlk12316 小时前
Linux文件权限完全图解:读懂 ls -l 和 chmod 755 背后的秘密
linux·操作系统
阿巴斯甜16 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker17 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952718 小时前
Andorid Google 登录接入文档
android
黄林晴19 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android