【TCP/IP】多进程服务器的实现(进阶) - 僵尸进程及wait、waitpid函数

这篇具有很好参考价值的文章主要介绍了【TCP/IP】多进程服务器的实现(进阶) - 僵尸进程及wait、waitpid函数。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

僵尸(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会默认对僵尸进程进行处理):

【TCP/IP】多进程服务器的实现(进阶) - 僵尸进程及wait、waitpid函数

【TCP/IP】多进程服务器的实现(进阶) - 僵尸进程及wait、waitpid函数

        通过 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;
}

运行结果: 

【TCP/IP】多进程服务器的实现(进阶) - 僵尸进程及wait、waitpid函数

        但是请大家注意,利用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;
}

运行结果: 

【TCP/IP】多进程服务器的实现(进阶) - 僵尸进程及wait、waitpid函数

        sleep(2)函数被调用了5次,共计延迟 2*5 =10 s,验证了waitpid在没有发生阻塞的同时销毁了可能出现的僵尸进程。文章来源地址https://www.toymoban.com/news/detail-487618.html

到了这里,关于【TCP/IP】多进程服务器的实现(进阶) - 僵尸进程及wait、waitpid函数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于linux下的高并发服务器开发(第二章)- 2.7 进程退出、孤儿进程、僵尸进程

    ◼ 父进程运行结束,但子进程还在运行(未运行结束),这样的子进程就称为孤儿进程 (Orphan Process)。 ◼ 每当出现一个孤儿进程的时候,内核就把孤儿进程的父进程设置为 init ,而 init 进程会循环地 wait() 它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束 了其生

    2024年02月16日
    浏览(52)
  • TCP服务器实现—多进程版,多线程版,线程池版

    目录 前言 1.存在的问题 2.多进程版 3.多线程版 4.线程池版 总结         在上一篇文章中使用TCP协议实现了一个简单的服务器,可以用来服务端和客户端通信,但是之前的服务器存在一个问题,就是当有多个客户端连接服务器的时候,服务器只能和一个客户端通信,其它的客

    2024年02月12日
    浏览(46)
  • [Linux] 网络编程 - 初见TCP套接字编程: 实现简单的单进程、多进程、多线程、线程池tcp服务器

    网络的上一篇文章, 我们介绍了网络变成的一些重要的概念, 以及 UDP套接字的编程演示. 还实现了一个简单更简陋的UDP公共聊天室. [Linux] 网络编程 - 初见UDP套接字编程: 网络编程部分相关概念、TCP、UDP协议基本特点、网络字节序、socket接口使用、简单的UDP网络及聊天室实现…

    2024年02月16日
    浏览(64)
  • 多进程并发TCP服务器模型(含客户端)(网络编程 C语言实现)

    摘要 :大家都知道不同pc间的通信需要用到套接字sockte来实现,但是服务器一次只能收到一个客户端发来的消息,所以为了能让服务器可以接收多个客户端的连接与消息的传递,我们就引入了多进程并发这样一个概念。听名字就可以知道--需要用到进程,当然也有多线程并发

    2024年02月17日
    浏览(60)
  • 网络通信(13)-C#TCP服务器和客户端同时在一个进程实现的实例

    有时项目需求中需要服务器和客户端同时在一个进程实现,一边需要现场接收多个客户端的数据,一边需要将数据汇总后发送给远程服务器。下面通过实例演示此项需求。 C#TCP服务器和客户端同时在一个进程实现的实例如下: 界面设计 UI文件代码

    2024年01月22日
    浏览(62)
  • Socket网络编程(TCP/IP)实现服务器/客户端通信。

    一.前言 回顾之前进程间通信(无名管道,有名管道,消息队列,共享内存,信号,信号量),都是在同一主机由内核来完成的通信。 那不同主机间该怎么通信呢? 可以使用Socket编程来实现。 Socket编程可以通过网络来实现实现不同主机之间的通讯。 二.Socket编程的网络模型如

    2024年02月08日
    浏览(82)
  • Linux网络编程二(TCP三次握手、四次挥手、TCP滑动窗口、MSS、TCP状态转换、多进程/多线程服务器实现)

    TCP三次握手 TCP 三次握手 (TCP three-way handshake)是TCP协议建立可靠连接的过程,确保客户端和服务器之间可以进行可靠的通信。下面是TCP三次握手的详细过程: 假设客户端为A,服务器为B 1 、第一次握手(SYN=1,seq=500) A向B发送一个带有SYN标志位的数据包,表示A请求建立连接。

    2024年02月06日
    浏览(58)
  • 【TCP/IP】利用I/O复用技术实现并发服务器 - select函数

    目录 I/O复用技术 select函数 设置文件描述符 指定监视范围 设置超时 I/O复用服务器端的实现        由服务器创建多个进程来实现并发的做法有时会带来一些问题,比如:内存上的开销、CPU的大量占用等,这些因素会消耗掉服务器端有限的计算资源、进而影响程序之间的执

    2024年02月08日
    浏览(49)
  • Linux网络编程二(TCP图解三次握手及四次挥手、TCP滑动窗口、MSS、TCP状态转换、多进程/多线程服务器实现)

    1、TCP三次握手 TCP 三次握手 (TCP three-way handshake)是 TCP协议建立可靠连接 的过程,确保客户端和服务器之间可以进行可靠的通信。下面是TCP三次握手的 详细过程 : 假设客户端为A,服务器为B。 (1) 第一次握手 第一次握手(SYN=1,seq=500) A向B发送一个带有 SYN 标志位的数据包,

    2024年04月22日
    浏览(48)
  • Linux网络编程之TCP/IP实现高并发网络服务器设计指南

    目录 引言: 多进程服务器 例程分享: 多线程服务器  例程分享: I/O多路复用服务器 select 例程分享: poll 例程分享: epoll 例程分享: 总结建议         随着互联网的迅猛发展,服务器面临着越来越多的并发请求。如何设计一个能够高效处理大量并发请求的服务器成为

    2024年02月20日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包