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/函數

相關文章
相關標籤/搜索