您的位置:首页 > 其它

从Activity的启动细窥BinderIPC(2)

2015-09-17 03:44 274 查看

上次的源码分析出现了一个runOnce()函数,实际的孵化过程其实是在runOnce()里面,本文章我们再来看看这个函数的源代码



boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
//...
try {
//就是这个神奇的函数封装了孵化的过程,成功后就获得新进程的pid了
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName);
}
//...
try {
//这里就是一些异常的处理,可以直接对异常逃逸到main()函数
if (pid == 0) {
// in child
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
//handleChildProc是一个很重要的函数,在该函数里使用了异常进行逃逸
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
//...
} else {
//...
}
} finally {
//...
}
}


至于forkAndSpecialize()方法,它实质上调用的一个本地方法nativeForkAndSpecialize(),这个函数实现用C++实现,可以对内核进行调用,在C++代码中,有个叫Dalvik_dalvik_system_Zygote_forkAndSpecialize()的函数,这个函数再调用forkAndSpecializeCommon()函数,然后就直接调用内核的fork系统调用创建一个新进程了。

还是来看看第一个函数调用的代码

说明:u4是一个指向从java层传过来的参数的指针,已经被dvm转换为运行在虚拟机的c++对象了,垃圾回收以及内存管理都按照dvm舒服的处理方式。

JValue* pResult是c++函数调用的结果。

static void Dalvik_dalvik_system_Zygote_forkAndSpecialize(const u4* args,
JValue* pResult)
{
pid_t pid;
//还是调用这个函数
pid = forkAndSpecializeCommon(args, false);
RETURN_INT(pid);
}


至于forkAndSpecializeCommon()函数,它的功能主要就是拿到fork的系统调用了,代码在dalvik_system_Zygote.c文件中

看看这个函数的实现代码:

static pid_t forkAndSpecializeCommon(const u4* args, bool isSystemServer)
{
//uid,gid来自java层的参数parsedArgs.uid, parsedArgs.gid,都是从
//ActivityManagerService发送过来的请求
//java层传过来的参数都会根据在参数列表中顺序保存在u4指向的这个数组
pid_t pid;
uid_t uid = (uid_t) args[0];
gid_t gid = (gid_t) args[1];
ArrayObject* gids = (ArrayObject *)args[2];
u4 debugFlags = args[3];
ArrayObject *rlimits = (ArrayObject *)args[4];
int64_t permittedCapabilities, effectiveCapabilities;
if (isSystemServer) {
/*
* Don't use GET_ARG_LONG here for now.  gcc is generating code
* that uses register d8 as a temporary, and that's coming out
* scrambled in the child process.  b/3138621
*/
//permittedCapabilities = GET_ARG_LONG(args, 5);
//effectiveCapabilities = GET_ARG_LONG(args, 7);
permittedCapabilities = args[5] | (int64_t) args[6] << 32;
effectiveCapabilities = args[7] | (int64_t) args[8] << 32;
} else {
permittedCapabilities = effectiveCapabilities = 0;
}
//在进程被创建出来后,gDvm.zygote会设为false,表示不是zygote进程,只有一个原来的版本啊,不能迭代fork
//当然子进程中为false,在父进程即zygote进程gDvm.zygote还是保持true,还得继续fork给其他的app使用
if (!gDvm.zygote) {
......
return -1;
}
//......这里就进行了fork的系统调用啦
pid = fork();
//下面就是一些异常处理
if (pid == 0) {
int err;
err = setgroupsIntarray(gids);
err = setrlimitsFromArray(rlimits);
err = setgid(gid);
err = setuid(uid);
//......
err = setCapabilities(permittedCapabilities, effectiveCapabilities);
//......
enableDebugFeatures(debugFlags);
//......
gDvm.zygote = false;
if (!dvmInitAfterZygote()) {
//......
dvmAbort();
}
} else if (pid > 0) {
/* the parent process */
}
//最后返回的是fork进程的pid
return pid;
}


好咯,现在app的一个进程已经产生,实质为一个dalvik进程,继续看callstack(就是android的调用栈),可以发现在这个进程上调用了activity Thread的main方法,这是一个native的调用,不要忘了还要告诉ActivityManagerService一声app的进程已经建立,你就不用操心啦。同时ActivityManagerService会保留一个activity的代理对象(proxy),它的本质就是一个智能指针,里面封装了app在dalvik进程实例的内存数据(说到代理,就多说两句,binderIPC机制的server跟client间通信也是通过代理来成事,不过是同时实现manager的一个接口获取IBinder,即binder的一个智能指针,再通过binder封装远程调用的函数的函数参数以及返回值信息,顺便实现onTransact()的回调,kernel层也是通过binder driver通过内核空间的内存共享实现,这就是经典的RPC的套路),ActivityManagerService这下就可以控制我们的app啦,包括我们的task,backstack之类的(这些简单的东西就不多说了),下面就是创造一个activity来进入app咯,也是ActivityManagerService来完成,按activity的生命周期一次执行oncreate(),onStart()等方法,当然中间免不了建立main Thraed,并通过looper.mylooper()方法建立主线程的消息循环,接受消息加载activity的类,构造activity的rootViewGroup等消息。就是在栈中看到的关于looper,handler之类的调用咯,所以主线程就不劳烦我们自己创建looper啦。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ipc-binde