Last active
March 30, 2021 08:54
-
-
Save spiritedRunning/6a4178a2e939b9a0db7eddacefa8d67d to your computer and use it in GitHub Desktop.
进程守护 使用信号量通知
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <jni.h> | |
#include "ProcessWatcher.h" | |
extern ProcessBase *g_process; | |
JNIEXPORT jboolean JNICALL | |
Java_com_example_free_watcher_Watcher_createWatcher(JNIEnv *env, jobject instance, jstring objname_, | |
jstring type_) { | |
const char *objname = env->GetStringUTFChars(objname_, 0); | |
const char *type = env->GetStringUTFChars(type_, 0); | |
// TODO | |
g_process = new Parent(env, instance); | |
g_process->catch_child_dead_signal(); | |
if (!g_process->create_child()) { | |
LOGE("<<create child error!>>"); | |
return JNI_FALSE; | |
} | |
env->ReleaseStringUTFChars(objname_, objname); | |
env->ReleaseStringUTFChars(type_, type); | |
return JNI_TRUE; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class MainActivity extends Activity { | |
@Override | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_main); | |
Watcher watcher = new Watcher(); | |
watcher.createActivityMonitor("com.example.free.ndkdemocode/com.example.free.ndkdemocode.MainActivity"); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "ProcessWatcher.h" | |
ProcessBase *g_process; | |
const char *g_objname; | |
const char *g_type; | |
JNIEnv *g_env; | |
int get_version(); | |
static bool DEBUG = true; | |
ProcessBase::ProcessBase() { | |
} | |
ProcessBase::~ProcessBase() { | |
} | |
Parent::Parent(JNIEnv *env, jobject jobj) { | |
if (DEBUG) { | |
LOGE("<<new parent instance>>"); | |
} | |
} | |
Parent::~Parent() { | |
if (DEBUG) { | |
LOGE("<<Parent::~Parent()>>"); | |
} | |
g_process = NULL; | |
} | |
void Parent::do_work() { | |
} | |
/** | |
* 子进程死亡会发出SIGCHLD信号,通过捕捉此信号父进程可以 | |
* 知道子进程已经死亡,此函数即为SIGCHLD信号的处理函数. | |
*/ | |
static void sig_handler(int signo) { | |
pid_t pid; | |
int status; | |
//调用wait等待子进程死亡时发出的SIGCHLD | |
//信号以给子进程收尸,防止它变成僵尸进程 | |
pid = wait(&status); | |
if (DEBUG) { | |
LOGE("<<sig_handler>>"); | |
} | |
if (g_process != NULL) { | |
g_process->on_child_end(); | |
} | |
} | |
void Parent::catch_child_dead_signal() { | |
if (DEBUG) { | |
LOGE("<<process %d install child dead signal detector!>>", getpid()); | |
} | |
struct sigaction sa; | |
sigemptyset(&sa.sa_mask); | |
sa.sa_flags = 0; | |
sa.sa_handler = sig_handler; | |
sigaction(SIGCHLD, &sa, NULL); | |
} | |
void Parent::on_child_end() { | |
if (DEBUG) { | |
LOGE("<<on_child_end:create a new child process>>"); | |
} | |
create_child(); | |
} | |
bool Parent::create_child() { | |
pid_t pid; | |
if ((pid = fork()) < 0) { | |
return false; | |
} else if (pid == 0) //子进程 | |
{ | |
if (DEBUG) { | |
LOGE("<<In child process,pid=%d>>", getpid()); | |
} | |
Child child; | |
ProcessBase &ref_child = child; | |
ref_child.do_work(); | |
} else if (pid > 0) //父进程 | |
{ | |
if (DEBUG) { | |
LOGE("<<In parent process,pid=%d>>", getpid()); | |
} | |
} | |
return true; | |
} | |
bool Child::create_child() { | |
//子进程不需要再去创建子进程,此函数留空 | |
return false; | |
} | |
Child::Child() { | |
RTN_MAP.member_rtn = &Child::parent_monitor; | |
} | |
Child::~Child() { | |
} | |
void Child::catch_child_dead_signal() { | |
//子进程不需要捕捉SIGCHLD信号 | |
return; | |
} | |
void Child::on_child_end() { | |
//子进程不需要处理 | |
return; | |
} | |
void Child::handle_parent_die() { | |
//子进程成为了孤儿进程,等待被Init进程收养后在进行后续处理 | |
while (getppid() != 1) { | |
usleep(500); //休眠0.5ms | |
} | |
//重启父进程服务 | |
if (DEBUG) { | |
LOGE("<<parent died,restart now>>"); | |
} | |
restart_parent(); | |
} | |
void Child::restart_parent() { | |
if (DEBUG) { | |
LOGE("<<restart_parent enter>>"); | |
} | |
/** | |
* TODO 重启父进程,通过am启动Java空间的任一组件(service或者activity等)即可让应用重新启动 | |
*/ | |
if (strcmp(g_type, "Activity") == 0) { | |
if (DEBUG) { | |
LOGE("<<restart_Activity>>"); | |
} | |
execlp("am", "am", "start", "-e", "daemon", "triger", "--user", "0", "-n", g_objname, "-a", | |
"android.intent.action.VIEW", "-d", "", (char *) NULL); | |
} else if (strcmp(g_type, "Service") == 0) { | |
//在api17之后AM命令有些不同这里需要写兼容。获取版本号的方法已经写在了下面。 | |
int g_version = get_version(); | |
if (g_version >= 17 || g_version == 0) { | |
if (DEBUG) { | |
LOGE("<<restart_service more than 17>>"); | |
} | |
int ret = execlp("am", "am", "startservice", "-e", "daemon", "triger", "--user", "0", | |
"-n", g_objname, (char *) NULL); | |
} else { | |
if (DEBUG) { | |
LOGE("<<restart_service enter bleow 17>>"); | |
} | |
execlp("am", "am", "startservice", "-e", "daemon", "triger", "-n", g_objname, | |
(char *) NULL); | |
} | |
} | |
} | |
void *Child::parent_monitor() { | |
handle_parent_die(); | |
} | |
void Child::start_parent_monitor() { | |
pthread_t tid; | |
pthread_create(&tid, NULL, RTN_MAP.thread_rtn, this); | |
pthread_join(tid, NULL); | |
} | |
void Child::do_work() { | |
start_parent_monitor(); //启动监视线程 | |
if (DEBUG) { | |
LOGE("<<start_parent_monitor>>"); | |
} | |
} | |
int get_version() { | |
char value[8] = ""; | |
__system_property_get("ro.build.version.sdk", value); | |
return atoi(value); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef NDKDEMOCODE_DAEMON_H | |
#define NDKDEMOCODE_DAEMON_H | |
#include <jni.h> | |
#include <sys/select.h> | |
#include <unistd.h> | |
#include <sys/socket.h> | |
#include <pthread.h> | |
#include <signal.h> | |
#include <sys/wait.h> | |
#include <android/log.h> | |
#include <sys/types.h> | |
#include <sys/un.h> | |
#include <errno.h> | |
#include <stdlib.h> | |
#include <sys/system_properties.h> | |
#define LOG_TAG "Native" | |
#define LOGE(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__) | |
/** | |
* 功能:对父子进程的一个抽象 | |
*/ | |
class ProcessBase { | |
public: | |
ProcessBase(); | |
/** | |
* 父子进程要做的工作不相同,留出一个抽象接口由父子进程 | |
* 自己去实现. | |
*/ | |
virtual void do_work() = 0; | |
/** | |
* 进程可以根据需要创建子进程,如果不需要创建子进程,可以给 | |
* 此接口一个空实现即可. | |
*/ | |
virtual bool create_child() = 0; | |
/** | |
* 捕捉子进程死亡的信号,如果没有子进程此方法可以给一个空实现. | |
*/ | |
virtual void catch_child_dead_signal() = 0; | |
/** | |
* 在子进程死亡之后做任意事情. | |
*/ | |
virtual void on_child_end() = 0; | |
virtual ~ProcessBase(); | |
}; | |
/** | |
* 功能:父进程的实现 | |
*/ | |
class Parent : public ProcessBase { | |
public: | |
Parent(JNIEnv *env, jobject jobj); | |
virtual bool create_child(); | |
virtual void do_work(); | |
virtual void catch_child_dead_signal(); | |
virtual void on_child_end(); | |
virtual ~Parent(); | |
bool create_channel(); | |
/** | |
* 获取父进程的JNIEnv | |
*/ | |
JNIEnv *get_jni_env() const; | |
/** | |
* 获取Java层的对象 | |
*/ | |
jobject get_jobj() const; | |
}; | |
/** | |
* 子进程的实现 | |
*/ | |
class Child : public ProcessBase { | |
public: | |
Child(); | |
virtual ~Child(); | |
virtual void do_work(); | |
virtual bool create_child(); | |
virtual void catch_child_dead_signal(); | |
virtual void on_child_end(); | |
private: | |
/** | |
* 处理父进程死亡事件 | |
*/ | |
void handle_parent_die(); | |
/** | |
* 重新启动父进程. | |
*/ | |
void restart_parent(); | |
/** | |
* 线程函数,用来检测父进程是否挂掉 | |
*/ | |
void *parent_monitor(); | |
void start_parent_monitor(); | |
/** | |
* 这个联合体的作用是帮助将类的成员函数做为线程函数使用 | |
*/ | |
union { | |
void *(*thread_rtn)(void *); | |
void *(Child::*member_rtn)(); | |
} RTN_MAP; | |
}; | |
#endif //NDKDEMOCODE_DAEMON_H |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Watcher { | |
static { | |
System.loadLibrary("ProcessWatcher"); | |
} | |
public Watcher() { | |
} | |
public void createActivityMonitor(String g_objname) { | |
this.createWatcher(g_objname, "Activity"); | |
} | |
public void createServiceMonitor(String g_objname) { | |
this.createWatcher(g_objname, "Service"); | |
} | |
private native boolean createWatcher(String objname, String type); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment