当前位置: 首页 > news >正文

心跳进程与守护进程(一)

一、进程心跳

日常服务程序运行过程中,一般进程的调度、进程的心跳、守护进程共同工作。本文介绍心跳进程的原理和实现。心跳进程就是服务程序在后台运行过程中表示自己还“活着”,表示服务进程还在健康地运行着。具体实现:每个服务程序启动的时候查找共享内存,把自己心跳信息填写进去,运行过程不断更新到共享内存,代表自己还活着。流程如下:

  • 设置心跳进程结构体{进程pid,进程名,超时时间,存活时间}
  • 处理程序的退出信号。
  • 创建/获取共享内存。
  • 将共享内存连接到当前进程的地址空间。
  • 把当前进程的信息填充到结构体中。
  • 在共享内存中寻找一个空的位置,把当前进程的结构体保存到共享内存中。
  • 更新进程的心跳信息。
  • 从共享内存中删除当前进程的心跳信息。
  • 把共享内存从当前进程分离。

二、共享内存

知识点

🔴shmget函数

该函数用于创建/获取共享内存。

  • int shmget(key_t key, size_t size, int shmflg);

  • key 共享内存的键值,是一个整数(typedef unsigned int key_t),一般采用十六进制,例如0x5005,不同共享内存的key不能相同。

  • size 共享内存的大小,以字节为单位。

  • shmflg 共享内存的访问权限,与文件的权限一样,例如0666|IPC_CREAT,0666表示全部用户对它可读写,IPC_CREAT表示如果共享内存不存在,就创建它。

  • 返回值:成功返回共享内存的id(一个非负的整数),失败返回-1(系统内存不足、没有权限)

  • ipcs -m可以查看系统的共享内存,包括:键值(key),共享内存id(shmid),拥有者(owner),权限(perms),大小(bytes)。

  • ipcrm -m 共享内存id 可以手工删除共享内存,如下:

🔴shmat函数

该函数用于把共享内存连接到当前进程的地址空间。

  • void *shmat(int shmid, const void *shmaddr, int shmflg);

  • shmid 由shmget()函数返回的共享内存标识。

  • shmaddr 指定共享内存连接到当前进程中的地址位置,通常填0,表示让系统来选择共享内存的地址。

  • shmflg 标志位,通常填0。

  • 调用成功时返回共享内存起始地址,失败返回(void*)-1。

🔴shmdt函数

该函数用于将共享内存从当前进程中分离,相当于shmat()函数的反操作。

  • int shmdt(const void *shmaddr);

  • shmaddr shmat()函数返回的地址。

  • 调用成功时返回0,失败时返回-1。

🔴shmctl函数

该函数用于操作共享内存,最常用的操作是删除共享内存。

  • int shmctl(int shmid, int command, struct shmid_ds *buf);

  • shmid shmget()函数返回的共享内存id。

  • command 操作共享内存的指令,如果要删除共享内存,填IPC_RMID。

  • buf 操作共享内存的数据结构的地址,如果要删除共享内存,填0。

  • 调用成功时返回0,失败时返回-1。

三、心跳进程的代码实现

/**  heartbeat.cpp  本程序是demo用于服务程序在共享内存记录自己的心跳,表示自己还活着。最终封装成进程心跳的类。*  作者:张咸武。
*/
#include<_public.h>
using namespace std;
using namespace idc;//进程心跳的结构体
struct stprocinfo
{int pid=0;          //进程idchar pname[51];    //进程名称,可以为空。int timeout=0;      //超时时间,单位:秒。time_t atime=0;     //最后一次心跳的时间,用整数表示。stprocinfo()=default;//有了自定义的构造函数,编译器将不再提供默认构造函数,所以启用默认构造函数stprocinfo(const int in_pid,const string &in_pname,const int in_timeout,const time_t in_atime):pid(in_pid),timeout(in_timeout),atime(atime){strncpy(pname,in_pname.c_str(),50);}
};int m_shmid=-1;     //共享内存的id。
stprocinfo *m_shm=nullptr;  //指向共享内存的地址空间。
int m_pos=-1;       //用于存放当前进程在数组中的下标。void EXIT(int sig);int main()
{//处理程序的退出信号。signal(SIGINT,EXIT);signal(SIGTERM,EXIT);//创建/获取共享内存。if((m_shmid=shmget((key_t)0x5095, 1000*sizeof(stprocinfo),0666|IPC_CREAT))==-1)//如果失败{printf("创建/获取共享内存(%x)失败。\n",0x5095);return -1;}//将共享内存连接到当前进程的地址空间。m_shm=(struct stprocinfo *)shmat(m_shmid,0,0);//把共享内存中的全部进程的信息显示出来,用于调试for(int ii=0;ii<1000;ii++){if(m_shm[ii].pid!=0)    //只显示进程已使用的位置,空位置不显示。{printf("ii=%d,pid=%d,pname=%s,timeout=%d,atime=%d\n",ii,m_shm[ii].pid,m_shm[ii].pname,m_shm[ii].timeout,m_shm[ii].atime);}}stprocinfo procinfo(getpid(),"server1",30,time(0));//当前进程号 进程名 超时时间 当掐时间csemp semlock;      //用于给共享内存加锁的信号量id。if(semlock.init(0x5095)==false){printf("创建/获取信号量(%x)失败。\n",0x5095);   EXIT(-1);}//信号量加锁 P操作semlock.wait();//优化后添加的代码,重用异常退出残留共享内存的旧位置。
//进程id是循环使用的,如果曾经有一个进程异常退出,没有清理自己的心跳信息,
//他的进程信息将残留在共享内存中,不巧的是,如果当前进程重用了他的id,
//所以,如果共享内存中已存在当前进程编号,一定是其他进程残留的信息,当前进程应该重用这个位置。for(int ii=0;ii<1000;ii++){if(m_shm[ii].pid==procinfo.pid){m_pos=ii;printf("找到旧位置ii=%d\n",ii);break;}}if(m_pos==-1)   //如果已经找到了一个旧位置并重用了,就不需要再找空位置了。{//在共享内存中寻找一个空的位置,把当前进程的结构体保存到共享内存中。for(int ii=0;ii<1000;ii++){if((m_shm+ii)->pid==0)  //如果pid是空的,表示这是一个空位置。{m_pos=ii;printf("找到新位置ii=%d\n",ii);break;}}}//如果m_pos==-1,表示没找到空位置,说明共享内存的空间已用完。if(m_pos==-1){//共享空间用完需要解锁 V操作 semlock.post();("共享内存空间已用完。\n");EXIT(-1);}//把当前进程的结构体保存到共享内存中。// memcpy(m_shm+m_pos,&procinfo,sizeof(struct stprocinfo));memcpy(m_shm + m_pos, &procinfo, sizeof(struct stprocinfo));semlock.post();     //解锁 V操作while(true){printf("服务程序运行中...\n");//更新进程的心跳信息。sleep(25);m_shm[m_pos].atime=time(0);sleep(25);m_shm[m_pos].atime=time(0);}return 0;
}//程序退出和信号2、15的处理函数。
void EXIT(int sig)
{printf("sig=%d\n",sig);//从共享内存中删除当前进程的心跳信息。if(m_pos!=-1) memset(m_shm+m_pos,0,sizeof(struct stprocinfo));//把共享内存从当前进程分离。if(m_shm!=0) shmdt(m_shm);//这句是GPT4提示需要销毁的。“标记为”删除,我们的目的不需要启用。// 销毁共享内存// if (m_shmid != -1) shmctl(m_shmid, IPC_RMID, nullptr);exit(0);
}

四、一些细节

🔴第一个问题:当程序异常退出如kill -9 或 段错误,那么EXIT没机会执行,共享内存会残留(这(异常退出)是不可避免地)。因此在程序执行开始在共享内存寻找空位置的时候,共享内存中残留了进程的信息并且进程编号与当前进程相同,就重用这个旧位置而不再寻找共位置了!

🔴第二个问题:多个进程同时操作共享内存,会造成安全性的问题。需要对共享内存加锁(内存框架中的信号量csemp类)。程序对共享内存的操作由三个:1.在共享内存中寻找一个新的位置,把当前进程结构体写入共享内存 2.更新自己在共享内存的结构体的心跳时间。 3.从共享内存中删除自己。 通过分析可知只需要在1操作加锁即可,因为23操作只会操作自己那部分结构体,不会造成冲突!这里一定要搞明白。

🔴3.将heatbeat封装成进程心跳的类,便于在后续服务程序中使用。


http://www.mrgr.cn/news/41218.html

相关文章:

  • MySQL 实验1:Windows 环境下 MySQL5.5 安装与配置
  • 基于SSM的宿舍管理系统 宿舍管理平台 智慧宿舍管理系统 在线宿舍信息管理系统SSM开发 宿舍管理平台 宿舍信息管理 宿舍资源管理系统(源码+定制+文档)
  • MySQL基础篇 - 事务
  • 初识算法 · 双指针(2)
  • 基于Spring Boot+Vue的精品项目分享
  • 2024最新的软件测试面试大全(含答案+文档)
  • 详解Java中的Collection单列集合(从底层到用法超详细解析和细节分析)
  • vue框架和uniapp框架区别
  • 使用默认不可变的Rust变量会踩什么坑
  • C++平台跳跃游戏
  • 光缆的组成、结构、型号
  • python串口示波器(将串口数据接收与绘图分开)
  • s5pv210 -- 集合
  • 六.应用层
  • 命令行操作的基本指令【Linux】学完使用操作命令行就像喝汤一样快
  • 计算机网络:计算机网络体系结构 —— OSI 模型 与 TCP/IP 模型
  • PayPal轮询系统
  • 2024年9月30日--10月6日(ue5肉鸽结束)
  • python之数据类型详解
  • 开放式耳机的优缺点?哪个品牌专业?好用的开放式蓝牙耳机分享