进程的终止,回收子进程

2024-08-25 14:04
文章标签 回收 进程 终止

本文主要是介绍进程的终止,回收子进程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

进程的终止

正常终止

从main函数中返回可令进程终止

  • main函数一旦返回,主线程即终止,进程即终止,进程一旦终止,进程中的所有线程统统终止。这就是main函数的返回与其它函数的返回在本质上的区别。
  • main函数的返回值即进程的退出码,父进程可以在回收子进程的同时获得该退出码,以了解导致其终止的具体原因。

调用exit函数令进程终止 

#include <stdlib.h>
void exit(int status);

功能:令进程终止
参数:statusi进程的退出码,相当于main函数的返回值                                                           该函数不返回!!!
        虽然exit函数的参数和main函数的返回值都是int类型,但只有其中最低数位的字节可被其父进程回收,高三个字节会被忽略因此在设计进程的退出码时最好不要超过一字节的值域范围

        通过return语句终止进程只能在main函数中实现,但是调用exit函数终止进程可以在包括 main函数在内的任何函数中使用。
        exit函数在终止调用进程之前还会做几件收尾工作
A、调用实现通过atexit或on exit所数注册的退出处理函数;

B、冲刷并关闭所有仍处于打开状态的标准I/O流;

C、删除所有通过tmpfile函数创建的临时文件;

D、_exit(status);

代码演示 
//exit函数演示
#include <stdio.h>
#include <stdlib.h> //exit函数
#include <unistd.h> //_exit函数
//退出处理函数
/*void exitfun(void){printf("我是退出处理函数\n");
}
void exitfun2(int status,void *arg){printf("status = %d\n",status);printf("arg    = %s\n",(char*)arg);
}*/
int fun(void){printf("我是fun函数\n");exit(0); //终止程序//_exit(0); //终止程序return 10;
}int main(){//atexit(exitfun); //注册退出处理函数//on_exit(exitfun2,"hello"); //注册退出处理函数printf("fun函数返回%d\n",fun());return 0;
}

注册退出处理函数
#include <stdlib.h>
int atexit (void (*function) (void));

参数: function 函数指针,指向退出处理函数返回值:成功返回0,失败返回-1
        注意atexit函数本身并不调用退出处理函数,而只是将function参数所表示的退出处理函数地址,保存(注册)在系统内核的某个地方(进程表项)。待到exit函数被调用或在main函数里执行return语句时,再由系统内核根据这些退出处理函数的地址来调用它们。此过程亦称回调

注册退出处理函数
#include <stdlib.h>
int on_exit (void (*function) (int , void*), void* arg);

参数:function 函数指针,指向退出处理函数。其中第一个参数来自传递给exit函数
        的status参数或在main函数里执行return语句的返回值,而第二个参数则来自传递给on         exit函数的arg参数
        arg泛型指针,将作为第二个参数传递给function所指向的退出处理函数
返回值:成功返回0,失败返回-1

 调用 _exit/_Exit函数令进程终止

#include<unistd.h>                                                                                                                   void _exit(int status);
参数:status 进程退出码,相当于main函教的返回值

该函数不返回!
#include <stdlib.h>
void _Exit(int status);

参数:status 进程退出码,相当于main函数的返回值

该函数不返回

代码演示 
//exit函数演示
#include <stdio.h>
#include <stdlib.h> //exit函数
#include <unistd.h> //_exit函数
//退出处理函数
void exitfun(void){printf("我是退出处理函数\n");
}
void exitfun2(int status,void *arg){printf("status = %d\n",status);printf("arg    = %s\n",(char*)arg);
}
int fun(void){printf("我是fun函数\n");exit(0); //终止程序//_exit(0); //终止程序return 10;
}int main(){atexit(exitfun); //注册退出处理函数on_exit(exitfun2,"hello"); //注册退出处理函数printf("fun函数返回%d\n",fun());return 0;
}
运行结果 (观察输出结果顺序,栈的特点)
status = 0
arg    = hello
我是退出处理函数

异常终止

1、当进程执行了某些在系统看来具有危险性的操作,或系统本身发生了某种故障或意外,内核会向相关进程发送特定的信号。如果进程无意针对收到的信号采取补救措施,那么内核将按照缺省方式将进程杀死,并视情形生成核心转储文件(core)以备事后分析,俗称吐核
SIGILL(4):进程试图执行非法指令
SIGBUS(7):硬件或对齐错误
SIGFPE(8):浮点异常
SIGSEGV(11):无效内存访问
SIGPWR(30):系统供电不足

代码演示(无效内存访问) 

//回收子进程
#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
int main(){//父进程创建子进程pid_t pid = fork();if(pid == -1){perror("fork");return -1;}//子进程代码,代码暂时不结束if(pid == 0){printf("%d进程:我是子进程,暂时不结束\n",getpid());// sleep(5);// return 0;//exit(300);//_exit(300);//_Exit(300);//abort();int *p = NULL;*p = 1;return 0;}// 父进程代码,等待子进程结束收尸int status;pid_t childpid = wait(&status);if(childpid == -1){perror("wait");return -1;}   printf("%d进程:回收了%d进程的僵尸\n",getpid(),childpid);if(WIFEXITED(status)){printf("正常终止:%d\n",WEXITSTATUS(status));}else{printf("异常终止:%d\n",WTERMSIG(status));}return 0;
}

运行结果

19055进程:我是子进程,暂时不结束
19041进程:回收了19055进程的僵尸
异常终止:11

2、人为触发信号
SIGINT(2):Ctr+C
SIGQUIT(3):Ctr+\
SIGKILL(9):不能被捕获或忽略的进程终止信号

SIGTERM(15):可以被捕获或忽略的进程终止信号

3、向进程自己发送信号
#include <stdlib.h>
void abort(void);

功能:向调用进程发送SIGABRT(6)信号,该信号默认情况下可使进程结束无参数,不返回! 

 回收子进程

为什么要回收子进程:
        清除僵尸进程,避免消耗系统资源。

        父进程需要等待子进程的终止,以继续后续工作父进程需要知道子进程终止的原因
        如果是正常终止,那么进程的退出码是多少?
        如果是异常终止,那么进程是被那个信号所终止的?

wait函数

#include <sys/wait.h>
pid_t wait(int* status);

功能:等待并回收任意子进程
参数:status 用于输出子进程的终止状态可置NULL

返回值:成功返回所回收的子进程的PID,失败返回-1 

父进程在创建若干子进程以后调用wait函数:
A.若所有子进程都在运行,则阻塞,直到有子进程终止才返回
B.若有一个子进程已终止,则返回该子进程的P!D并通过status参数输出其终止状态

C.若没有任何可被等待并回收的子进程,则,返回-1,置errno为ECHILD。 

在任何一个子进程终止前,wait函数只能阻塞调用进程,如果有一个子进程在wait函数被调用之前,已经终止并处于僵尸状态,wait函数会立即返回,并取得该子进程的终止状态,同时子进程僵尸消失。由此可见wait函数主要完成三个任务
1.阻塞父进程的运行,直到子进程终止再继续,停等同步
2.获取子进程的PID和终止状态,令父进程得知谁因何而死
3.为子进程收尸,防止大量僵尸进程耗费系统资源

以上三个任务中,即使前两个与具体需求无关,仅仅第三个也足以凸显wait函数的重要性,尤其是对那些多进程服务器型的应用而言

子进程的终止状态通过wait函数的status参数输出给该函数调用者。<sys/wait.h>头文件提供了几个辅助分析进程终止状态的工具宏
WIFEXITED(status)
真:正常终止    WEXITSTATUS(status)进程退出码    
假:异常终止    WTERMSIG(status)-> 终止进程的信号    
WIFSIGNALED(status)
真:异常终止    WTERMSIG(status) -> 终止进程的信号    
假:正常终止    WEXITSTATUS(status) ->进程退出码    

代码演示 (单子进程)
//回收子进程
#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
int main(){//父进程创建子进程pid_t pid = fork();if(pid == -1){perror("fork");return -1;}//子进程代码,代码暂时不结束if(pid == 0){printf("%d进程:我是子进程,暂时不结束\n",getpid());// sleep(5);// return 0;//exit(300);//_exit(300);//_Exit(300);//abort();int *p = NULL;*p = 1;return 0;}// 父进程代码,等待子进程结束收尸int status;pid_t childpid = wait(&status);if(childpid == -1){perror("wait");return -1;}   printf("%d进程:回收了%d进程的僵尸\n",getpid(),childpid);if(WIFEXITED(status)){printf("正常终止:%d\n",WEXITSTATUS(status));}else{printf("异常终止:%d\n",WTERMSIG(status));}return 0;
}
代码演示(多子进程) 
//回收多个子进程
#include <stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<errno.h>int main(){//创建多个子进程for(int i = 0; i < 5; i++){pid_t pid = fork();if(pid == -1){perror("fork");return -1;}if(pid == 0){printf("%d进程:我是子进程\n", getpid());sleep(1 + i);return 0;}}for(;;){pid_t pid = wait(NULL);  //NULL不保存子进程的终止状态if(pid == -1){if(errno == ECHILD){printf("没有子进程\n");break;}else{perror("wait");return -1;}}printf("%d进程:回收了%d进程\n",getpid(),pid);}return 0;
}

waitpid函数 

 #include <sys/wait.h>
pid_t waitpid(pid_t pid, int* status, int options);

>功能:等待并回收任意或特定子进程
参数:pid 可取以下值
        -1等待并回收任意子进程相当于wait函数
        0 等待并回收特定子进程
        status 用于输出子进程的终止状态,可置NULL

        options可以如下取值
                0  阻塞模式,若所等子进程仍在运行,则阻塞直至其终止

                WNOHANG  非阻塞模式,若所等子进程仍在运行,则返回 0

返回值:成功返回所回收子进程的PID或者0,失败返回-1。
waitpid(-1,&status,0)等价于 wait(&status)

代码演示 
// 非阻塞回收
#include <stdio.h>
#include<unistd.h>
#include<sys/wait.h>int main(){//创建子进程pid_t pid = fork();if(pid == -1){perror("fork");return -1;}//子进程代码,暂时不结束if(pid == 0){printf("%d进程:我是子进程\n",getpid());sleep(10);return 0;}//父进程代码,以非阻塞方式回收子进程while(1){pid_t childpid = waitpid(pid,NULL,WNOHANG);if(childpid == -1){perror("waitpid");return -1;}else if(childpid == 0){printf("%d进程:子进程还在运行,无法回收\n",getpid());sleep(1);}else{printf("%d进程:回收了%d进程的僵尸\n",getpid(),childpid);break;}}return 0;}
运行结果 
22618进程:我是子进程
22613进程:子进程还在运行,无法回收
22613进程:子进程还在运行,无法回收
22613进程:子进程还在运行,无法回收
22613进程:子进程还在运行,无法回收
22613进程:子进程还在运行,无法回收
22613进程:子进程还在运行,无法回收
22613进程:子进程还在运行,无法回收
22613进程:子进程还在运行,无法回收
22613进程:子进程还在运行,无法回收
22613进程:子进程还在运行,无法回收
22613进程:回收了22618进程的僵尸

        事实上,无论一个进程是正常终止还是异常终止,都会通过系统内核向其父进程发送SIGCHLD(17)信号。父进程可以忽略该信号,也可以提供一个针对该信号的信号处理函数,在信号处理函数中以异步的方式回收子进程。这样做不仅流程简单,而且僵尸的存活时间短,回收效率高

这篇关于进程的终止,回收子进程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1105753

相关文章

一文解密Python进行监控进程的黑科技

《一文解密Python进行监控进程的黑科技》在计算机系统管理和应用性能优化中,监控进程的CPU、内存和IO使用率是非常重要的任务,下面我们就来讲讲如何Python写一个简单使用的监控进程的工具吧... 目录准备工作监控CPU使用率监控内存使用率监控IO使用率小工具代码整合在计算机系统管理和应用性能优化中,监

Linux进程CPU绑定优化与实践过程

《Linux进程CPU绑定优化与实践过程》Linux支持进程绑定至特定CPU核心,通过sched_setaffinity系统调用和taskset工具实现,优化缓存效率与上下文切换,提升多核计算性能,适... 目录1. 多核处理器及并行计算概念1.1 多核处理器架构概述1.2 并行计算的含义及重要性1.3 并

Linux线程之线程的创建、属性、回收、退出、取消方式

《Linux线程之线程的创建、属性、回收、退出、取消方式》文章总结了线程管理核心知识:线程号唯一、创建方式、属性设置(如分离状态与栈大小)、回收机制(join/detach)、退出方法(返回/pthr... 目录1. 线程号2. 线程的创建3. 线程属性4. 线程的回收5. 线程的退出6. 线程的取消7.

Linux下进程的CPU配置与线程绑定过程

《Linux下进程的CPU配置与线程绑定过程》本文介绍Linux系统中基于进程和线程的CPU配置方法,通过taskset命令和pthread库调整亲和力,将进程/线程绑定到特定CPU核心以优化资源分配... 目录1 基于进程的CPU配置1.1 对CPU亲和力的配置1.2 绑定进程到指定CPU核上运行2 基于

Javaee多线程之进程和线程之间的区别和联系(最新整理)

《Javaee多线程之进程和线程之间的区别和联系(最新整理)》进程是资源分配单位,线程是调度执行单位,共享资源更高效,创建线程五种方式:继承Thread、Runnable接口、匿名类、lambda,r... 目录进程和线程进程线程进程和线程的区别创建线程的五种写法继承Thread,重写run实现Runnab

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Java进程异常故障定位及排查过程

《Java进程异常故障定位及排查过程》:本文主要介绍Java进程异常故障定位及排查过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、故障发现与初步判断1. 监控系统告警2. 日志初步分析二、核心排查工具与步骤1. 进程状态检查2. CPU 飙升问题3. 内存

Windows的CMD窗口如何查看并杀死nginx进程

《Windows的CMD窗口如何查看并杀死nginx进程》:本文主要介绍Windows的CMD窗口如何查看并杀死nginx进程问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录Windows的CMD窗口查看并杀死nginx进程开启nginx查看nginx进程停止nginx服务

Java进程CPU使用率过高排查步骤详细讲解

《Java进程CPU使用率过高排查步骤详细讲解》:本文主要介绍Java进程CPU使用率过高排查的相关资料,针对Java进程CPU使用率高的问题,我们可以遵循以下步骤进行排查和优化,文中通过代码介绍... 目录前言一、初步定位问题1.1 确认进程状态1.2 确定Java进程ID1.3 快速生成线程堆栈二、分析

Python多进程、多线程、协程典型示例解析(最新推荐)

《Python多进程、多线程、协程典型示例解析(最新推荐)》:本文主要介绍Python多进程、多线程、协程典型示例解析(最新推荐),本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定... 目录一、multiprocessing(多进程)1. 模块简介2. 案例详解:并行计算平方和3. 实现逻