ACL编程之父子进程机制,父进程守护子进程以防止子进程异常退出

在WIN32平台进行编程时,常常会遇到工做进程由于程序内部BUG而异常退出的现象,固然为了解决此类问题最好仍是找到问题所在并解决它,但若是这类致使程序崩溃的BUG并非常常出现,只有当某种条件发生时才会有,在咱们解决BUG的时间里,为了尽最大可能地为用户提供服务能够采用一种父进程守护机制:当子进程异常退出时,守护父进程能够截获这一消息,并当即重启子进程,这样用户就能够继续使用咱们的程序了,固然若是子进程的问题比较严重频繁地 DOWN掉,而父进程却不停地重启子进程的话,势必形成用户机系统资源的大量耗费,那咱们的程序就如病毒同样,很快耗尽了用户机资源,因此须要父进程可以智能地控制重启子进程的时间间隔。
  本文将给出一个具体的例子(利用ACL库),介绍父、子进程的编程方法。
1、接口介绍
1.1 以守护进程方式运行的接口
建立守护进程的方式很是简单,只须要调用 acl_proctl_deamon_init, acl_proctl_daemon_loop 两个函数便可
接口说明以下:
编程

/**
 * 初始化进程控制框架(仅 acl_proctl_start 须要)
 * @param progname {const char*} 控制进程进程名
 */
ACL_API void acl_proctl_deamon_init(const char *progname);

/**
 * 控制进程做为后台服务进程运行,监视全部子进程的运行状态,
 * 若是子进程异常退出则会重启该子进程
 */
ACL_API void acl_proctl_daemon_loop(void);


1.2 以命令方式来控制守护进程(守护进程即控制进程的意思)
守护进程启动后,能够以命令方式控制守护进程来启动、中止子进程,或查询显示当前正在运行的子进程。
启动子进程:acl_proctl_start_one
中止子进程:acl_proctl_stop_one
中止全部子进程:acl_proctl_stop_all
查询子进程是否在运行:acl_proctl_probe
查询当前全部在运行的子进程:acl_proctl_list
经过守护进程中止全部子进程且守护进程自身退出:acl_proctl_quit

接口说明以下:
数组

/**
 * 以命令方式启动某个子进程
 * @param progname {const char*} 控制进程进程名
 * @param progchild {const char*} 子进程进程名
 * @param argc {int} argv 数组的长度
 * @param argv {char* []} 传递给子进程的参数
 */
ACL_API void acl_proctl_start_one(const char *progname,
    const char *progchild, int argc, char *argv[]);

/**
 * 以命令方式中止某个子进程
 * @param progname {const char*} 控制进程进程名
 * @param progchild {const char*} 子进程进程名
 * @param argc {int} argv 数组的长度
 * @param argv {char* []} 传递给子进程的参数
 */
ACL_API void acl_proctl_stop_one(const char *progname,
    const char *progchild, int argc, char *argv[]);

/**
 * 以命令方式中止全部的子进程
 * @param progname {const char*} 控制进程进程名
 */
ACL_API void acl_proctl_stop_all(const char *progname);

/**
 * 探测某个服务进程是否在运行
 * @param progname {const char*} 控制进程进程名
 * @param progchild {const char*} 子进程进程名
 */
ACL_API void acl_proctl_probe(const char *progname, const char *progchild);

/**
 * 列出当前全部正在运行的服务进程
 * @param progname {const char*} 控制进程进程名
 */
ACL_API void acl_proctl_list(const char *progname);

/**
 * 以命令方式通知控制进程中止全部的子进程,并在子进程退出后控制进程也自动退出
 * @param progname {const char*} 控制进程进程名
 */
ACL_API void acl_proctl_quit(const char *progname);


1.三、子进程编写
子进程编程也比较容易,只需在程序初始化时调用 acl_proctl_child 便可,这样子进程就会在硬盘建立本身的信息并与父进程(即守护进程)创建联系。
接口说明:
服务器

/**
 * 子进程调用接口,经过此接口与父进程之间创建控制/被控制关系
 * @param progname {const char*} 子进程进程名
 * @param onexit_fn {void (*)(void*)} 若是非空则当子进程退出时调用的回调函数
 * @param arg {void*} onexit_fn 参数之一
 */
ACL_API void acl_proctl_child(const char *progname, void (*onexit_fn)(void *), void *arg);


2、例子
2.一、父进程
程序名:acl_project\samples\proctl\proctld.cpp
框架

// proctld.cpp : 定义控制台应用程序的入口点。
//
#pragma comment(lib,"ws2_32")
#include "lib_acl.h"
#include <assert.h>

static void init(void)
{
	acl_init();  // 初始化ACL库  
}

static void usage(const char *progname)
{
	printf("usage: %s -h [help] -d [START|STOP|QUIT|LIST|PROBE] -f filepath -a args\r\n",
			progname);
	getchar();
}

int main(int argc, char *argv[])
{
	char  ch, filepath[256], cmd[256];
	char **child_argv = NULL;
	int   child_argc = 0, i;
	ACL_ARGV *argv_tmp;

	filepath[0] = 0;
	cmd[0] = 0;

	init();

	while ((ch = getopt(argc, argv, "d:f:a:h")) > 0) {
		switch(ch) {
			case 'd':
				ACL_SAFE_STRNCPY(cmd, optarg, sizeof(cmd));
				break;
			case 'f':
				ACL_SAFE_STRNCPY(filepath, optarg, sizeof(filepath));
				break;
			case 'a':
				argv_tmp = acl_argv_split(optarg, "|");
				assert(argv_tmp);
				child_argc = argv_tmp->argc;
				child_argv = (char**) acl_mycalloc(child_argc + 1, sizeof(char*));
				for (i = 0; i < child_argc; i++) {
					child_argv[i] = acl_mystrdup(argv_tmp->argv[i]);
				}
				child_argv[i] = NULL;

				acl_argv_free(argv_tmp);
				break;
			case 'h':
				usage(argv[0]);
				return (0);
			default:
				usage(argv[0]);
				return (0);
		}
	}

	if (strcasecmp(cmd, "STOP") == 0) {
		// 向守护进程发送消息命令,中止某个子进程或全部的子进程
		if (filepath[0])
			acl_proctl_stop_one(argv[0], filepath, child_argc, child_argv);
		else
			acl_proctl_stop_all(argv[0]);
	} else if (strcasecmp(cmd, "START") == 0) {
		if (filepath[0] == 0) {
			usage(argv[0]);
			return (0);
		}
		// 向守护进程发送消息命令,启动某个子进程
		acl_proctl_start_one(argv[0], filepath, child_argc, child_argv);
	} else if (strcasecmp(cmd, "QUIT") == 0) {
		// 向守护进程发送消息命令,中止全部的子进程同时守护父进程也退出
		acl_proctl_quit(argv[0]);
	} else if (strcasecmp(cmd, "LIST") == 0) {
		// 向守护进程发送消息命令,列出由守护进程管理的正在运行的全部子进程
		acl_proctl_list(argv[0]);
	} else if (strcasecmp(cmd, "PROBE") == 0) {
		if (filepath[0] == 0) {
			usage(argv[0]);
			return (0);
		}
		// 向守护进程发送消息命令,探测某个子进程是否在运行
		acl_proctl_probe(argv[0], filepath);
	} else {
		// 父进程以守护进程方式启动
		char  buf[MAX_PATH], logfile[MAX_PATH], *ptr;

		// 得到父进程执行程序所在的磁盘路径
		acl_proctl_daemon_path(buf, sizeof(buf));
		ptr = strrchr(argv[0], '\\');
		if (ptr == NULL)
			ptr = strrchr(argv[0], '/');

		if (ptr == NULL)
			ptr = argv[0];
		else
			ptr++;

		snprintf(logfile, sizeof(logfile), "%s/%s.log", buf, ptr);
		// 打开日志文件
		acl_msg_open(logfile, "daemon");
		// 打开调试信息
		acl_debug_init("all:2");

		// 以服务器模式启动监控进程
		acl_proctl_deamon_init(argv[0]);
		// 父进程做为守护进程启动
		acl_proctl_daemon_loop();
	}

	if (child_argv) {
		for (i = 0; child_argv[i] != NULL; i++) {
			acl_myfree(child_argv[i]);
		}
		acl_myfree(child_argv);
	}
	return (0);
}


2.二、子进程
acl_project\samples\proctl\proctlc.cpp
socket

// proctlc.cpp : 定义控制台应用程序的入口点。
//
#pragma comment(lib,"ws2_32")
#include "lib_acl.h"

static void onexit_fn(void *arg acl_unused)
{
	printf("child exit now\r\n");
}

int main(int argc, char *argv[])
{
	int   i;

	acl_socket_init();
	acl_msg_open("debug.txt", "proctlc");
	acl_msg_info(">>> in child progname(%s), argc=%d\r\n", argv[0], argc);
	if (argc > 1)
		acl_msg_info(">>> in child progname, argv[1]=(%s)\r\n", argv[1]);

	// 子进程启动,同时注册自身信息
	acl_proctl_child(argv[0], onexit_fn, NULL);

	for (i = 0; i < argc; i++) {
		acl_msg_info(">>>argv[%d]:%s\r\n", i, argv[i]);
	}

	i = 0;
	while (1) {
		acl_msg_info("i = %d\r\n", i++);
		if (i == 5)
			break;
		else
			sleep(1);
	}
	return (-1);  // 返回 -1 是为了让父进程继续启动
}


2.三、编译、运行
  能够打开 acl_project\win32_build\vc\samples\samples_vc2003.sln,编译其中的 proctlc, proctld 两个工程,便会生成两个可执行文件:proctlc.exe(子进程程序),proctld.exe(父进程程序)。
  先让父进程以守护进程模式启动 proctld.exe,而后运行 proctld.exe -d START {path}/proctlc.exe 通知父进程启动子进程;能够运行 proctld.exe -d LIST 列出当前正在运行的子进程,运行 proctld.exe -d PROBE {path}/proctld.exe 判断子进程是否在运行,运行 proctld.exe -d STOP {path}/proctld.exe 让守护父进程中止子进程,运行 proctld.exe -d QUID 使守护进程中止全部子进程并自动退出。
  另外,从子进程的程序能够看出,每隔5秒子进程就会异常退出,则守护进程便会当即重启该子进程,若是子进程死的过于频繁,则守护进程会延迟重启子进程,以防止太过耗费系统资源。

3、小结
  由于有守护进程保护,就没必要担忧子进程(即你的工做进程)异常崩溃了,这种父子进程模型能够应用于大多数工做子进程偶尔异常崩溃的情形,若是你的程序 BUG太多,每一下子就崩溃好屡次,建议你仍是先把主要问题解决后再使用父子进程,毕竟若是你的程序太过脆弱,虽然父进程能不断地重启你的程序,但你仍是不能为用户提供正常服务。这种模型适用于在WIN32平台下,你的程序可能写得比较复杂,程序基本上是比较健壮的,只是会因偶尔某些缘由而异常退出的状况。
    关于ACL库的在线帮助能够参照:http://acl.sourceforge.net/函数

相关文章
相关标签/搜索