Android 5.0/6.0进程自动重启调研(5.0+)
2015-11-06 11:13
337 查看
前言
众所周知,在Android 5.0+,系统安全有了比较大的提升。守护进程也不像4.4之前那么容易了。比如通过fork等创建的子进程也会同父进程一样被Kill.
那么到底在5.0+上发生了什么变化呢?带着好奇,决定走进去看一看究竟....
初探
查看ActivityManagerService.java源码,发现在M上kill进程时多出了一句Process.killProcessGroup(app.info.uid,
pid);
也就是这一句,保证了在杀死APP的同时,也结束掉它fork/exec创建出来的所有子进程。
那么事实真的是这样吗?
Demo1: fork一个子进程
示列代码如下:
ps打印出的进程:
可以看到有两个进程,17011便是在JNI中通过fork创建出的子进程。
在5.0+上只要kill 16992,17011也会被kill.而在4.4上,则不会。
Demo2: fork出的子进程再fork子进程
这么做的目的是为了测试子进程被init进程所领养后的情况是否也如此。
示列代码如下:
ps打印出的进程:
第二个进程的父进程变为了1,也就是init进程。
测试发现若kill 18602,18650也会一同被kill。
这究竟是怎么回事呢?
原来这是因为在5.0+上开启了Control Group来管理进程.
它会把进程以及它的所有子进程都绑到了一个组里面管理,这样就能做到将所有子进程都杀了。
关于cgroup的参考资料:
http://lxr.free-electrons.com/source/Documentation/cgroups/cgroups.txt
http://www.linux-kongress.org/2010/slides/seyfried-cgroups-linux-kongress-2010-presentation.pdf
具体到android中,相关的代码文件:
对代码进行分析:
在AMS中杀App时,会调用到processgroup.cpp的killProcessGroup函数,看下该函数会做什么:
可以看到在killProcessGroup中只是循环调用killProcessGroupOnce,再看看该函数又是做什么的:
它通过getOneAppProcess循环获取子进程id,再kill掉。
进入getOneAppProcess查看,最终发现子进程是从下面这个函数组成的文件名中读取到的:
所以上面的函数组成的文件名是这样的:
再通过adb进入手机看看这个目录:
看到jni中fork创建出的子进程18650也确实被记录在了该文件中,这是cgroup在fork,exec等的回调中完成的。
此时也就解释了为什么kill APP时,所有fork出的子进程也会被kill.
总结:
这样要在5.0上做自己的卸载提示等,看样子就不可能了。
fork出的进程生命周期与App生命周期是绑定在一起的。
重启的解决方案:
通过研究几款App,能够在被kill后还能重启的方案可能就只有利用service或者广播了。
查看工商银行的客户端,重启的机制是在Service的onDestory()函数中重新启动自己,demo code如下:
虽然这种方式不是特别的靠谱,但在5.0+上,可能这就是目前能够做的了吧。
众所周知,在Android 5.0+,系统安全有了比较大的提升。守护进程也不像4.4之前那么容易了。比如通过fork等创建的子进程也会同父进程一样被Kill.
那么到底在5.0+上发生了什么变化呢?带着好奇,决定走进去看一看究竟....
初探
查看ActivityManagerService.java源码,发现在M上kill进程时多出了一句Process.killProcessGroup(app.info.uid,
pid);
也就是这一句,保证了在杀死APP的同时,也结束掉它fork/exec创建出来的所有子进程。
final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread) { .............. Process.killProcessQuiet(pid); Process.killProcessGroup(app.info.uid, pid); app.killed = true;
那么事实真的是这样吗?
Demo1: fork一个子进程
示列代码如下:
pid_t pid = fork(); if(pid < 0){ return; }else if(pid > 0){ return; }else{ }
ps打印出的进程:
USER PID PPID VSIZE RSS WCHAN PC NAME u0_a64 16992 317 1536924 52588 ffffffff 00000000 S com.example.testndk2 u0_a64 17011 16992 1504092 34508 ffffffff 00000000 S com.example.testndk2
可以看到有两个进程,17011便是在JNI中通过fork创建出的子进程。
在5.0+上只要kill 16992,17011也会被kill.而在4.4上,则不会。
Demo2: fork出的子进程再fork子进程
这么做的目的是为了测试子进程被init进程所领养后的情况是否也如此。
示列代码如下:
pid_t pid = fork(); if(pid < 0){ return; }else if(pid > 0){ return; }else{ }
pid = fork();
if(pid < 0){
return;
}else if(pid > 0){
return;
}else{
}
ps打印出的进程:
USER PID PPID VSIZE RSS WCHAN PC NAME u0_a64 18602 317 1538796 53848 ffffffff 00000000 S com.example.testndk2 u0_a64 18650 1 1504092 34508 ffffffff 00000000 S com.example.testndk2
第二个进程的父进程变为了1,也就是init进程。
测试发现若kill 18602,18650也会一同被kill。
这究竟是怎么回事呢?
原来这是因为在5.0+上开启了Control Group来管理进程.
它会把进程以及它的所有子进程都绑到了一个组里面管理,这样就能做到将所有子进程都杀了。
关于cgroup的参考资料:
http://lxr.free-electrons.com/source/Documentation/cgroups/cgroups.txt
http://www.linux-kongress.org/2010/slides/seyfried-cgroups-linux-kongress-2010-presentation.pdf
具体到android中,相关的代码文件:
system/core/libprocessgroup/processgroup.cpp
对代码进行分析:
在AMS中杀App时,会调用到processgroup.cpp的killProcessGroup函数,看下该函数会做什么:
int killProcessGroup(uid_t uid, int initialPid, int signal) { .......... while ((processes = killProcessGroupOnce(uid, initialPid, signal)) > 0) { if (retry > 0) { usleep(sleep_us); --retry; } else { break; } } .......... }
可以看到在killProcessGroup中只是循环调用killProcessGroupOnce,再看看该函数又是做什么的:
static int killProcessGroupOnce(uid_t uid, int initialPid, int signal) { while ((pid = getOneAppProcess(uid, initialPid, &ctx) >= 0) { processes++; ...... int ret = kill(pid, signal); if (ret == -1) { SLOGW("failed to kill pid %d: %s", pid, strerror(errno)); } }
它通过getOneAppProcess循环获取子进程id,再kill掉。
进入getOneAppProcess查看,最终发现子进程是从下面这个函数组成的文件名中读取到的:
static int convertUidPidToPath(char *path, size_t size, uid_t uid, int pid) { return snprintf(path, size, "%s/%s%d/%s%d", PROCESSGROUP_CGROUP_PATH, PROCESSGROUP_UID_PREFIX, uid, PROCESSGROUP_PID_PREFIX, pid); }上面几个常量的定义如下:
#define PROCESSGROUP_CGROUP_PATH "/acct" #define PROCESSGROUP_UID_PREFIX "uid_" #define PROCESSGROUP_PID_PREFIX "pid_" #define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs"
所以上面的函数组成的文件名是这样的:
/acct/uid_%d/pid_%d/cgroup.procs
再通过adb进入手机看看这个目录:
root@cancro:/acct/uid_10064 # cd pid_18602/ root@cancro:/acct/uid_10064/pid_18602 # ls cgroup.clone_children cgroup.event_control cgroup.procs cpuacct.stat cpuacct.usage cpuacct.usage_percpu notify_on_release tasks root@cancro:/acct/uid_10064/pid_18602 # cat cgroup.procs 18602 18650
看到jni中fork创建出的子进程18650也确实被记录在了该文件中,这是cgroup在fork,exec等的回调中完成的。
此时也就解释了为什么kill APP时,所有fork出的子进程也会被kill.
总结:
这样要在5.0上做自己的卸载提示等,看样子就不可能了。
fork出的进程生命周期与App生命周期是绑定在一起的。
重启的解决方案:
通过研究几款App,能够在被kill后还能重启的方案可能就只有利用service或者广播了。
查看工商银行的客户端,重启的机制是在Service的onDestory()函数中重新启动自己,demo code如下:
虽然这种方式不是特别的靠谱,但在5.0+上,可能这就是目前能够做的了吧。
public class MyService1 extends Service { private MyReceiver1 a; private MyReceiver2 b; public IBinder onBind(Intent paramIntent) { return null; } public void onDestroy() { startService(new Intent(this, MyService1.class)); super.onDestroy(); } }
相关文章推荐
- android:visibility的三个属性区别,invisible,gone,visible
- 动态设置android:drawableLeft|Right|Top|Bottom
- android代码截屏截切图片并且保存到指定文件夹中
- android 代码设置 color的selector
- Android布局属性大全详解
- 倍数提高工作效率的 Android Studio 奇技
- android四大组件之ContentProvide
- Android 无法接收开机广播的问题
- Android 代码混淆和破解apk
- Android studio使用小技巧之 快速生成onClick()点击方法
- 电脑 监控 android手机数据
- Android NDK 开发教程
- Android Fragment简介
- Android学习笔记--Menu菜单的使用
- android定时器
- Android系列教程之EditText使用详解-包含很多教程上看不到的功能演示
- android onInterceptTouchEvent和onTouchEvent的执行关系
- Android开机无法自启动service
- 自定义ImageView系列 - 区域截图(下)
- 关于android 字间距的问题