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

多进程服务器

深入理解并发服务器与进程管理:从僵尸进程到多任务处理

并发服务器的三种实现方式

现代服务器需要同时处理多个客户端请求,主要有三种实现方式:

  1. 多进程服务器:为每个客户端创建一个独立进程
  2. 多路复用服务器:使用单个进程通过事件驱动处理多个连接
  3. 多线程服务器:为每个客户端创建一个线程

僵尸进程

什么是僵尸进程?

当一个进程完成任务后没有被正确销毁,它就成了僵尸进程。就像完成工作的工人没有被妥善安置,仍在系统中占据着进程表项。

产生原因解析

进程正常终止需要满足以下条件之一:

  • 调用exit函数
  • main函数中执行return语句

终止时,进程会产生一个返回值,这个值需要传递给父进程。关键在于:操作系统不会主动传递返回值,而是等待父进程主动索取。如果父进程不索取,子进程就会成为僵尸进程。

消灭僵尸进程的方法

1. 使用wait函数
pid_t wait(int *status);

wait函数会:

  • 主动获取子进程返回值(存储在status指向的内存)
  • 如果没有终止的子进程,父进程将阻塞等待
  • 如果没有子进程也调用wait,进程会一直阻塞
2. 使用waitpid函数
pid_t waitpid(pid_t pid, int *status, int options);

waitpid提供了更多控制:

  • 可以指定等待特定子进程
  • 使用WNOHANG选项可非阻塞调用

信号机制:操作系统的"即时通讯"

当使用wait函数等待时,父进程会被阻塞。信号机制解决了这个问题。

signal函数:注册信号处理器

void (*signal(int sig, void (*func)(int)))(int);

这个看似复杂的声明实际上表示:

  • 参数1:信号类型(如SIGALRM、SIGINT、SIGCHLD)
  • 参数2:信号处理函数指针
  • 返回:之前的信号处理函数指针

alarm函数:定时信号

unsigned int alarm(unsigned int seconds);

设置一个定时器,指定时间后产生SIGALRM信号。特别说明:

  • 参数为0会取消之前的定时器
  • 如果没有注册处理函数,默认会终止进程

sigaction函数:更强大的信号处理

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

sigaction结构体包含:

  • sa_handler:基本信号处理函数
  • sa_sigaction:扩展的信号处理函数
  • sa_mask:处理期间要阻塞的信号集
  • sa_flags:控制信号行为的标志位

基于多任务的并发服务器实现

fork创建子进程

pid_t fork(void);

关键特性:

  • 复制父进程的文件描述符
  • 不复制套接字本身(套接字是系统级的)
  • 父子进程的文件描述符指向同一个套接字

正确的资源管理

重要原则:

  1. 子进程应关闭服务器套接字
  2. 父进程应关闭客户端套接字
  3. 只有当所有相关描述符都关闭,套接字才会真正销毁

分割TCP的I/O程序

在客户端程序中可以采用:

  • 父进程负责发送数据
  • 子进程负责接收数据

这样设计的优势:

  • 实现全双工通信
  • 任何时候都可以进行数据传输
  • 提高响应速度

实际应用示例

并发服务器框架

while(1) {// 接受新连接clnt_sock = accept(serv_sock, ...);// 创建子进程pid = fork();if(pid == 0) {  // 子进程close(serv_sock);  // 关闭服务器套接字handle_client(clnt_sock);close(clnt_sock);exit(0);  // 子进程退出}else {  // 父进程close(clnt_sock);  // 关闭客户端套接字// 注册SIGCHLD处理函数防止僵尸进程signal(SIGCHLD, handle_child_term);}
}

信号处理示例

void handle_child_term(int sig) {int status;pid_t pid = waitpid(-1, &status, WNOHANG);if(WIFEXITED(status))printf("Child %d terminated\n", pid);
}int main() {struct sigaction act;act.sa_handler = handle_child_term;sigemptyset(&act.sa_mask);act.sa_flags = 0;sigaction(SIGCHLD, &act, 0);// ...服务器代码...
}

总结

  1. 正确管理进程生命周期:及时处理僵尸进程
  2. 合理使用信号机制:避免阻塞,提高响应性
  3. 谨慎管理资源:特别是套接字描述符
  4. 考虑I/O分离:提升通信效率

通过理解这些核心概念,开发者可以构建出高效、稳定的并发服务器,能够同时服务大量客户端请求,同时保持系统的健壮性和可靠性。

http://www.dtcms.com/a/287515.html

相关文章:

  • 【愚公系列】《MIoT.VC》001-认识、安装 MIoT.VC 软件
  • 安装postgresql
  • 深度学习Depth Anything V2神经网络实现单目深度估计系统源码
  • OpenCV 官翻8 - 其他算法
  • warning: _close is not implemented and will always fail
  • 驾驭 Spring Boot 事件机制:8 个内置事件 + 自定义扩展实战
  • Custom SRP - Custom Render Pipeline
  • SurfaceView、TextureView、SurfaceTexture 和 GLSurfaceView
  • 立创EDA中双层PCB叠层分析
  • 原码、反码和补码在计算机中的运算规则有何不同?
  • 医疗AI与融合数据库的整合:挑战、架构与未来展望(上)
  • 小谈相机的学习过程
  • 软考 系统架构设计师系列知识点之杂项集萃(112)
  • jvm-sandbox-repeater 录制和回放
  • 基于深度学习的微表情识别算法研究
  • 智慧园区工程监控与工单管理系统需求文档
  • Go语言里的map
  • RocketMQ源码级实现原理-NameServer路由机制
  • 解锁C++性能密码:TCMalloc深度剖析
  • 低代码平台ToolJet实战总结
  • Java学习--------消息队列的重复消费、消失与顺序性的深度解析​
  • n8n教程分享,从Github读取.md文档内容
  • Redisson RLocalCachedMap 核心参详解
  • Astro:前端性能革命!从原生 HTML 到 Astro + React 的升级指南
  • Flutter基础(前端教程①⑤-API请求转化为模型列成列表展示实战)
  • 前端面试专栏-工程化:28.团队协作与版本控制(Git)
  • 运用KANO模型分析扫地机器人用户需求
  • LangGraph教程9:LangGraph检查点和Send机制
  • Linux 基础命令:文件和目录操作、文件内容查看、进程管理
  • 【嵌入式电机控制#16】电流环(三):过采样提高采集精度看门狗监测总线电压