八_进程控制6+1 - 进程的消亡及释放资源 wait()waitpid()

博客探讨了在C语言中如何使用wait()和waitpid()函数来处理子进程结束后的善后工作,防止产生僵死进程。示例代码展示了在创建多个子进程后,如何通过wait()循环等待所有子进程结束,避免子进程成为僵死状态。添加wait()后,父进程能够正确清理子进程,使得系统资源得到释放。

上一节说道,父进程需要处理子进程结束后的善后工作,怎么善后呢? 就是 wait() / waitpid()

这里只是简单的使用 wait()

waitpid()以及各个判断终止状态宏的使用 还需要研究


NAME
wait, waitpid, waitid - wait for process to change state 等待进程更改状态

SYNOPSIS
#include <sys/types.h>
#include <sys/wait.h>

   pid_t wait(int *status);

   pid_t waitpid(pid_t pid, int *status, int options);

   int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);

RETURN VALUE
wait(): on success, returns the process ID of the terminated child; on error, -1 is returned.
成功返回 终止的子进程 的 进程ID ,失败返回-1

在这里插入图片描述
wait 使用:

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

#define LEFT 200
#define RIGHT 250

int main(void)
{
	int i,j,mark;
	pid_t pid;
	
	for(i = LEFT; i <= RIGHT; i++)
	{
		pid = fork();
		if(pid < 0)
		{
			fprintf(stderr,"fork() failed!\n");
			exit(1);
		}
		else if(pid == 0)//child
		{
			mark = 1;
			for(j = 2; j < i/2; j++)
			{
				if(i % j ==0)
					{
						mark = 0;
						break;
					}
			
			}

			if(mark)
				printf("%d is a primer\n",i);

			exit(0);//!!!
		}
		
	}
	sleep(1000);
	exit(0);
}

此时 201个子进程 是僵死进程 父进程为init

在这里插入图片描述

添加 wait() 善后处理后:

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

#define LEFT 200
#define RIGHT 250

int main(void)
{
	int i,j,mark;
	pid_t pid;
	
	for(i = LEFT; i <= RIGHT; i++)
	{
		pid = fork();
		if(pid < 0)
		{
			fprintf(stderr,"fork() failed!\n");
			exit(1);
		}
		else if(pid == 0)//child
		{
			mark = 1;
			for(j = 2; j < i/2; j++)
			{
				if(i % j ==0)
					{
						mark = 0;
						break;
					}
			
			}

			if(mark)
				printf("%d is a primer\n",i);

			exit(0);//!!!
		}
		
	}

	for(i = LEFT;i <= 250; i++)
		wait(NULL);

	sleep(1000);
	exit(0);
}

此时 ps axf 就没有了 变成僵死进程的子进程,只剩下一个父进程了。

<think> 我们正在分析一个名为 `plugins_call_handle_waitpid` 的函数。它的作用是在服务器进程中处理子进程结束事件(waitpid)时,调用所有已注册该事件的插件处理函数。 函数原型: handler_t plugins_call_handle_waitpid(server *srv, pid_t pid, int status) 参数: - srv: 服务器结构体指针 - pid: 结束的子进程进程ID - status: 子进程的退出状态 返回值: handler_t 枚举类型,可能是 HANDLER_GO_ON 或其他值,用于指示后续处理行为。 函数分析: 1. 计算插件函数槽的偏移量: const uint32_t offset = ((const uint16_t *)srv->plugin_slots)[PLUGIN_FUNC_HANDLE_WAITPID]; - `srv->plugin_slots` 是一个指针,指向一个数组,该数组存储了不同插件函数的偏移量。 - `PLUGIN_FUNC_HANDLE_WAITPID` 是一个枚举值,代表我们想要调用的“handle_waitpid”插件函数的索引。 - 这里通过索引获取到偏移量 `offset`。 2. 如果偏移量为0,表示没有插件注册该事件,直接返回 HANDLER_GO_ON。 if (0 == offset) return HANDLER_GO_ON; 3. 根据偏移量计算出实际存储插件函数信息的结构体数组的起始位置: const plugin_fn_waitpid_data *plfd = (const plugin_fn_waitpid_data *)(((uintptr_t)srv->plugin_slots) + offset); 注意:`srv->plugin_slots` 指向的内存区域是一个连续区域,前面部分存储各个插件的函数偏移量(索引表),后面部分存储具体的数据结构(如plugin_fn_waitpid_data数组)。 4. 遍历插件函数数组,并调用每个函数: handler_t rc = HANDLER_GO_ON; while (plfd->fn && (rc = plfd->fn(srv, plfd->data, pid, status)) == HANDLER_GO_ON) ++plfd; - 这个循环遍历一个以空指针(fn为NULL)结尾的数组。数组元素类型为 `plugin_fn_waitpid_data`,包含两个重要字段: 函数指针 `fn`:实际的处理函数。 数据指针 `data`:可能指向该插件相关的数据上下文。 - 循环条件: plfd->fn 不为空,并且调用该函数后的返回值 rc 等于 HANDLER_GO_ON。 - 调用函数:`plfd->fn(srv, plfd->data, pid, status)` - 如果函数返回 HANDLER_GO_ON,则继续调用下一个插件的函数(plfd++)。 - 如果函数返回其他值(如 HANDLER_FINISHED 或 HANDLER_ERROR),则停止遍历并返回该值。 5. 返回最后调用的函数的返回值。 设计解析: 这个函数的设计允许服务器在子进程退出时,依次调用多个插件注册的处理函数,直到某个插件处理函数返回非 HANDLER_GO_ON 的值,或者所有函数都被调用完毕。 这种机制类似于责任链模式,每个插件都有机会处理子进程退出事件,并且可以根据需要决定是否中断链式调用。 典型应用场景: - 插件可能创建了子进程(例如 CGI 进程),当子进程结束时,需要由插件来回收资源或记录日志。 - 多个插件可能都关心子进程退出事件,通过这个调用链,每个插件都可以得到通知。 注意:这个函数是同步调用的,即在主进程(或主线程)中按顺序调用每个注册的插件处理函数。 </think> ### `plugins_call_handle_waitpid` 函数解析(Lighttpd 子进程事件分发器) #### 核心作用: 这个函数是 **Lighttpd 的子进程状态事件分发中心**,当子进程终止或状态变更时,负责将事件通知给所有注册处理的插件。相当于服务器的"进程状态广播系统"。 --- ### 关键代码解析: ```c handler_t plugins_call_handle_waitpid(server *srv, pid_t pid, int status) { // 1. 查找插件注册位置 const uint32_t offset = ((const uint16_t *)srv->plugin_slots)[PLUGIN_FUNC_HANDLE_WAITPID]; // 2. 无注册插件时快速返回 if (0 == offset) return HANDLER_GO_ON; // 3. 定位插件处理函数数组 const plugin_fn_waitpid_data *plfd = (const plugin_fn_waitpid_data *) (((uintptr_t)srv->plugin_slots) + offset); // 4. 链式调用插件处理函数 handler_t rc = HANDLER_GO_ON; while (plfd->fn && (rc = plfd->fn(srv, plfd->data, pid, status)) == HANDLER_GO_ON) { ++plfd; // 移动到下一个插件处理函数 } return rc; } ``` --- ### 核心机制图解: ``` 子进程状态变更 → 内核通知Lighttpd → 本函数被调用 → 遍历插件链 → 执行各插件回调 ``` --- ### 四大关键步骤详解: #### 1. **插件定位机制** ```c const uint32_t offset = ... [PLUGIN_FUNC_HANDLE_WAITPID]; ``` - 🔍 **插件槽位表**:`srv->plugin_slots` 是插件注册表的内存指针 - 📍 **偏移计算**:`PLUGIN_FUNC_HANDLE_WAITPID` 是预定义枚举值,对应子进程处理类型 - ⚡ **性能优化**:直接内存偏移访问,避免哈希查找开销 #### 2. **短路返回优化** ```c if (0 == offset) return HANDLER_GO_ON; ``` - 🚦 **高效判断**:当没有插件注册此事件时立即返回 - 💡 避免无意义的遍历操作,提升服务器性能 #### 3. **插件函数数组定位** ```c const plugin_fn_waitpid_data *plfd = ... + offset; ``` - **数据结构**: ```c typedef struct { handler_t (*fn)(server*, void*, pid_t, int); // 处理函数指针 void *data; // 插件私有数据 } plugin_fn_waitpid_data; ``` - 📌 **内存布局**:通过偏移量直接定位到插件函数数组起始位置 #### 4. **链式调用引擎** ```c while (plfd->fn && (rc = plfd->fn(...)) == HANDLER_GO_ON) ++plfd; ``` - 🔄 **遍历逻辑**: 1. 检查当前插件函数指针非空 (`plfd->fn`) 2. 调用插件处理函数,传入: - `srv`:服务器核心结构 - `plfd->data`:插件私有上下文 - `pid`:状态变更的进程ID - `status`:进程退出状态码 3. 若返回 `HANDLER_GO_ON` 继续下一个插件 - **中断机制**:任一插件返回非 `HANDLER_GO_ON` 值即终止链式调用 --- ### 典型应用场景: 1. **CGI进程监控** ```python # mod_cgi 插件注册示例 plugin_slots[PLUGIN_FUNC_HANDLE_WAITPID] = { .fn = cgi_handle_waitpid, .data = cgi_context } ``` - 当CGI子进程退出时,回收资源并记录日志 2. **热更新管理** ```c // mod_hot_reload 插件 handler_t hot_reload_waitpid(...) { if (WIFEXITED(status)) reload_config(); return HANDLER_FINISHED; // 中断后续插件 } ``` 3. **资源泄漏防护** ```c // mod_resource_monitor 插件 handler_t monitor_waitpid(...) { log_memory_usage(pid, status); return HANDLER_GO_ON; // 允许其他插件继续处理 } ``` --- ### 设计亮点: 1. **零成本抽象** 通过直接内存访问避免函数指针表查找开销 2. **灵活中断机制** 插件可通过返回值控制事件传播: - `HANDLER_GO_ON`:继续传播 - `HANDLER_FINISHED`:终止传播 - `HANDLER_ERROR`:错误处理 3. **上下文隔离** 每个插件携带独立的 `data` 指针,避免全局状态污染 4. **高效遍历** 插件函数数组以 `NULL` 指针结尾,无需额外记录长度 --- ### 执行流程图解: ```mermaid graph TD A[子进程状态变更] --> B{有注册插件?} B -->|无| C[返回GO_ON] B -->|有| D[定位插件数组] D --> E[调用插件1] E --> F{返回GO_ON?} F -->|是| G[调用插件2] F -->|否| H[返回当前值] G --> F ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ma浩然

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值