Linux socket编程(4):服务端fork之僵尸进程的处理

在上一节利用fork实现服务端与多个客户端建立连接中,我们使用fork函数来实现服务端既可以accept新的客户端连接请求,又可以接收已连接上的客户端发来的消息。但在Linux中,在子进程终止后,父进程需要处理该子进程的终止状态,否则子进程将成为僵尸进程,本节就来探讨一下僵尸进程的处理。

文章目录

  • 1 什么是僵尸进程
  • 2 回收僵尸进程
    • 2.1 SIG_IGN忽略
    • 2.2 wait和waitpid
      • 2.2.1 wait
      • 2.2.1 waitpid

1 什么是僵尸进程

僵尸进程(Zombie Process)是操作系统中的一种特殊进程状态,它通常出现在一个子进程终止,但其父进程尚未能够处理该子进程的终止状态。

  1. 特点

    僵尸进程不执行任何代码,它们仅仅是一个进程描述符和一些状态信息,如退出状态码,占用少量系统资源。如果大量的僵尸进程积累,可能会导致系统资源耗尽。

  2. 解决方法

    • 当子进程终止时,父进程可以使用wait()waitpid()等系统调用来等待子进程的退出状态信息,从而释放子进程的资源,同时告知操作系统可以回收子进程的进程表项
    • 另一种方法是使用信号处理程序,在父进程中注册SIGCHLD信号处理程序来处理子进程的退出状态

在上一篇文章的例子中,如果在客户端的进程终止后,服务端没有回收子进程的话,将产生一个僵尸进程。

在这里插入图片描述

我们可以使用top指令来看系统中现在有多少个僵尸进程:

在这里插入图片描述

我们还可以使用ps -aux |grep Z来查看具体的僵尸进程的信息:

在这里插入图片描述

图中STAT(状态)为Z+(Zombie)的即为僵尸进程。

2 回收僵尸进程

2.1 SIG_IGN忽略

最简单的,我们可以使用SIG_IGN来忽略SIGCHLD信号,这样内核会在子进程终止时立即将其资源释放,而不需要父进程调用waitwaitpid来获取子进程的终止状态以释放资源。

在这里插入图片描述

可以看到此时是没有产生僵尸进程的:

在这里插入图片描述

2.2 wait和waitpid

使用signal(SIGCHLD, SIG_IGN);的方式处理僵尸进程有一些局限性和潜在的问题:

  1. 父进程无法得知子进程是正常退出还是异常终止,以及子进程的退出状态是什么。
  2. 父进程无法正确处理每个子进程的终止状态。

如果需要掌握子进程退出的情况,建议注册信号回调函数,然后使用waitwaitpid来处理僵尸进程。

2.2.1 wait

如下图所示,可以使用wait函数来回收子进程的资源。

在这里插入图片描述

运行程序,创建一个客户端然后关闭,再创建一个客户端然后再关闭,结果如下:

在这里插入图片描述

可以看到服务端正常地回收了资源,此时使用top查看也是没有僵尸进程的。

但是在多个客户端同时关闭的情况下,wait会产生问题

我们现在对客户端的代码做出如下修改:

在这里插入图片描述

现在来看一下这10个套接字同时退出后会发生什么:

在这里插入图片描述

可以看到我们注册的SIGCHLD回调函数只被触发了4次,也就是说只有4个子进程的资源被回收了。此时用top查看僵尸进程的数量,果然还有6个:

在这里插入图片描述

实际上也好理解,这些套接字在非常短的时间间隔内同时关闭,对于Linux的内核来看,应该是有一个进程专门用来处理这些信号,在上一个信号还在处理的同时又来了多个信号,那么下次OS只会响应一个信号,而不会调用多次回调函数,然后调用一次wait就回收一个子进程。

所以如果我们多次测试,可以发现每次被回收的进程的数量都是不同的,这和OS内部的任务调度有关,但基本上不可能10个全部回收。

那我们是否可以在sigchld_handler中调用while循环无限地wait来解决这个问题呢?

答案是否定的。因为 wait 是一个阻塞调用,会导致信号处理函数阻塞,而信号处理函数的处理应该尽量迅速。

2.2.1 waitpid

这时我们就可以使用waitpid函数:

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

其中第三个参数options的常用值如下(可以使用按位或运算符|组合多个选项):

  • WNOHANG:在没有终止的子进程时立即返回,不阻塞。如果指定了这个选项,waitpid 将立即返回,不会等待子进程终止。
  • WUNTRACED:也等待已停止的子进程的状态。
  • WCONTINUED:也等待被停止的子进程被继续的状态。

所以我们只要在信号处理回调函数中使用waitpid(-1, &status, WNOHANG)即可避免前面回收资源不完全的情况。

void sigchld_handler(int signo) {pid_t pid;int status;// 在信号处理函数中循环调用waitpid以获取所有子进程的终止状态,其中-1表示等待任意子进程while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {printf("Child process %d exited with status %d\n", pid, WEXITSTATUS(status));}
}

结果如下:

在这里插入图片描述
这样就回收了所有的僵尸进程的资源。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/153878.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ChatGLM3-6B安装

1 CentOS7 安装Python 10 1.1 安装基础环境 yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel libffi-devel1.2 升级 OpenSSL 不升级 OpenSSL,pip instal…

【开源】基于Vue.js的车险自助理赔系统的设计和实现

项目编号: S 018 ,文末获取源码。 \color{red}{项目编号:S018,文末获取源码。} 项目编号:S018,文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 角色管理模块2.3 车…

[GFCTF 2021]wordy 编写去花IDAPYTHON

首先查壳 发现没有东西 然后放入ida 发现没有main并且软件混乱 发现这里1144的地方 出错 IDA无法识别数据 报错内容是EBFF 机器码 这里看了wp知道是很常见的花指令 所以我们现在开始去花 这里因为我们需要取出 EBFF 下面的地址也都是 EBFF 所以工作量大 使用IDApython脚本即…

EasyRecovery2024最新永久破解版本安装包下载

当我们处理重要的文件数据时,遇到突然停电导致数据来不及保存,再次打开电脑后,此前处理的数据可能丢失,这无疑会影响我们的工作进度,数据恢复软件在此时就派上用场,那么下面就来具体介绍EasyRecovery软件的…

FL Studio21怎么破解?2024年最新FLStudio21.2.0安装解锁特别版下载使用图文教程

用FL Studio编曲,让音乐成为你的翅膀,飞翔在无尽的创作海洋中吧! FL Studio作为一款功能强大且备受赞誉的音乐制作软件,为你提供了一个独特的创作平台。通过FL Studio,你可以自由地创作、编曲,制作属于自己…

4.6 Windows驱动开发:内核遍历进程VAD结构体

在上一篇文章《内核中实现Dump进程转储》中我们实现了ARK工具的转存功能,本篇文章继续以内存为出发点介绍VAD结构,该结构的全程是Virtual Address Descriptor即虚拟地址描述符,VAD是一个AVL自平衡二叉树,树的每一个节点代表一段虚…

API测试:了解API接口测试与API接口测试指南

本文为您提供API接口测试的全面指南。从基础概念到测试方法,再到最佳实践,本文将帮助您深入了解API测试,确保您的API稳健、高效。 一、API测试的基础概念 API(应用程序编程接口)是不同软件应用程序之间的通信桥梁。A…

【算法】区间调度算法

目录 1.概述2.代码实现3.应用 1.概述 (1)区间调度算法 (Interval Scheduling Algorithm) 是一种在给定的一组任务中,选择尽可能多的相互不冲突的任务的算法。在这个问题中,每个任务都有一个开始时间和结束时间。两个任务是相互冲…

rosnode ping指令

节点是一个可执行文件,它可以通过ROS来与其他节点进行通信(这些节点可以分布在不同设备上面的软件直接,例如一台在Windows上面,一台在Ubuntu,一台在stm32上面) 节点是ROS中非常重要的一个概念,为了帮助初学者理解这个…

springboot中动态api如何设置

1.不需要编写controller 等mvc层,通过接口动态生成api。 这个问题,其实很好解决,以前编写接口,是要写controller,需要有 RestController RequestMapping("/test1") public class xxxController{ ApiOperat…

敏捷DevOps专家王立杰:端到端DevOps持续交付的5P法则 | IDCF

今天有一个流行的英文缩写词用来刻画这个风云变幻的时代:VUCA(乌卡时代)。四个英文字母分别表示动荡性(Volatility)、不确定性(Uncertainty)、复杂性(Complexity)和模糊性…

JavaScript-如何使用变量

更多内容,请访问: 声明和定义区别 JavaScript-变量类型 JavaScript-变量类型判断 JavaScript-undefined和null区别 变量定义 变量的定义,可以通过var、const、let来定义,let和const是ES6新特性,let和const是解决var…

C编译流程

1.预处理 hello.c 经过预处理得到 hello.i gcc -E hello.c -o hello.i -E的含义:说明这是一个预处理操作 生成预处理文件(.i) 预处理阶段做了什么事: 1.1 头文件展开 我们发现 原先只有几行的hello.c变成了上千行的hello.i 实际上 预处理完成的是 将头…

Unity中Shader反射环境

文章目录 前言一、制作反射环境的两种办法法1:属性面板接收一个 Cubemap 作为反射环境法2:把环境烘焙成一张Cubemap 二、在Unity中实现把环境烘焙成一张Cubemap1、先创建一个反射探针(可以直接创建,也可以空物体增加组件&#xff…

【Linux】:进程间通信和日志模拟

进程间通信 一.基本概念二.简单的通信-管道(匿名管道)1.建立通信信道2.通信接口 三.命名管道三.模拟命名管道通信(加上日志)1.完整代码2.基本使用 一.基本概念 是什么 两个或多个进程实现数据层面的交互。 因为进程独立性的存在,导致进程间…

广度优先算法(BFS)

广度优先算法(Breadth-First Search)是在图和树领域的搜索方法,其核心思想是从一个起始点开始,访问其所有的临近节点,然后再按照相同的方式访问这些临近节点的节点,这种访问方式类似涟漪泛起,一…

HINSTANCE是什么?

HINSTANCE 就是 HMODULE:

队列OJ--循环队列

目录 题目链接:622. 设计循环队列 - 力扣(LeetCode)​​​​​ 题解: ​编辑 代码实现: 完整代码: 题目链接:622. 设计循环队列 - 力扣(LeetCode)​​​​​ 题解&#x…

python 对比数据库,生成sql

import pymysqldef get_table_structure(connection, cursor, database, table):# 获取表结构query f"SHOW COLUMNS FROM {database}.{table}"cursor.execute(query)return cursor.fetchall()def generate_create_table(cursor, table_structure, database, table):…

jenkins传参给robotframework

在做自动化的时候,需要使用jenkins传参给rf,rf根据传来的变量运行,在将变量传递给py脚本文件。特此记录。 一、配置jenkins 构建的命令使用如下格式即可(注意空格): cd D:\xxx\test call pybot --variabl…