在linux经常提到deamon进程,父进程监控管理子进程,在服务端或不间断服务编程中常用,且实现原理较容易,因为linux 有完善的信号机制,在win32下也可以实现,虽然有些复杂。下面就说说win32实现的原理。分析ACL 实现机制。
先让父进程以守护进程模式启动 proctld.exe,然后运行 proctld.exe -d START {path}/proctlc.exe 通知父进程启动子进程;
- 以deamon方式启动父进程,主要是这二个接口
- void acl_proctl_deamon_init
- void proctl_monitor_loop
-
在文件 acl_proctl.c 中
-
void acl_proctl_deamon_init(const char *progname)
{
acl_pthread_attr_t attr;
acl_pthread_t tid;
var_progname = acl_mystrdup(progname);
proctl_start_init(var_progname); //
acl_pthread_attr_init(&attr);
(void) acl_pthread_attr_setdetachstate(&attr, 1);
acl_pthread_create(&tid, &attr, proctl_monitor_thread, NULL);//
}
1) proctl_start_init(var_progname); 主要是完成如下
__sem_handle = CreateSemaphore(NULL, 0, 1024, NULL); //创建信号灯允许1024个子进程
handles_add(__sem_handle); //增加到handle 数组 后面用到
2)proctl_monitor_thread 线程函数
调用关系:
proctl_monitor_loop
-->proctl_monitor_main
-->proctl_monitor_cmd_start
-->proctl_msg_push
(msg
)主要完成本地端口bind,listen,accept, 进入recv循环,收到要启动的子进程信息。这里要说明一下 proctld.exe -d START {path}/proctlc.exe的实现,这里会socket方式 连接到proctld.exe ,告知deamon进程要启动的子进程的信息,路径、文件名等。然后此进程收到服务端(proctld.exe)响应后就退出了。服务端将收到的信息构造成“msg”,
proctl_msg_push
(msg
)放到了队列中,然后ReleaseSemaphore
(__sem_handle
, 1
, NULL); 发出信号,这是线程函数完成功能。
那主线程呢?
看proctl_monitor_loop
void acl_proctl_daemon_loop()
{
const char *myname = "acl_proctl_daemon_loop";
time_t tm_start, tm_end;
while (1) {
tm_start = time(NULL);
proctl_service_wait(); // --> proctl_service_wait(void)
proctl_service_join(); //**** tm_end = time(NULL);
if (tm_end - tm_start <= 1) {
acl_msg_warn("%s(%d): start process too fast, sleep 2 second",
myname, __LINE__);
sleep(2);
}
}
}
int proctl_service_wait(void)
- {
const char *myname = "proctl_service_wait";
DWORD timeout = 1000 * 2, ret;
char ebuf[256];
HANDLE handle_sem;
if (__cur_handle == 0)
return (0);
/* Create the semaphore, with max value 32K */
handle_sem = CreateSemaphore(NULL, 0, 32 * 1024, NULL);
while (1) {
ret = WaitForMultipleObjects(__cur_handle, __handles, FALSE, timeout); //接收到上面提到的发出的信号
if (ret == WAIT_OBJECT_0) {
proctl_msg_main();
} else - .......
}
return (0);
}
- proctl_msg_main 函数
- 调用关系如下:
-
acl_fifo_pop-->proctl_service_start()-->proctl_service_add(service);
-
proctl_service_start(service)-->handles_add(service->hProcess);
功能:从队列中取出消息,启动子进程。
int proctl_service_join(void)
{
const char *myname = "proctl_service_join";
PROCTL_SERVICE *service;
HANDLE hProcess;
DWORD status;
int i;
char ebuf[256];
for (i = 0; i < __cur_handle; i++) {
hProcess = __handles[i];
if (hProcess == INVALID_HANDLE_VALUE)
service = proctl_service_find(hProcess);
if (service == NULL) {
if (hProcess == __sem_handle)
continue;
}
status = 0;
if (!GetExitCodeProcess(hProcess, &status)) { //不断获得子进程的状态,若停止了就重新启动
if (proctl_service_restart(service) < 0)
proctl_service_stopped(service);
} else if (status == 0) {
proctl_service_stopped(service); } else if (status != STILL_ACTIVE) {
if (proctl_service_restart(service) < 0) proctl_service_stopped(service);
}
/* else: STILL_ACTIVE */
}
return (0);
}
-
---------------------------------------------------------------
-
---------------------------------------------------------------
-
//子进程代码
- 主要函数void acl_proctl_child(const char *progname, void (*onexit_fn)(void *), void *arg);
子进程在启动时会用local_listen
,acl_vstream_accept,acl_vstream_gets_nonl等函数bind,accept,recv与父进程进行socket连接,在输入命令proctld.exe -d STOP {path}/proctld.exe时,父进程需要主动连接到对应的子进程,然后发送“STOP”命令给子进程,由子进程自己结束自己,而不是父进程直接结束子进程,这一点很不错,可以由子进程来选择自己适当的停止或重新等操作。
到此为止,将win32 下ACL watch dog 机制简单的理顺了一下。但是这样做法也有个问题,若是在一台没有网卡的机器上运行,因需要bind ip和port, 所以会不成功。那么是不是就失效了,虽然现在很少有这种情况。唉,还是linux 下舒服。
可以运行 proctld.exe -d LIST 列出当前正在运行的子进程,运行 proctld.exe -d PROBE
{path}/proctld.exe 判断子进程是否在运行,运行 proctld.exe -d STOP {path}/proctld.exe
让守护父进程停止子进程,运行 proctld.exe -d QUID 使守护进程停止所有子进程并自动退出。