Android 5.0/6.0进程自动重启调研(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();
    }
}



展开阅读全文

没有更多推荐了,返回首页