上一节提到,当子进程执行结束,父进程还在执行,在父进程结束之前子进程会成为僵尸进程,那么怎么销毁僵尸进程呢?父进程主动接收子进程的返回值。

销毁僵尸进程的方法:

  1:使用wait函数

  2:使用waitpid函数

  3:利用信号

1:使用wait函数销毁僵尸进程

#include <sys/wait.h>

pid_t wait(int *status);
// 成功返回终止的子进程id,失败返回-1

  在父进程中调用wait函数以后,如果有子进程已经执行结束,那么子进程传回的返回值将存储到status所指的内存中,但是如果没有子进程执行结束,父进程将会阻塞在wait函数,直到有子进程执行结束,这是一种不好的实现方法。

// wait函数销毁僵尸进程
#include <iostream>
#include <unistd.h>
#include <sys/wait.h> using namespace std; int main()
{
pid_t pid = fork();
if (pid < ) {
cout << "fork() failed" << endl;
return ;
} if (pid == ) {
return ;
} else {
int status = ;
wait(&status);
if (WIFEXITED(status)) { // 子进程正常结束
cout << "child return: " << WEXITSTATUS(status) << endl;
} sleep();
} return ;
}

2:使用waitpid函数销毁僵尸进程

  wait函数会使程序阻塞,可换用waitpid函数杀死僵尸进程,同时能避免程序阻塞。

#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *status, int options);
// 成功返回终止的子进程id,失败返回-1
// pid : 等待终止的子进程id,传-1代码任意子进程终止
// status:和wait函数的参数一样
// options:传递WNOHANG,没有终止的子进程也不进入阻塞状态,返回0
#include <iostream>
#include <unistd.h>
#include <sys/wait.h> using namespace std; int main()
{
pid_t pid = fork();
if (pid < ) {
cout << "fork() failed" << endl;
return ;
} if (pid == ) {
sleep();
return ;
} else {
int status = ;
while (!waitpid(pid, &status, WNOHANG)) {
sleep();
cout << "child proc is not finish" << endl;
} if (WIFEXITED(status)) { // 子进程正常结束
cout << "child return: " << WEXITSTATUS(status) << endl;
}
} return ;
}

3:利用信号销毁僵尸进程

  利用wait函数能销毁僵尸进程,但是会阻塞父进程;利用waitpid也能销毁僵尸进程并且不阻塞父进程,但是也需要不停的去检查子进程结束没有。所以wait及waitpid方式都不完美。于是引入了信号,信号是在特定事件发生时由操作系统向进程发送的消息,接收到消息的进程做信号处理。信号的使用方法是先注册信号,告诉操作系统,当某个信号发生时,要做什么事情。

  signal(SIGCHLD, do_what);  SIGCHLD是子进程结束的信号,当子进程结束时,执行do_what函数。

  其他常用信号,SIGALRM:已到通过调用alarm函数注册的时间;SIGINT:输入CTRL+C

#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h> using namespace std; void read_child_proc(int sig)
{
int status;
pid_t pid = waitpid(-, &status, WNOHANG); if (WIFEXITED(status)) { // 子进程正常结束
cout << "child proc finish: " << pid <<" " << WEXITSTATUS(status) << endl;
}
} int main()
{
pid_t pid = fork();
if (pid < ) {
cout << "fork() failed" << endl;
return ;
} signal(SIGCHLD, read_child_proc); // 注册子进程结束信号,当子进程结束时调用read_child_proc函数 if (pid == ) {
return ;
} else {
sleep();
cout << "master proc wake up" << endl;
} return ;
}

  执行程序发现 master proc wake up并不是等30秒以后才输出,而输出很快,说明当注册子进程结束信号以后,当有子进程执行结束时,会马上唤醒sleep的进程。

  

C/C++网络编程8——多进程服务器端之销毁僵尸进程的更多相关文章

  1. C/C++网络编程7——多进程服务器端之fork函数

    通过前面几节的内容,我们已经可以实现基本的C/S结构的程序了,但是当多个客户端同时向服务器端请求服务时,服务器端只能按顺序一个一个的服务,这种情况下,客户端的用户是无法忍受的.所以虚实现并发的服务器端 ...

  2. Linux 网络编程详解七(并发僵尸进程处理)

    在上一篇程序框架中,解决了子进程退出,父进程继续存在的功能,但是多条客户端连接如果同一时间并行退出,导致服务器端多个子进程同一时间全部退出,而SIGCHLD是不可靠信号,同时来多条信号可能无法处理,导 ...

  3. C/C++网络编程9——多进程服务器端实现

    #include <iostream> #include <unistd.h> #include <cstdlib> #include <arpa/inet. ...

  4. TCP/IP网络编程之多进程服务端(二)

    信号处理 本章接上一章TCP/IP网络编程之多进程服务端(一),在上一章中,我们介绍了进程的创建和销毁,以及如何销毁僵尸进程.前面我们讲过,waitpid是非阻塞等待子进程销毁的函数,但有一个不好的缺 ...

  5. TCP/IP网络编程之多进程服务端(一)

    进程概念及应用 我们知道,监听套接字会有一个等待队列,里面存放着不同客户端的连接请求,如果有一百个客户端,每个客户端的请求处理是0.5s,第一个客户端当然不会不满,但第一百个客户端就会有相当大的意见了 ...

  6. 网络编程并发 多进程 进程池,互斥锁,信号量,IO模型

    进程:程序正在执行的过程,就是一个正在执行的任务,而负责执行任务的就是cpu 操作系统:操作系统就是一个协调.管理和控制计算机硬件资源和软件资源的控制程序. 操作系统的作用: 1:隐藏丑陋复杂的硬件接 ...

  7. python并发编程之多进程1-----------互斥锁与进程间的通信

    一.互斥锁 进程之间数据隔离,但是共享一套文件系统,因而可以通过文件来实现进程直接的通信,但问题是必须自己加锁处理. 注意:加锁的目的是为了保证多个进程修改同一块数据时,同一时间只能有一个修改,即串行 ...

  8. python并发编程之多进程1--(互斥锁与进程间的通信)

    一.互斥锁 进程之间数据隔离,但是共享一套文件系统,因而可以通过文件来实现进程直接的通信,但问题是必须自己加锁处理. 注意:加锁的目的是为了保证多个进程修改同一块数据时,同一时间只能有一个修改,即串行 ...

  9. python并发编程之多进程1互斥锁与进程间的通信

    一.互斥锁 进程之间数据隔离,但是共享一套文件系统,因而可以通过文件来实现进程直接的通信,但问题是必须自己加锁处理. 注意:加锁的目的是为了保证多个进程修改同一块数据时,同一时间只能有一个修改,即串行 ...

随机推荐

  1. at org.apache.hadoop.hbase.tmpl.master.BackupMasterStatusTmplImpl.renderNoFlush(BackupMasterStatusTm

    at org.apache.hadoop.hbase.tmpl.master.BackupMasterStatusTmplImpl.renderNoFlush(BackupMasterStatusTm ...

  2. 概率dp poj 2151

    题意: 这道题目的意思很简单,有t个ACM队,m个题目,题目给出了每个队对每个题目做出的概率大小(0到1之间,包含0和1),要求每个队至少做出一道题(签到题),同时,要求获胜队必须至少能够做出n道题( ...

  3. Codeforces Round #622 C2.Skyscrapers (hard version)

    This is a harder version of the problem. In this version n≤500000n≤500000 The outskirts of the capit ...

  4. Docker - 周边 - Go Template

    概述 docker inspect -f 引出 希望学会后, 可以处理这个命令 问题: 这是啥 疑问 最开始, 我以为是 jsonpath 后来看 命令行的解释, 说叫 go template 但是我 ...

  5. +(new Date())

    +(new Date()) 等于 new Date().getTime();展示 1561003191879 毫秒时间戳

  6. C++ string类的使用

    C++ string的使用 在了解如何使用string类之前,我们先来看看C语言中使用字符串有多麻烦: 调用头文件:cstring 定义一个C字符串: char str1[51]="Hell ...

  7. python代码在linux终端中执行报错:Unable to init server: Could not connect: Connection refused

    python代码在linux终端中执行时报错: Unable to init server: Could not connect: Connection refused Unable to init ...

  8. django ForeignKey ManyToMany 前后端联动

    总结 外键基本和普通的字段是一样的 多对多 获取 getlist() 更新 clear() add() remove() 前端和后端是通过字符串沟通的,所以使用ajax的时候如果是数据类型,记得要JS ...

  9. redis持久化2

    编程迷思 博客园 首页 联系 订阅 管理 随笔 - 11  文章 - 0  评论 - 318 深入学习Redis(2):持久化 前言 在上一篇文章中,介绍了Redis的内存模型,从这篇文章开始,将依次 ...

  10. 【代码学习】PYTHON 闭包

    一.闭包 在函数内部再定义一个函数,并且这个函数用到了外边函数的变量 在函数里定义一个函数,不会默认被执行 def test(number): print("-----1-----" ...