Android——NDK双进程守护

Android——NDK双进程守护

先皮一下,没办法接下来很枯燥。

说到双进程守护,大家第一点想到的肯定是Android里面的双进程守护,然而我们今天不讲这个,因为有弊端。大家有没有,kill app后servece都被kill了,你让他们谁拉谁?所以说我们要看的更深一些,大家有没有发现一个很奇怪的东西,就是Android设备你可以在进程看到很多系统servece,你们就不好奇为啥这个系统servece就一直能运行吗?我们今天就讲这个,c层的双进程守护,涉及到好几个知识点,主要是linux中多进程,unix domain套接字实现跨进程通信,linux的信号处理以及exec函数族的用法(当然不是所有的都讲,之后慢慢讲,因为怕刚学的人不懂。)。看到套接字有没有很熟悉,java三次握手四次挥手。这些内容是不是很熟悉。可是我们说了,这是基于C++的,所以希望看完这个大家都有所学,学以致用,而不是看完就丢了,你的工作会有很多这样的需求(PS:我也不想这么干啊!国内的google服务全kill了,GM部门新成立的推送联盟感觉没啥用,涉及到各家手机厂商大佬们的利益,他也没法协调。所以我们只能耍耍流氓咯。我也不支持大家这么做,没办法,谁叫这是GM为老大呢?)。

我在想要不要先讲一下原理,因为有些长,算了,讲一下吧!谁叫我啰嗦呢?(关于现在app涉及各种隐晦的权限,我在这里说一下原则问题哈,技术本身没有错,用善则善,用恶则恶。我们都希望一个好的环境做更好的事情。)

<1> 子进程(监视进程)如何监视到父进程死亡?

当父进程死亡以后,子进程就成为了孤儿进程由Init进程领养,于是我们可以在一个循环中读取子进程的父进程PID,当变为1就说明其父进程已经死亡,于是可以重启父进程。这里因为采用了循环,所以就有了新问题,那就是耗电。耗电的问题大家先想一想怎么解决,当然本文也会讲到,对用户多负责一点,也是对我们自己负责(重声原则问题,看上面)。

<2>父进程(监视进程)如何监视到子进程的死亡?

很简单,在linux中,子进程被终止时,会向父进程发送SIG_CHLD信号,于是我们可以安装信号处理函数,并在此信号处理函数中重新启动创建监视进程;

<3>父子进程间的通信

父子进程间建立通信通道,然后通过监视此通道来感知对方的存在,这样不会存在之前提到的耗电量的问题,采用了轮询父进程PID的办法,但是还是留出了父子进程的通信通道。(暂时不讲这个)

现在开始写代码,请看下面先来个java类,给外部提供一个API接口,当然所有的实现都通过native方法在C++中完成!爆红我们先不管,等会处理

Android——NDK双进程守护

java类

然后我们写.cpp类,看下图(如果你不想看文字觉得枯燥就直接看图,我在代码里面写的注释应该算是很清楚),然后说一下sigaction函数,它是用于改变进程接收到特定信号后的行为。它有三个参数

int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact)
参数
--第一个参数是信号的值,可以为除了SIGKILL及SIGSTOP外的任何一个特定有效的信号(因为这两个信号定义了自己的处理函数,将导致信号安装错误)
--第二个参数是指向节后sigaction的一个实例的指针,在sigaction的实例中,指定了对特定信号的处理,可以为NULL,进程会以缺省方式对信号处理
--第三个参数oldact指向的对象用来保存原来对相应信号的处理,可以为NULL
返回值:函数成功返回0,失败返回-1。

然后我们这个sigaction现在干什么呢?

首先sigaction信号处理sa.sa_flags=SA_RESTART:使被信号打断的系统调用自动重新发起信号处理交给sig_handler处理的,当子进程挂了的时候会向其父进程发送一个SIGCHLD信号,父进程就会收到SIGCHLD信号,并且开始执行sig_handler方法,重生成子进程。

然后调用create_child()方法(这个时候估计有人就奇怪了,怎么你一会说函数,一会说方法,首先明确我是搞Android的,我们跟JAVA一样都是叫方法,所以习惯性的。)。

说下原理:

一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。就是相当于克隆了一个自己。

当pid==0 说明是子进程(这个时候我们就开启子线程child_start_monitor()),pid>0是父进程

Android——NDK双进程守护

.cpp文件

开启子线程:讲一下里面的参数以及功能

原型:int pthread_create((pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
用法:#include <pthread.h>
功能:创建线程(实际上就是确定调用该线程函数的入口点),在线程创建以后,就开始运行相关的线程函数。
说明:thread:线程标识符;
attr:线程属性设置;
start_routine:线程函数的起始地址(这里我们写上我们需要守护的service);
arg:传递给start_routine的参数;
返回值:成功,返回0;出错,返回-1。
/<pthread.h>

然后是指针函数,返回值是指针的函数。有些难理解吧!我们类比一下, 如果是java的话,它就相当于run()方法。这下应该知道了吧!然后我们在讲一下原理:

这里在代码中我们明显可以看到while循环,为什么要循环呢?很简单,就是我们要不断的判断父进程是不是被干掉了,子进程反复的循环判断父进程是否被干掉,如果被干掉就说明小插件的服务被干掉了。这也就是为什么会出现耗电问题,其实有办法优化,有兴趣的老铁可以自己想想,或者查阅相关资料,下次再发一篇关于优化的。咋们继续,如果父进程被干掉了,那么它的子进程就会被init托管,而这个进程的id就是1。那么我们就要重启父进程,调用am.startservice,制定服务的进程id。 

Android——NDK双进程守护

开启子线程

最后,上面都只是讲到从创建子进程拉起父进程,那么子进程咋办,所以我们还需要下面这个方法:

Android——NDK双进程守护

子进程变成僵尸进程会调用这个方法

好了,现在基本已经写完了.cpp文件,讲的太多就容易感觉枯燥。然后生成.so文件,上次没讲到如果生成.so文件报这个错咋办,看下图:

Android——NDK双进程守护

点击这个小锤子生成.so文件

这个错下面给了解决办法,我们不细说这个错是为什么。一讲又是很长,直接说解决办法,两种方法:

第一:创建Android.mk和Application.mk。(详见上文)
第二:在你的gradle.propperties文件加上这么一句话:android.deprecatedNdkCompileLease=1541151645526

OK,咋们继续小锤子一下。好了,暂时文章到此结束。谢谢大家花时间看这么枯燥的文章,如果有大神大看到文章中有什么地方有错误,希望直接指出,我会第一时间改进,因为不希望出现误人子弟,再次表示感谢。

由于我日常很忙,手里两三个项目,所以基本上很难抽时间出来分享,不过在经过几天的思想斗争后,我觉得再忙我也要写一些技术的文章,因为回顾,学习是很重要的,我想要进步。从下周开始每周分享四篇HTML+CSS+JS的文章,从基础到最后能开发项目为止(基础就是帮助没学过但想学习的人从头教,系统的学),加上固定两篇Android的技术文章(下周开始不固定源码解析,因为忙所以写关于源码的可能容易跳票。),有兴趣的希望大家一起学,共同进步。码字真的不容易,花了两个小时,因为我是所有的代码都是现写的,边写边截图真心累。


分享到:


相關文章: