动态多进程池实现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);
}
其他参考:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现