动态多进程池实现NTP服务器

动态多进程池实现NTP服务器

服务端:server.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
#include <sys/mman.h>
#include "proto.h"

#define SIG_NOTIFY SIGUSR2   //用户自定义信号
#define MINIDLECOUNT 5     //最小空闲进程
#define MAXIDLECOUNT 10   //最大空闲进程
#define MAXCOUNT 20     //最大总进程数
#define BUFFSIZE 64

static int proc_idle_num =0;   //空闲进程统计
static int proc_busy_num =0;   //忙碌进程统计

enum proc_state   //进程状态
{
  IDLE =1,
  BUSY
};

struct proc_st   //进程结构体
{
  enum proc_state state;
  pid_t pid;
};

static struct proc_st *proc_pool;  //进程数组指针

static void usr2_handler(int s)   //信号处理函数
{
  return;
}

static int getpos(pid_t pid)   //通过PID号查找数组位置
{
  int i;

  for(i=0;i<MAXCOUNT;i++)
  {
    if(proc_pool[i].pid == pid)
      break;
  }

  return i;
}

static void server_loop(int lfd)   //子进程
{
  struct sockaddr_in raddr;
  socklen_t lenth = sizeof(raddr);
  int rfd;
  char buff[BUFFSIZE];
  int count,i;
  pid_t current_pid = getpid();
  pid_t current_ppid = getppid();

  while(1)
  {
    rfd = accept(lfd,(void *)&raddr,&lenth);   //等待客户端连接
    if(rfd <0)
    {
      perror("accept()");
      exit(1);
    }


    i = getpos(current_pid);   //获取当前进程在进程数组中的位置
    if(i>=MAXCOUNT)
    {
      fprintf(stderr,"getpos error\n");
      exit(1);
    }

    proc_pool[i].state = BUSY;   //设置当前进程状态
    kill(current_ppid,SIG_NOTIFY);  //给父进程发送信号

    count = snprintf(buff,BUFFSIZE,FMT,(long long)time(NULL));  //获取时间戳
    send(rfd,buff,count,0);  //发送给客户端
    sleep(5);  //测试
    close(rfd);  //关闭客户端

    proc_pool[i].state = IDLE;  //设置当前进程状态
    kill(current_ppid,SIG_NOTIFY);  //给父进程发送信号
  }

  exit(0);  //退出
}

static void pool_scan(void)  //循环查看进程池各进程状态
{
  int i=0;
  int idle=0,busy=0;

  for(i=0;i<MAXCOUNT;i++)
  {
    if(proc_pool[i].pid == -1)  //未使用
    {
      putchar(' ');
      continue;
    }
    if(proc_pool[i].pid>0 && proc_pool[i].state == IDLE)  //空闲
    {
      putchar('.');
      idle++;
    }
    else if(proc_pool[i].pid>0 && proc_pool[i].state == BUSY) //繁忙
    {
      putchar('x');
      busy++;
    }
    else  //未知错误
      _exit(1);
  }
  printf("\n");
  proc_idle_num = idle;   //更新全局空闲进程个数
  proc_busy_num = busy;  //更新全局繁忙进程个数
}

static int pool_add_one(int lfd)   //向进程池添加一个进程
{
  pid_t pid;
  int i;

  if(proc_idle_num + proc_busy_num >=MAXCOUNT)  //判断是否正确
    return -1;

  pid = fork();  //创建子进程
  if(pid<0)
  {
    perror("fork()");
    _exit(-1);
  }
  if(pid ==0)  //子进程
  {
    server_loop(lfd);
  }
  else  //父进程
  {
    for(i=0;i<MAXCOUNT;i++)
    {
      if(proc_pool[i].pid ==-1)
        break;
    }
    proc_pool[i].pid = pid;  //更新进程数组
    proc_pool[i].state = IDLE;
    kill(getpid(),SIG_NOTIFY);  //通知进程
  }

  return 0;
}

static int pool_del_one(void)  //销毁一个子进程
{
  int i;

  if(proc_idle_num + proc_busy_num >MAXCOUNT)
    return -1;

  for(i=0;i<MAXCOUNT;i++)
  {
    if(proc_pool[i].pid !=-1 && proc_pool[i].state == IDLE)
    {
      kill(proc_pool[i].pid,SIGTERM);   //发送结束信号
      proc_pool[i].pid = -1;
      kill(getpid(),SIG_NOTIFY);  //通知进程
      break;
    }
  }
  return 0;
}

int main()
{
  struct sigaction newact,oldact;
  sigset_t newset,oldset;
  int lfd,ret,i;
  struct sockaddr_in laddr;
  pid_t pid;

  proc_pool = mmap(NULL,sizeof(struct proc_st)*MAXCOUNT,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);   //mmap内存申请
  if(proc_pool == MAP_FAILED)
  {
    perror("mmap()");
    exit(1);
  }

  for(i=0;i<MAXCOUNT;i++)  //初始化进程池
    proc_pool[i].pid = -1;

  newact.sa_handler = SIG_IGN;   //屏蔽SIGCHLD信号不让子进程变为僵尸进程(退出时直接回收)
  sigemptyset(&newact.sa_mask);
  newact.sa_flags = SA_NOCLDWAIT;
  sigaction(SIGCHLD,&newact,&oldact);

  newact.sa_handler = usr2_handler;  //注册SIG_NOTIFY信号
  sigemptyset(&newact.sa_mask);
  newact.sa_flags = 0;
  sigaction(SIG_NOTIFY,&newact,&oldact);

  sigemptyset(&newset); 
  sigaddset(&newset,SIG_NOTIFY);
  sigprocmask(SIG_BLOCK,&newset,&oldset);  //添加SIG_NOTIFY信号到信号集

  lfd = socket(AF_INET,SOCK_STREAM,0); //创建socket
  if(lfd <0)
  {
    perror("socket()");
    exit(1);
  }

  laddr.sin_family = AF_INET;  //初始化结构体
  laddr.sin_port = htons(atoi(PORT));
  inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr.s_addr);
  ret = bind(lfd,(void *)&laddr,sizeof(laddr));  //绑定IP和端口
  if(ret <0)
  {
    perror("bind()");
    exit(1);
  }

  ret = listen(lfd,200);  //监听最大200个
  if(ret <0)
  {
    perror("listen()");
    exit(1);
  }

  for(i=0;i<MINIDLECOUNT;i++)  //创建指定个数的子进程
  {
    pid = fork();  //创建子进程
    if(pid <0)
    {
      perror("fork()");
      _exit(1);
    }
    else if(pid == 0)  //子进程
    {
      server_loop(lfd);
    }
    else  //父进程
    {
      proc_pool[i].state = IDLE;  //设置进程数组
      proc_pool[i].pid = pid;
      proc_idle_num++;
    }
  }

  pool_scan();  //查看进程数组状态

  while(1)
  {
    sigsuspend(&oldset);      //等待SIG_NOTIFY信号唤醒
    pool_scan();          //查看进程数组状态
    if(proc_idle_num<MINIDLECOUNT)  //小于最小空闲进程时
      pool_add_one(lfd);
    if(proc_idle_num>MAXIDLECOUNT)  //大于最大空闲进程时
      pool_del_one();
  }

  sigprocmask(SIG_SETMASK,&oldset,NULL);  //恢复setmask集合
  exit(0);
}

 其他参考:

通过TCP实现NTP服务器

posted @ 2022-08-12 15:34  *^VV^*  阅读(52)  评论(0编辑  收藏  举报