作者 [email protected]
2 进程与线程
2.1 Dalvik虚拟机的进程
在Dalvik新进程创建的最关键一步是使用linux的Fork机制从zygote母体Fork出一个新的进程来。到了这里有如下值得关注的地方:
由于是linux的Fork机制,新进程复制Zygote的可共享虚拟地址空间的页表页目录。而不可共享区域由linux自身的COW机制在写时机创建
Zygote已经进行大量的初始化,加载了大量的常用类库,大量的二进制链接库,都被新进程继承下来。
新进程如果有什么异常状况,可以轻易被kernel杀掉,只要systemserver跟zygote没有问题,系统仍然可以健康的运行。
在新进程运行应用代码之前构建Android进程安全框架。除了传统的通过用户ID、组ID将进程文件访问限制在自己局部区域,通过Capabilities能力位更加细分的控制对内核访问以外。
Zygote Fork新进程的实现如下:
static pid_t forkAndSpecializeCommon(const u4* args, bool isSystemServer)
{
pid_t pid;
//新进程的UID和GID,这是在由PackagemangeService传送过的。
uid_t uid = (uid_t) args[0];
...
//调用linux系统调用Fork
pid = fork();
//完成Fork动作,新老进程都从这里返回
if (pid == 0) {
//进程的返回的pid为零
/*至此新进程的实体被创建完毕,但是这离Android进程还有几步要走。首先这是由Zygote Fork出的进程,他具有与Zygote相同的权限,这是不被允许的,不然Android上的恶意病毒就可以为所欲为了。而新进程到了这里还没有加载APK的.so和dex,所以新进程在这里自废武功限制自己以后的所为,等到加载了恶意的APK也不怕伤及筋骨了。*/
/*新版Android的安全性更进一步,支持基于Selinux的面向企业应用的更高级别的安全性,但是本节摘自较早笔记,其版本没有涉及到Selinux的适配。由于时间关系,针对SEAndroid/SElinux的分析不在本书设计的范围之内。*/
#ifdef HAVE_ANDROID_OS
…
/* keep caps across UID change, unless we're staying root */
if (uid != 0) {
err = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
…
#endif /* HAVE_ANDROID_OS */
//重置用户组
err = setgroupsIntarray(gids);
…
//设置资源限制
err = setrlimitsFromArray(rlimits);
…
//重置GID
err = setgid(gid);
…
//重置UID
err = setuid(uid);
…
//Capabilities位的设置
err = setCapabilities(permittedCapabilities, effectiveCapabilities);
…
//设置DVM控制结构的线程号
Thread* thread = dvmThreadSelf();
thread->systemTid = dvmGetSysThreadId();
…
} else if (pid > 0) {
/* the parent process */
}
return pid;
}
再往后,父子进程都退到zygote中,但是父进程继续等待下一个新的DVM进程创建申请,子进程在清空原有的JAVA栈之后开始了新生。