|
最近经常写一些需要自我维护且能自动启动的程序。
当然,最简单的方法是用一个shell去搞定,但是这个shell太粗俗了,如果我一个程序启动有7个进程,其中一个挂了,我就不得不重启7个所有的进程,这显然是不能让我满意的。
于是开发了一个小接口,通用代码,实现对当前程序所有进程的监控,时刻监控当前程序下的所有进程,如果有挂了的,立刻重启当前进程,其他进程不动,最大程度减少损失。
于是,于是,这个小程序就诞生了。
看看我用了什么东东。
首先是文件锁,这个东东很有用。
相关API是,fcntl
关于这个函数,我只用到了三个宏。
F_WRLCK //加写锁
F_UNLCK //解锁
F_GETLK //得到锁当前的状态
作为测试用例,我故意启动了一个子进程,然后在程序外,直接强硬的kill掉这个子进程,我的目的是,这个程序会自己恢复这个子进程。
作料嘛,还差一点。
那就是一个文件,提供一个IO记录这个锁状态。
这里对锁操作,我封装了三个函数。- //写独占文件锁
- int AcquireWriteLock(int fd, int start, int len)
- {
- struct flock arg;
- arg.l_type = F_WRLCK; // 加写锁
- arg.l_whence = SEEK_SET;
- arg.l_start = start;
- arg.l_len = len;
- arg.l_pid = getpid();
- return fcntl(fd, F_SETLKW, &arg);
- }
复制代码 释放独占锁- //释放独占文件锁
- int ReleaseLock(int fd, int start, int len)
- {
- struct flock arg;
- arg.l_type = F_UNLCK; // 解锁
- arg.l_whence = SEEK_SET;
- arg.l_start = start;
- arg.l_len = len;
- arg.l_pid = getpid();
- return fcntl(fd, F_SETLKW, &arg);
- }
复制代码 查看独占锁- //查看写锁
- int SeeLock(int fd, int start, int len)
- {
- struct flock arg;
- arg.l_type = F_WRLCK;
- arg.l_whence = SEEK_SET;
- arg.l_start = start;
- arg.l_len = len;
- arg.l_pid = getpid();
- if (fcntl(fd, F_GETLK, &arg) != 0) // 获取锁
- {
- return -1; // 测试失败
- }
- if (arg.l_type == F_UNLCK)
- {
- return 0; // 无锁
- }
- else if (arg.l_type == F_RDLCK)
- {
- return 1; // 读锁
- }
- else if (arg.l_type == F_WRLCK)
- {
- return 2; // 写所
- }
- return 0;
- }
复制代码 好了,我模拟一个独立子进程- int Chlid_Run()
- {
- //测试子进程
- struct timespec tsSleep;
-
- tsSleep.tv_sec = 5;
- tsSleep.tv_nsec = 0;
-
- while(true)
- {
- nanosleep(&tsSleep, NULL);
- }
- }
复制代码 这里只是一个死循环,模拟子进程工作的动作。
实际生产环境中,我有若干个队列处理子进程,对应不同的msgqueue。有20个。
这里只是为了展示功能,并提供相应的测试代码,你可以在这里替换成你的代码。
好了,最后看看我的主程序如何实现吧。对了,再说一句,你要想玩它,你可以把nNumChlid设置成你喜欢的子进程数。
而实际我的生产环境代码比这个要多的多,主要是我把配置文件,子进程数量全部写到ini文件里面去了,直接读取配置文件从而实现自动配置管理。
并且我的进程挂掉的时候,我会自动输出到短信接口,并备份记录core文件,提示维护人员查看。配合core文件提供给开发人员分析错误。同时保证最大的业务运行。
关于这部分的代码,相信读者会自己实现的,就不在这里展示了。- int main(int argc, char *argv[])
- {
- //当前监控子线程个数
- int nNumChlid = 1;
-
- //检测时间间隔参数
- struct timespec tsRqt;
-
- //文件锁
- int fd_lock = 0;
-
- int nRet = 0;
- //主进程检测时间间隔(设置每隔5秒一次)
- tsRqt.tv_sec = 5;
- tsRqt.tv_nsec = 0;
-
- // 打开(创建)锁文件
- char szFileName[200] = {'\0'};
- memset(szFileName, 0, sizeof(flock));
- sprintf(szFileName, "%s/cfg/testwatch.lk", getenv("HOME"));
- fd_lock = open(szFileName, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
- if (fd_lock < 0)
- {
- printf("open the flock and exit, errno = %d.", errno);
- exit(1);
- }
-
- //查看当前文件锁是否已锁
- nRet = SeeLock(fd_lock, 0, sizeof(int));
- if (nRet == -1 || nRet == 2)
- {
- printf("file is already exist!");
- exit(1);
- }
-
- //如果文件锁没锁,则锁住当前文件锁
- if (AcquireWriteLock(fd_lock, 0, sizeof(int)) != 0)
- {
- printf("lock the file failure and exit, idx = 0!.");
- exit(1);
- }
-
- //写入子进程锁信息
- lseek(fd_lock, 0, SEEK_SET);
- for (int nIndex = 0; nIndex <= nNumChlid; nIndex++)
- {
- write(fd_lock, &nIndex, sizeof(nIndex));
- }
-
- while (1)
- {
-
- for (int nChlidIndex = 1; nChlidIndex <= nNumChlid; nChlidIndex++)
- {
- //测试每个子进程的锁是否还存在
- nRet = SeeLock(fd_lock, nChlidIndex * sizeof(int), sizeof(int));
- if (nRet == -1 || nRet == 2)
- {
- continue;
- }
- //如果文件锁没有被锁,则设置文件锁,并启动子进程
- int npid = fork();
- if (npid == 0)
- {
- //上文件锁
- if(AcquireWriteLock(fd_lock, nChlidIndex * sizeof(int), sizeof(int)) != 0)
- {
- printf("child %d AcquireWriteLock failure.\n", nChlidIndex);
- exit(1);
- }
-
- //启动子进程
- Chlid_Run();
- printf("schang debug 2\n");
-
- //子进程在执行完任务后必须退出循环和释放锁
- //ReleaseLock(fd_lock, nChlidIndex * sizeof(int), sizeof(int));
- }
- }
-
- printf("child count(%d) is ok.\n", nNumChlid);
- //检查间隔
- nanosleep(&tsRqt, NULL);
- }
-
- return 0;
- }
复制代码 对了,在补充一下它需要的头文件。- #include <sys/types.h>
- #include <stdarg.h>
- #include <signal.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <time.h>
- #include <string.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <errno.h>
复制代码 加上空行一共170行代码。
简单吧。
行了,编译运行吧
g++ -o test testwatch.cpp
运行一下,然后kill掉一个它的子进程,过5秒,看看是否自动起来了。
以上代码在Linux 64 Red Hat Enterprise Linux Server release 5.5 测试通过。
|
|