进程的终止,回收子进程

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

相关文章

离心萃取机废旧磷酸铁锂电池回收工艺流程

在废旧磷酸铁锂电池的回收工艺流程中,离心萃取机主要应用于萃取除杂的步骤,以提高回收过程中有价金属(如锂)的纯度。以下是结合离心萃取机应用的废旧磷酸铁锂电池回收工艺流程: 电池拆解与预处理 拆解:将废旧磷酸铁锂电池进行拆解,分离出电池壳、正负极片、隔膜等部分。破碎与筛分:将正负极片进行破碎处理,并通过筛分将不同粒径的物料分开,以便后续处理。 浸出与溶解 浸出:采用适当的浸出工艺(如二段式逆

[Linux]:进程(下)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:Linux学习 贝蒂的主页:Betty’s blog 1. 进程终止 1.1 进程退出的场景 进程退出只有以下三种情况: 代码运行完毕,结果正确。代码运行完毕,结果不正确。代码异常终止(进程崩溃)。 1.2 进程退出码 在编程中,我们通常认为main函数是代码的入口,但实际上它只是用户级

java 进程 返回值

实现 Callable 接口 与 Runnable 相比,Callable 可以有返回值,返回值通过 FutureTask 进行封装。 public class MyCallable implements Callable<Integer> {public Integer call() {return 123;}} public static void main(String[] args

C#关闭指定时间段的Excel进程的方法

private DateTime beforeTime;            //Excel启动之前时间          private DateTime afterTime;               //Excel启动之后时间          //举例          beforeTime = DateTime.Now;          Excel.Applicat

linux中使用rust语言在不同进程之间通信

第一种:使用mmap映射相同文件 fn main() {let pid = std::process::id();println!(

浅谈PHP5中垃圾回收算法(Garbage Collection)的演化

前言 PHP是一门托管型语言,在PHP编程中程序员不需要手工处理内存资源的分配与释放(使用C编写PHP或Zend扩展除外),这就意味着PHP本身实现了垃圾回收机制(Garbage Collection)。现在如果去PHP官方网站(php.net)可以看到,目前PHP5的两个分支版本PHP5.2和PHP5.3是分别更新的,这是因为许多项目仍然使用5.2版本的PHP,而5.3版本对5.2并不是完

Golang进程权限调度包runtime

关于 runtime 包几个方法: Gosched:让当前线程让出 cpu 以让其它线程运行,它不会挂起当前线程,因此当前线程未来会继续执行GOMAXPROCS:设置最大的可同时使用的 CPU 核数Goexit:退出当前 goroutine(但是defer语句会照常执行)NumGoroutine:返回正在执行和排队的任务总数GOOS:目标操作系统NumCPU:返回当前系统的 CPU 核数量 p

如何保证android程序进程不到万不得已的情况下,不会被结束

最近,做一个调用系统自带相机的那么一个功能,遇到的坑,在此记录一下。 设备:红米note4 问题起因 因为自定义的相机,很难满足客户的所有需要,比如:自拍杆的支持,优化方面等等。这些方面自定义的相机都不比系统自带的好,因为有些系统都是商家定制的,难免会出现一个奇葩的问题。比如:你在这款手机上运行,无任何问题,然而你换一款手机后,问题就出现了。 比如:小米的红米系列,你启用系统自带拍照功能后

flume系列之:记录一次flume agent进程被异常oom kill -9的原因定位

flume系列之:记录一次flume agent进程被异常oom kill -9的原因定位 一、背景二、定位问题三、解决方法 一、背景 flume系列之:定位flume没有关闭某个时间点生成的tmp文件的原因,并制定解决方案在博主上面这篇文章的基础上,在机器内存、cpu资源、flume agent资源都足够的情况下,flume agent又出现了tmp文件无法关闭的情况 二、

C++编程:ZeroMQ进程间(订阅-发布)通信配置优化

文章目录 0. 概述1. 发布者同步发送(pub)与订阅者异步接收(sub)示例代码可能的副作用: 2. 适度增加缓存和队列示例代码副作用: 3. 动态的IPC通道管理示例代码副作用: 4. 接收消息的超时设置示例代码副作用: 5. 增加I/O线程数量示例代码副作用: 6. 异步消息发送(使用`dontwait`标志)示例代码副作用: 7. 其他可以考虑的优化项7.1 立即发送(ZMQ_IM