4、调度模块

两个程序,一个是崩溃后再运行、一个是杀死超时程序。

调度进程

先让1号进程接管,然后由这个被1号进程接管的进程来负责fork子进程,并等待子进程退出。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
    // ./procct1 5 /bin/ls -lt /tmp/tmp.txt
    if (argc < 3)
    {
        printf("Using: ./procct1 timetv1 program argv ...\n");
        printf("Example:/project/tools1/bin/procct1 5 /bin/tar zcvf /tmp/tmp.tgz /usr/include\n\n");

        printf("本程序是服务程序的调度程序,周期性启动服务程序或shell脚本\n");
        printf("timetv1 运行周期,单位:秒  被调度的程序运行结束后,在timetv1秒后会被procct1重新启动\n");
        printf("program 被调度的程序名,必须使用完整路径\n");
        printf("argvs   被调度的程序的参数\n");
        printf("注意,本程序不会被kill杀死,但可以用kill -9强行杀死\n\n\n");
        return -1;
    }

    // 关闭所有信号和io
    for (int ii=0; ii<64; ii++)
    {
        signal(ii,SIG_IGN);
        close(ii);
    }

    // 生成子进程,父进程退出,让程序运行在后台,由一号进程接管
    if (fork() != 0) exit(0);

    // 启用SIGCHLD信号,让父进程可以wait子进程退出的状态(僵尸进程)
    signal(SIGCHLD,SIG_DFL);

    char *pargv[argc-1];
    for (int ii=2;ii<argc;ii++)
        pargv[ii-2] = argv[ii];

    pargv[argc-2] = NULL;

    while (true)
    {
        if (fork() == 0)
        {
            execv(argv[2], pargv);
            exit(0);    //execv调用失败被执行,不加死循环
        }
        else
        {
            int status;
            wait(&status);
            sleep(atoi(argv[1]));
        }
    }
    return 0;
}

共享内存超时进程

共享内存是进程间通信的一种方式,好处是可以在内存里存起来。掉电不保存。

#include "_public.h"

// 程序运行的日志。
CLogFile logfile;

int main(int argc,char *argv[])
{
  // 程序的帮助。
  if (argc != 2)
  {
    printf("\n");
    printf("Using:./checkproc logfilename\n");

    printf("Example:/project/tools1/bin/procctl 10 /project/tools1/bin/checkproc /tmp/log/checkproc.log\n\n");

    printf("本程序用于检查后台服务程序是否超时,如果已超时,就终止它。\n");
    printf("注意:\n");
    printf("  1)本程序由procctl启动,运行周期建议为10秒。\n");
    printf("  2)为了避免被普通用户误杀,本程序应该用root用户启动。\n");
    printf("  3)如果要停止本程序,只能用killall -9 终止。\n\n\n");

    return 0;
  }

  // 忽略全部的信号和IO,不希望程序被干扰。
  CloseIOAndSignal(true);

  // 打开日志文件。
  if (logfile.Open(argv[1],"a+")==false)
  { printf("logfile.Open(%s) failed.\n",argv[1]); return -1; }

  int shmid=0;

  // 创建/获取共享内存,键值为SHMKEYP,大小为MAXNUMP个st_procinfo结构体的大小。
  if ( (shmid = shmget((key_t)SHMKEYP, MAXNUMP*sizeof(struct st_procinfo), 0666|IPC_CREAT)) == -1)
  {
    logfile.Write("创建/获取共享内存(%x)失败。\n",SHMKEYP); return false;
  }

  // 将共享内存连接到当前进程的地址空间。
  struct st_procinfo *shm=(struct st_procinfo *)shmat(shmid, 0, 0);

  // 遍历共享内存中全部的记录。
  for (int ii=0;ii<MAXNUMP;ii++)
  {
    // 如果记录的pid==0,表示空记录,continue;
    if (shm[ii].pid==0) continue;

    // 如果记录的pid!=0,表示是服务程序的心跳记录。

    // 程序稳定运行后,以下两行代码可以注释掉。
    //logfile.Write("ii=%d,pid=%d,pname=%s,timeout=%d,atime=%d\n",\
    //               ii,shm[ii].pid,shm[ii].pname,shm[ii].timeout,shm[ii].atime);

    // 向进程发送信号0,判断它是否还存在,如果不存在,从共享内存中删除该记录,continue;
    int iret=kill(shm[ii].pid,0);
    if (iret==-1)
    {
      logfile.Write("进程pid=%d(%s)已经不存在。\n",(shm+ii)->pid,(shm+ii)->pname);
      memset(shm+ii,0,sizeof(struct st_procinfo)); // 从共享内存中删除该记录。
      continue;
    }

    time_t now=time(0);   // 取当前时间。

    // 如果进程未超时,continue;
    if (now-shm[ii].atime<shm[ii].timeout) continue;

    // 如果已超时。
    logfile.Write("进程pid=%d(%s)已经超时。\n",(shm+ii)->pid,(shm+ii)->pname);

    // 发送信号15,尝试正常终止进程。
    kill(shm[ii].pid,15);     

    // 每隔1秒判断一次进程是否存在,累计5秒,一般来说,5秒的时间足够让进程退出。
    for (int jj=0;jj<5;jj++)
    {
      sleep(1);
      iret=kill(shm[ii].pid,0);     // 向进程发送信号0,判断它是否还存在。
      if (iret==-1) break;     // 进程已退出。
    } 

    // 如果进程仍存在,就发送信号9,强制终止它。
    if (iret==-1)
      logfile.Write("进程pid=%d(%s)已经正常终止。\n",(shm+ii)->pid,(shm+ii)->pname);
    else
    {
      kill(shm[ii].pid,9);  // 如果进程仍存在,就发送信号9,强制终止它。
      logfile.Write("进程pid=%d(%s)已经强制终止。\n",(shm+ii)->pid,(shm+ii)->pname);
    }

    // 从共享内存中删除已超时进程的心跳记录。
    memset(shm+ii,0,sizeof(struct st_procinfo)); // 从共享内存中删除该记录。
  }

  // 把共享内存从当前进程中分离。
  shmdt(shm);

  return 0;
}

服务器上的共享内存有 MAXNUMP 个记录进程的空间。每执行一次check程序会遍历这个数组,遇到有记录的,先发送0确定进程在运行,再判断是否超时、如果超时:先发送15信号正常终止、再循环发送5次0、如果还没有结束发送9。

#include "_public.h"

// 程序运行的日志。
CLogFile logfile;

int main(int argc,char *argv[])
{
  // 程序的帮助。
  if (argc != 2)
  {
    printf("\n");
    printf("Using:./checkproc logfilename\n");

    printf("Example:/project/tools1/bin/procctl 10 /project/tools1/bin/checkproc /tmp/log/checkproc.log\n\n");

    printf("本程序用于检查后台服务程序是否超时,如果已超时,就终止它。\n");
    printf("注意:\n");
    printf("  1)本程序由procctl启动,运行周期建议为10秒。\n");
    printf("  2)为了避免被普通用户误杀,本程序应该用root用户启动。\n");
    printf("  3)如果要停止本程序,只能用killall -9 终止。\n\n\n");

    return 0;
  }

  // 忽略全部的信号和IO,不希望程序被干扰。
  CloseIOAndSignal(true);

  // 打开日志文件。
  if (logfile.Open(argv[1],"a+")==false)
  { printf("logfile.Open(%s) failed.\n",argv[1]); return -1; }

  int shmid=0;

  // 创建/获取共享内存,键值为SHMKEYP,大小为MAXNUMP个st_procinfo结构体的大小。
  if ( (shmid = shmget((key_t)SHMKEYP, MAXNUMP*sizeof(struct st_procinfo), 0666|IPC_CREAT)) == -1)
  {
    logfile.Write("创建/获取共享内存(%x)失败。\n",SHMKEYP); return false;
  }

  // 将共享内存连接到当前进程的地址空间。
  struct st_procinfo *shm=(struct st_procinfo *)shmat(shmid, 0, 0);

  // 遍历共享内存中全部的记录。
  for (int ii=0;ii<MAXNUMP;ii++)
  {
    // 如果记录的pid==0,表示空记录,continue;
    if (shm[ii].pid==0) continue;

    // 如果记录的pid!=0,表示是服务程序的心跳记录。

    // 程序稳定运行后,以下两行代码可以注释掉。
    //logfile.Write("ii=%d,pid=%d,pname=%s,timeout=%d,atime=%d\n",\
    //               ii,shm[ii].pid,shm[ii].pname,shm[ii].timeout,shm[ii].atime);

    // 向进程发送信号0,判断它是否还存在,如果不存在,从共享内存中删除该记录,continue;
    int iret=kill(shm[ii].pid,0);
    if (iret==-1)
    {
      logfile.Write("进程pid=%d(%s)已经不存在。\n",(shm+ii)->pid,(shm+ii)->pname);
      memset(shm+ii,0,sizeof(struct st_procinfo)); // 从共享内存中删除该记录。
      continue;
    }

    time_t now=time(0);   // 取当前时间。

    // 如果进程未超时,continue;
    if (now-shm[ii].atime<shm[ii].timeout) continue;

    // 如果已超时。
    logfile.Write("进程pid=%d(%s)已经超时。\n",(shm+ii)->pid,(shm+ii)->pname);

    // 发送信号15,尝试正常终止进程。
    kill(shm[ii].pid,15);

    // 每隔1秒判断一次进程是否存在,累计5秒,一般来说,5秒的时间足够让进程退出。
    for (int jj=0;jj<5;jj++)
    {
      sleep(1);
      iret=kill(shm[ii].pid,0);     // 向进程发送信号0,判断它是否还存在。
      if (iret==-1) break;     // 进程已退出。
    } 

    // 如果进程仍存在,就发送信号9,强制终止它。
    if (iret==-1)
      logfile.Write("进程pid=%d(%s)已经正常终止。\n",(shm+ii)->pid,(shm+ii)->pname);
    else
    {
      kill(shm[ii].pid,9);  // 如果进程仍存在,就发送信号9,强制终止它。
      logfile.Write("进程pid=%d(%s)已经强制终止。\n",(shm+ii)->pid,(shm+ii)->pname);
    }

    // 从共享内存中删除已超时进程的心跳记录。
    memset(shm+ii,0,sizeof(struct st_procinfo)); // 从共享内存中删除该记录。
  }

  // 把共享内存从当前进程中分离。
  shmdt(shm);

  return 0;
}

如何往共享内存里面写进程信息?
涉及到信号量加锁。锁之间的流程是:先找有没有已经存了该进程号的,没有的话定位到第一个空位置。然后将当前进程的心跳信息存入共享内存的进程组中。

如何加锁解锁?

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇