【TCP/IP】多进程服务器的实现(进阶) - 进程和僵尸进程

news/2024/5/18 16:09:00 标签: 服务器, tcp/ip, 网络, 网络协议, udp

目录

僵尸(Zombie)进程

僵尸进程的产生机制

僵尸进程的危害

僵尸进程的销毁

wait函数

waitpid函数


        进程管理在网络编程中十分重要,如果未处理好,将会导致出现“僵尸进程”,进而影响服务器端对进程的管控。

僵尸(Zombie)进程

        第一次听到这个名词大家可能会有些陌生,为什么叫“僵尸”进程?事实上,这个词用的很形象也很恰当:当一个进程使用fork创建子进程后,若出现子进程先于父进程结束,并且此时父进程没有对子进程的资源进行释放回收,那么这个子进程将成为一个“僵尸进程”,并始终占据着一个进程号。

僵尸进程的产生机制

        通过使用fork函数可以制造一些“僵尸进程”。根据之前的定义,让我们用代码来引出僵尸进程。

zombie.cpp

#include <stdio.h>
#include <unistd.h>

#define CIRCLE 40

int main(int argc, char *argv[])
{
    pid_t pid = fork();

    if (pid == 0) // 子进程
    {
        printf("I'm child process\n");
    }
    else
    {
        printf("Child Process ID: %d \n", pid);
        sleep(CIRCLE); // 休眠20s
    }

    pid == 0 ? printf("Child process end")
             : printf("Parent process end");

    return 0;
}

 运行结果(注意要编译出来运行哦~一些IDE会默认对僵尸进程进行处理):

        通过 ps au 指令,我们可以查看所有当前进程的具体信息。不难发现,ID为10476的进程被标记为僵尸进程(Z+),而经过40秒的等待后,子进程和父进程同时被销毁,僵尸进程消失。

补充:

用终端打开程序时,我们可以用 & 符号将窗口中输入的指令放到后台去运行。

比如我们编译好的zombie程序,输入 ./zombie & 后程序开始执行,继续输入 ps au,可以在同一个终端下查看进程的变化。

僵尸进程的危害

        出现少量的僵尸进程并不会对操作系统造成太大影响,但如果数量多了,那么操作系统将可能因为没有可用的进程号(僵尸进程也占用进程号)而导致系统无法创建新的进程。

僵尸进程的销毁

        在这之前,我们应晓得如何使程序得到结束,具体方式有以下几种:

  • 传递参数并调用exit函数
  • main函数中执行return语句并返回值

        为了正确销毁子进程,父进程应主动请求获取子进程的返回值。在<sys/wait.h>库中,有2个方法可以提供给我们帮助。

wait函数

#include <sys/wait.h>

pid_t wait(int * statloc);

//成功时退回终止的子进程 ID, 失败时返回-1。

        调用此函数时如果已有子进程终止 ,那么子进程终止时传递的返回值(exit函数的参数值、main函数的return返回值)将保存到该函数的参数所指内存空间内。但函数参数指向的单元中还包含其他信息,因此需要通过以下宏进行分离:

  • WIFEXITED(整数型变量):子进程正常终止时,返回true
  • WEXITSTATUS(整数型变量):获取子进程的的返回值

如何使用呢?请看以下例子:

wait.cpp

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

#define CIRCLE 40

int main(int argc, char *argv[])
{
    int status;

    pid_t pid = fork(); //这里创建的子进程将在通过return语句终止

    if (pid == 0) // 子进程进入
    {
        return 111;
    }
    else
    {
        printf("Child process ID: %d \n", pid);
        pid = fork(); //这里创建的进程将通过下面的exit()函数终止
        if (pid == 0)
        {
            exit(222); //exit中所填常量即返回值
        }
        else
        {
            printf("Child process ID: %d \n", pid);
            //之前终止的子进程信息将保存在status变量中,同时相关子进程被销毁
            wait(&status); 
            
            //通过WIFEXITED验证子进程是否正常终止。
            //如果正常退出,则调用WEXITSTATUS输出子进程的返回值。
            if (WIFEXITED(status)) 
            {
                printf("Child 1 return: %d \n", WEXITSTATUS(status));
            }
            //再次调用wait函数和宏,以处理另一个子进程
            wait(&status);
            if (WIFEXITED(status))
            {
                printf("Child 2 return: %d \n", WEXITSTATUS(status));
            }

            sleep(CIRCLE); //让父进程休眠,以便验证查看
        }
    }
    return 0;
}

运行结果: 

        但是请大家注意,利用wait来销毁僵尸进程时,如果没有己终止的子进程,那么程序将会一直阻塞(Blocking),直到有子进程终止才能继续下一步操作。

waitpid函数

        wait函数会引起程序阻塞,那么有没有其他方法能够解决这个问题呢?当然有,那就是wait函数的改进版——waitpid。这个方法能够避免阻塞的同时控制僵尸进程。

#include <sys/wait.h>

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

//成功时返回终止的子进程ID,失败时返回-1

/*参数说明*/

// pid: 等待终止的目标子进程的ID,若传递-1,则与wait函数相同,等待任意子进程终止
// statloc: 反应状态的变量 
// options: 传递头文件sys/wait.h中声明的常量WNOHANG(值为1),即使没有终止的子进程也不会进入阻塞状态,而是返回O并退出函数

测试用例:

waitpid.cpp

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

int main(int argc, char *argv[])
{
    int status;
    pid_t pid = fork();

    if (pid == 0)
    {
        sleep(10); //延迟子进程10s
        return 222;
    }
    else
    {
        while (!waitpid(-1, &status, WNOHANG)) //若之前没有终止的子进程将返回0结束循环
        {
            sleep(2);
            printf("sleep 2s");
        }

        if (WIFEXITED(status))
        {
            printf("Child return %d \n", WEXITSTATUS(status));
        }
    }
    return 0;
}

        运行结果: 

        sleep(2)函数被调用了5次,共计延迟 2*5 =10 s,验证了waitpid在没有发生阻塞的同时销毁了可能出现的僵尸进程。


http://www.niftyadmin.cn/n/413357.html

相关文章

chatgpt赋能python:Python中的中文分词神器——jieba

Python中的中文分词神器——jieba 介绍 如果你曾经在处理中文文本时&#xff0c;也许会遇到中文分词的需求&#xff0c;jieba就是一款不可错过的工具。 jieba 是目前最好的 Python 中文分词库&#xff0c;它具有高效、简单和可定制等优点&#xff0c;适合各种规模的文本分词…

chatgpt赋能python:Python怎么下jieba库

Python怎么下jieba库 Python是目前最流行的动态编程语言之一&#xff0c;广泛应用于Web开发、数据分析、人工智能等领域。对于中文文本处理来说&#xff0c;jieba库是一款非常实用的工具。本文将介绍如何下载jieba库&#xff0c;并探讨其在中文分词、情感分析等方面的应用。 …

数据结构-Redis(三)

前面介绍了redis的String和哈希&#xff0c;接下来看看其他的数据结构 List LPUSH&#xff1a;左边放入 RPUSH&#xff1a;右边放入 LPOP&#xff1a;取出左边第一个数&#xff0c;并且移除 RPOP&#xff1a;取出右边第一个数&#xff0c;并且移除 由上操作可以看出&#…

为什么我们在ANGULARJS中使用$ ROOTSCOPE $BROADCAST?

为什么我们在ANGULARJS中使用$ ROOTSCOPE $BROADCAST&#xff1f; 简介 试图findAngularJS $rootScope.$broadcast一些基本信息&#xff0c;但是AngularJS文档没有什么帮助。 简单地说&#xff0c;为什么我们使用这个&#xff1f; 此外&#xff0c;在John Papa的Hot Towel模板…

Hutool工具类之发送邮件

文章目录 一、引入依赖二、邮件服务器配置三、发送邮件1、发送普通文本邮件&#xff0c;最后一个参数可选是否添加多个附件2、发送HTML格式的邮件并附带附件&#xff0c;最后一个参数可选是否添加多个附件3、群发邮件&#xff0c;可选HTML或普通文本&#xff0c;可选多个附件 一…

python-面向对象:三大特性高级特性

文章目录 前言一、面向对象三大特性&#xff1a;封装、继承、多态1.对象和类2.封装3.继承(1)重写父类方法(2)多继承(3)私有属性与私有方法 4.多态 二、三大特性的应用1.链表的封装2.栈的封装3.队列的封装4.二叉树的封装 三、高级特性1.类属性与实例属性2.类方法与静态方法3.pro…

「HTML和CSS入门指南」article 标签详解

什么是 article 标签? 在 HTML 中,article 标签用于表示独立的、完整的文章或内容块。通常用于包含博客文章、新闻故事、论坛帖子或其他独立的信息块。 article 标签的基本语法 以下是 article 标签的基本语法: <article><!-- 在这里放置您的内容 --> </ar…

【Leetcode -2181.合并零之间的节点- 2326.螺旋矩阵Ⅳ】

Leetcode Leetcode -2181.合并零之间的节点Leetcode -2326.螺旋矩阵Ⅳ Leetcode -2181.合并零之间的节点 题目&#xff1a;给你一个链表的头节点 head &#xff0c;该链表包含由 0 分隔开的一连串整数。链表的 开端 和 末尾 的节点都满足 Node.val 0 。 对于每两个相邻的 0 &…