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的技術文章(下週開始不固定源碼解析,因為忙所以寫關於源碼的可能容易跳票。),有興趣的希望大家一起學,共同進步。碼字真的不容易,花了兩個小時,因為我是所有的代碼都是現寫的,邊寫邊截圖真心累。


分享到:


相關文章: