进程的心跳用来辅助守护模块对项目的各个进程是否正常运行进行判断,当进程的心跳间隔大于阈值时,认定程序异常,守护模块终止程序并由调度模块重新启动。保障了项目运行的稳定性。
头文件.h
//进程心跳信息结构体,共享内存的一个存储单元
struct st_procinfo
{
int pid=0; //进程id
char pname[51]={0}; //进程名,可为空
int timeout=0; //超时时间(阈值),单位:秒
time_t atime=0; //最近一次心跳时间,整数
st_procinfo() = default; //默认构造函数
st_procinfo(const int in_pid, const string& in_pname, const int in_timeout, const time_t in_atime): pid(in_pid), timeout(in_timeout), atime(in_atime){
strncpy(pname, in_pname.c_str(), 50);
}
};
定义宏参数
#define MAXNUMP 1000 //最大的进程数量
#define SHMKEYP 0x5095 //共享内存的key
#define SEMKEYP 0x5095 //信号量的key
//进程心跳的操作类
class cpactive
{
private:
int m_shmid; //共享内存的id
int m_pos; //当前进程在共享内存进程组中的位置
st_procinfo *m_shm; //指向共享内存的地址空间
public:
cpactive();
// 把当前进程的信息加入共享内存进程组中
bool addpinfor(const int timeout, const string &pname="", clogfile *logfile=nullptr);
//更新共享内存进程组中当前进程的心跳时间
bool uptatime();
~cpactive(); //析构函数,从共享内存中删除当前进程的心跳记录
};
实现文件.cpp
//默认构造函数
cpactive::cpactive()
{
m_shmid=0;// 共享内存的id。
m_pos=-1; // 当前进程在共享内存进程组中的位置。
m_shm=0;// 指向共享内存的地址空间。
}
// 把当前进程的信息加入共享内存进程组中。
bool cpactive::addpinfo(const int timeout,const string &pname,clogfile *logfile)
{
if(m_pos!=-1) return true; //当前进程信息已经加入共享内存组
//获取/创建共享内存
if((m_shmid = shmget((key_t)SHMKEYP, MAXNUMP*sizeof(struct st_procinfo), 0666|IPC_CREAT)) == -1){
if (logfile!=nullptr) logfile->write("创建/获取共享内存(%x)失败。\n", SHMKEYP);
else printf("创建/获取共享内存(%x)失败。\n",SHMKEYP);
return false;
}
//将共享内存连接到当前地址空间
m_shm = (struct st_procinfo *)shmat(m_shmid, 0, 0);
//获得当前进程心跳的结构体
st_procinfo stprocinfo(getpid(), pname.c_str(), timeout, time(0));
// 进程id是循环使用的,如果曾经有一个进程异常退出,没有清理自己的心跳信息,
// 它的进程信息将残留在共享内存中,不巧的是,如果当前进程重用了它的id,
// 守护进程检查到残留进程的信息时,会向进程id发送退出信号,将误杀当前进程。
// 所以,如果共享内存中已存在当前进程编号,一定是其它进程残留的信息,当前进程应该重用这个位置。
for(int ii=0;ii<MAXNUMP;ii++){
if((m_shm+ii)->pid==stprocinfo.pid){m_pos = ii; break;}
}
//信号量,给共享内存加锁
csemp semp;
//初始化信号量
if(semp.init(SEMKYP) == false){
if (logfile!=nullptr) logfile->write("创建/获取信号量(%x)失败。\n",SEMKEYP);
else printf("创建/获取信号量(%x)失败。\n",SEMKEYP);
return false;
}
//给共享内存上锁
semp.wait();
// 如果m_pos==-1,表示共享内存的进程组中不存在当前进程编号,那就找一个空位置
if(m_pos==-1){
for(int ii=0;ii<MAXNUMP;ii++){
if((m_shm+ii)->pid == 0){m_pos = ii;break;}
}
}
// 如果m_pos==-1,表示没找到空位置,说明共享内存的空间已用完。
if(m_pos==-1){
if (logfile!=0) logfile->write("共享内存空间已用完。\n");
else printf("共享内存空间已用完。\n");
semp.post(); // 解锁。
return false;
}
//找到位置了,把当前进程信息加入到进程组中
memcpy(m_shm+m_pos, &stprocinfo, sizeof(struct st_procinfo));
//解锁
semp.post();
return true;
}
// 更新共享内存进程组中当前进程的心跳时间。
bool cpactive::uptatime()
{
if (m_pos==-1) return false;
(m_shm+m_pos)->atime=time(0);
return true;
}
//析构函数,从共享内存组中移除当前进程信息
cpactive::~cpactive()
{
// 把当前进程从共享内存的进程组中移去。
if (m_pos!=-1) memset(m_shm+m_pos,0,sizeof(struct st_procinfo));
// 把共享内存从当前进程中分离。
if (m_shm!=0) shmdt(m_shm);
}