将_NSIG/8改动为_NSIG/4修改的原因为在bionic中定义NSIG=32,而内核为64,造成kernel/signal.c中rt_sigaction调用认为参数错误而返回错误。在android系统中对应非prelink的动态库调用dlopen(NULL, RTLD_LAZY);(即查找自己)会异常,需要屏蔽cr_libinit.c函数cri_init对dlopen的调用。
在blcr工程中加入Android.mk,需要注意的是下面几个文件需要编译为arm而非缺省的thumb指令:cr_async.c. cr_core.c cr_sig_sync.c cr_cs.c cr_syscall.c,否则编译无法通过。
三 zygote加入checkpoint 支持 当系统启动zygote服务时候先判断是否存在checkpoint文件,如果有则调用cr_restart载入保存的checkpoint文件,否则按照正常的zygote流程进行。由于zygote是其他android的父进程,其生成的许多进程/线程都有socket等checkpoint无法恢复的限制因素,故不适合将checkpoint放到zygote启动过后点,本文选择在ZygoteInit.java的main后面的preloadResources() 完成后进行。这个过程刚好加载完毕耗时长的公用类而且基本没有使用很多限制资源。原生的android在preload前有创建socket动作,可以调整到preloadResources后面,调整后的样子如下(斜体代码为调整顺序部分):
CheckPoint cp=new CheckPoint();
try {
// Start profiling the zygote initialization.
SamplingProfilerIntegration.start();
preloadClasses();
preloadResources();
cp.checkPoint("/data/zygote.blcr");
registerZygoteSocket();
其中CheckPoint 类是c扩展的java调用接口,通过jni调用checkpoint库函数实现checkpoint动作,下文列出了jni部分内容。该部分仅为一个参考模板,产品化过程应该做更多异常处理和合理化调整工作。
android_blcr_checkpoint.cpp
#define LOG_TAG "BLCR"
namespace android {
static int my_callback(void* arg)
{
int rc;
LOGV(__FUNCTION__);
rc = cr_checkpoint(0);
return 0;
}
static void checkPoint(JNIEnv* env, jobject object,jstring file) {
LOGV(__FUNCTION__);
pid_t my_pid;
int rc,fd;
struct stat s;
cr_checkpoint_handle_t my_handle;
cr_callback_id_t cb_id;
FILE *f;
const jchar* str = env->GetStringCritical(file, 0);
String8 filename;
if (str) {
filename = String8(str, env->GetStringLength(file));
env->ReleaseStringCritical(file, str);
}
else {
LOGE("checkPoint,file name is null");
return;
}
LOGI("checkPoint,filename=%s",filename);
//does the file exist?
f = fopen(filename, "r");
if(f==NULL) //create and save checkpoint to it.
{
my_pid = cr_init();
if(my_pid<0)
{
LOGE("cr_init failed,return:%d",my_pid);
return;
}
cb_id = cr_register_callback(my_callback, NULL, CR_SIGNAL_CONTEXT);
if (cb_id < 0)
{
LOGV("cr_register_callback() unexpectedly returned %d\n", cb_id);
return;
}
else
{
LOGV("cr_register_callback() correctly returned %d\n", cb_id);
}
/* Request a checkpoint of ourself */
fd = crut_checkpoint_request(&my_handle, filename);
if (fd < 0)
{
LOGE("crut_checkpoint_request() unexpectedly returned 0x%x\n", fd);
return ;
}
rc = stat(filename, &s);
if (rc) {
LOGE("stat() unexpectedly returned %d\n", rc);
return;
} else {
LOGV("stat(context.%d) correctly returned 0\n", my_pid);
}
if (s.st_size == 0) {
LOGE("context file unexpectedly empty\n");
return;
} else {
LOGV("context.%d is non-empty\n", my_pid);
}
/* Reap the checkpoint request */
rc = crut_checkpoint_wait(&my_handle, fd);
if (rc < 0) {
LOGE("crut_checkpoint_wait() #1 unexpectedly returned 0x%x\n", rc);
return;
} }}
/*
* JNI registration.
*/
static JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{"checkPoint", "(Ljava/lang/String;)V", (void *)checkPoint},
};
int register_android_blcr_checkpoint(JNIEnv* env)
{
return jniRegisterNativeMethods(env, "android/blcr/CheckPoint",
gMethods, NELEM(gMethods));
}
}
为了正常使用上面的jni接口,需要把注册函数注册到frameworks/base/core/jni/ AndroidRuntime.cpp,在gRegJNI[]中加入REG_JNI(register_android_blcr_checkpoint)。同时创建CheckPoint.java 文件提供给java调用:
package android.blcr;
public class CheckPoint {
public CheckPoint() {
}
public native void checkPoint(String fileName);
}
正常启动blcr还需要修改init.rc,调整启动zygote的方式。修改点包括载人blcr内核驱动:
insmod /system/lib/modules/blcr_imports.ko
insmod /system/lib/modules/blcr.ko
和启动blcr服务方式,将
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
替换为:service zygote /system/bin/quick.sh -Xzygote /system/bin --zygote --start-system-server
其中quick.sh脚本内容为:
#!/system/bin/busybox sh
ec()
{
/system/bin/busybox echo $* >/dev/console;
}
if [ -f /data/zygote.blcr ]; then
ec "***************load saved zygote********************"
/system/bin/cr_restart -f /data/zygote.blcr ;
else
ec "optimised by blcr"
ec "**************start a new zygote******************"
ec "you can contact the author through Email:shuaiwen@yahoo.com.cn"
ec "this is only an original demon version"
ec "release pid:$$."
/system/bin/cr_checkpoint
ec "release pid:$$."
/system/bin/cr_checkpoint
ec "release pid:$$."
/system/bin/app_process $*
fi
需要指出的是运行上面几个不带参数的cr_checkpoint目的为将后面的zygote的pid退后一些,以防下次checkpoint时候遇到pid冲突而导致checkpoint失败。