2026/4/6 5:51:37
网站建设
项目流程
网站建设个人建设,店群智能营销管理系统,如何仿别人网站的莫板,网站后台管理密码忘了一、引言
在上一篇文章中,我们实现了基本的多进程TCP服务器。但那个版本存在一个严重问题:子进程退出后没有被回收,会产生僵尸进程。本文将深入讲解如何使用信号机制优雅地回收子进程资源,并完善多进程服务器的实现。
二、多进程服务器的资源管理问题
2.1 进程复制机制回…一、引言在上一篇文章中,我们实现了基本的多进程TCP服务器。但那个版本存在一个严重问题:子进程退出后没有被回收,会产生僵尸进程。本文将深入讲解如何使用信号机制优雅地回收子进程资源,并完善多进程服务器的实现。二、多进程服务器的资源管理问题2.1 进程复制机制回顾当使用fork()创建子进程时,会发生以下复制:pid_tpid=fork();复制内容:✅用户区:堆区、栈区、全局数据区、代码区✅内核区:文件描述符表❌不复制:进程ID、父进程ID、进程状态关键特性:父子进程拥有独立的地址空间父进程修改数据不影响子进程文件描述符被复制但指向相同的内核对象2.2 僵尸进程问题问题描述:// 子进程退出exit(0);// 子进程变成僵尸进程(Z状态)子进程退出后,如果父进程不回收,会导致:进程表项被占用系统资源泄漏大量僵尸进程可能耗尽进程表查看僵尸进程:psaux|grepZ# 或ps-ef|grepdefunct2.3 回收时机冲突父进程需要同时处理两件事:while(1){intcfd=accept(lfd,...);// 阻塞等待新连接wait(NULL);// 阻塞等待子进程退出}问题:accept()和wait()都是阻塞函数,无法同时执行!解决方案:使用信号机制异步回收子进程三、信号机制回收子进程3.1 SIGCHLD信号原理当子进程状态改变(退出、停止、继续)时,内核会向父进程发送SIGCHLD信号(信号编号17)。工作流程:子进程退出 → 内核发送SIGCHLD → 父进程捕获信号 → 执行回调函数 → 回收子进程3.2 sigaction函数详解函数原型#includesignal.hintsigaction(intsignum,conststructsigaction*act,structsigaction*oldact);参数说明:signum:要捕获的信号编号(如SIGCHLD)act:新的信号处理方式oldact:保存旧的信号处理方式(通常传NULL)sigaction结构体structsigaction{void(*sa_handler)(int);// 信号处理函数(简单版)void(*sa_sigaction)(int,siginfo_t*,void*);// 信号处理函数(详细版)sigset_tsa_mask;// 信号屏蔽字intsa_flags;// 标志位void(*sa_restorer)(void);// 已废弃};关键成员:sa_handler:简单的信号处理函数sa_sigaction:带详细信息的处理函数(与sa_handler互斥)sa_flags:0:使用sa_handlerSA_SIGINFO:使用sa_sigactionsa_mask:信号处理期间要屏蔽的信号集3.3 信号处理函数实现#includesignal.h#includesys/wait.h// 信号处理回调函数voidsigchld_handler(intsignum){printf("捕获到信号: %d (SIGCHLD)\n",signum);// 循环回收所有退出的子进程pid_tpid;while((pid=waitpid(-1,NULL,WNOHANG))0){printf("成功回收子进程,PID = %d\n",pid);}}关键点:使用while循环:因为信号不支持排队,多个子进程同时退出只会收到一次信号waitpid(-1, NULL, WNOHANG):-1:等待任意子进程WNOHANG:非阻塞模式,没有子进程退出时立即返回03.4 注册信号处理voidregister_signal_handler(){structsigactionact;// 1. 设置信号处理函数act.sa_handler=sigchld_handler;// 2. 设置标志位(使用sa_handler)act.sa_flags=0;// 3. 清空信号屏蔽字(不屏蔽任何信号)sigemptyset(act.sa_mask);// 4. 注册信号处理if(sigaction(SIGCHLD,act,NULL)==-1){perror("sigaction error");exit(1);}printf("SIGCHLD信号处理已注册\n");}四、完整代码实现4.1 改进的服务器端代码#includestdio.h#includestdlib.h#includestring.h#includeunistd.h#includearpa/inet.h#includesignal.h#includesys/wait.h#definePORT9999#defineBUFFER_SIZE1024#defineMAX_LISTEN128// 信号处理函数:回收子进程voidsigchld_handler(intsignum){printf("\n[信号] 捕获到SIGCHLD信号 (编号: %d)\n",signum);pid_tpid;intcount=0;// 循环回收所有退出的子进程while((pid=waitpid(-1,NULL,WNOHANG))0){count++;printf("[回收] 成功回收子进程,PID = %d\n",pid);}if(count0){printf("[回收] 本次共回收 %d 个子进程\n",count);}}// 注册信号处理voidregister_signal_handler()