进程等待、进程替换

news2024/12/23 17:08:20

目录

进程等待

waitpid函数

wait函数

进程替换


进程等待

进程等待的意义

如果子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。 另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。 最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对, 或者是否正常退出。 父进程需要通过进程等待的方式,回收子进程资源,获取子进程退出信息。

waitpid函数

#include <sys/types.h>

#include <sys/wait.h>

pid_ t waitpid(pid_t pid, int *status, int options);

返回值:

  • 当正常返回的时候waitpid返回收集到的子进程的进程PID;
  • 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
  • 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;

参数:

1、pid(判定等待集合的成员)

如果pid=-1,那么等待集合就是由父进程所有的的子进程组成的。与wait等效。

如果pid>0,那么等待集合是一个单独的子进程,它的进程ID等于pid。

2、status(修改默认行为):

如果status参数是非空的,那么waitpid就会在status中放上关于导致返回的子进程的状态信息 。wait.h头文件定义了解释status参数的几个宏:

  • WIFEXITED(status):如果子进程通过调用exit或者 一个返回(return)正常终止,就返回真。
  • WEXITSTATUS(status):返回一个正常终止的子进程的退出状态,只有在WIFEXITED()返回为真时,才会定义这个状态。
  • WIFSIGNALED(status) :如果子进程是因为一个未被捕获的信号终止的,那么就返回真。
  • WTERMSIG( status):返回导致子进程终止的信号的编号。只有在WIFSIGNALED()返回为真时,才定义这个状态。
  • WIFSTOPPED(status):如果引起返回的子进程当前是停止的,那么就返回真。
  • WSTOPSIG(status): 返回导致子进程停止的信号的编号。只有在WIFSTOPPED()返回为真时,才定义这个状态。
  • WIFCONTINUED(status):如果子进程收到SIGCONT信号重新启动,则返回真。

如果调用进程没有子进程,那么waitpid返回-1,并且设置errno为ECHILD。如果waitpid函数被一个信号中断,那么它返回-1,并设置errno为EINTR。

3、options(检查已回收子进程的退出状态):

可以通过将options设置为常量WNOHANG、WUNTRACED和WCONTINUED的各种组合来修改默认行为:

  • WNOHANG:如果等待集合中的任何子进程都还没有终止,那么就立即返回(返回值为0)。默认的行为是挂起调用进程,直到有子进程终止。在等待子进程终止的同时,如果还想做些有用的工作,这个选项会有用。
  • WUNJTRACED: 挂起调用进程的执行,直到等待集合中的一个进程变成已终止或者被停止。返回的PID为导致返回的已终止或被停止子进程的PID。默认的行为是只返同已终止的子进程。当你想要检查已终止和被停止的子进程时,这个选项会有用。
  • WCONTINUED:挂起调用进程的执行,直到等待集合中一个正在运行的进程终止或等待集合中个被停止的进程收到SIGCONT信号重新开始执行。

    可以用或运算把这些选项组合起来。例如:

  • WNOHANG | WUNTRACED:立即返回,如果等待集合中的子进程都没有被停止或终止,则返回值为0;如果有一个停止或终止,则返回值为该子进程的PID。

wait函数

wait函数是waitpid函数的简单版本。

#include <sys/types.h>

#include <sys/wait.h>

pid_t wait(int*status);  

返回值: 成功返回被等待进程pid,失败返回-1。

参数: 输出型参数,获取子进程退出状态,不关心则可以设置成为NULL

调用wait(&status)等价于调用waitpid(-1,&status,0)。

如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。

如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。

如果不存在该子进程,则立即出错返回。

父进程按照顺序存储了它的子进程的PID,然后通过用适当的PID作为第一个参数来调用waitpid,按照同样的顺序来等待每个子进程。 

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
 #define N 2
int main()
{                                                                                                                                                                                           
  int status,i;
   pid_t pid[N],retpid;
    //Parent creates N children
   for(i=0;i<N;i++)
      if((pid[i] = fork()) == 0)//child
       exit(100+i);

     //Parent reaps N children in order
     i=0;
    while((retpid = waitpid(pid[i++],&status,0)) > 0)
    {
         if(WIFEXITED(status))
           printf("child %d terminated normally with exit status=%d\n",retpid,WEXITSTATUS(status));
         else 
           printf("child %d terminated abnormally\n",retpid);
     }
 
     the only normal termination is if there are no more children
    if(errno != ECHILD)
      unix_error("waitpid error");
  
     exit(0);                                                       
 } 

当子进程未退出时,父进程都在一直等待子进程退出,在等待期间,父进程不能做任何事情,这种等待叫做阻塞等待。

实际上我们可以让父进程在等待子进程退出期间,执行一些自己的事情,当子进程退出时再读取子进程的退出信息,即非阻塞等待。

这时候我们就可以用到options参数

WNOHANG:如果等待集合中的任何子进程都还没有终止,那么就立即返回(返回值为0)。默认的行为是挂起调用进程,直到有子进程终止。在等待子进程终止的同时,如果还想做些有用的工作,这个选项会有用。

#include <stdio.h> 
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
	pid_t pid;
	pid = fork();
	if (pid < 0) {
		printf("%s fork error\n", __FUNCTION__);
		return 1;
	}
	else if (pid == 0) { //child
		printf("child is run, pid is : %d\n", getpid());
		sleep(5);
		exit(1);
	}
	else {
		int status = 0;
		pid_t ret = 0;
		do
		{
			ret = waitpid(-1, &status, WNOHANG);//非阻塞式等待
			if (ret == 0) {
				printf("child is running\n");
			}
			sleep(1);
		} while (ret == 0);
		if (WIFEXITED(status) && ret == pid) {
			printf("wait child 5s success, child return code is :%d.\n", WEXITSTATUS(status));
		}
		else {
			printf("wait child failed, return.\n");
			return 1;
		}
	}
	return 0;
}

进程替换

exec是程序替换函数,本身并不创建进程,exec生成的进程是当前进程的一个相同副本,pid并没有改变。

替换函数

int execve(const char *path, char *const argv[], char *const envp[]);

如果成功,则不返回,如果错误,则返回-1。 

#include <unistd.h>

int execl(const char *path, const char *arg, ...);

int execlp(const char *file, const char *arg, ...);

int execle(const char *path, const char *arg, ...,char *const envp[]);

int execv(const char *path, char *const argv[]);

int execvp(const char *file, char *const argv[]);

只有execve是真正的系统调用,其它函数都是对execve函数的封装,最终都是调用execve。

这些函数原型看起来很容易混,但只要掌握了规律就很好记。

l(list) : 表示参数采用列表 ;

v(vector) : 参数用数组 ;

p(path) : 有p自动搜索环境变量PATH ;

e(env) : 表示自己维护环境变量 

execl:参数格式为列表;不带路径;使用当前环境变量。

execlp:参数格式为列表;带路径;需要自己组装环境变量。

execle:参数格式为列表;不带路径;使用当前环境变量。

execv:参数格式为数组;不带路径;使用当前环境变量。

execvp:参数格式为数组;带路径;需要自己组装环境变量。

我们可以看到,后来打印的end...并没有被输出,当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,并从新程序的启动例程开始执行。后面的代码属于老代码直接被替换,不会再执行了。 

 

#include <unistd.h>

int main()

{

        char *const argv[] = {"ls", "-l", NULL};

        char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};

        execl("/bin/ls", "ls", "-l", NULL); // 带p的,可以使用环境变量PATH,无需写全路径         execlp("ls", "ls", "-l", NULL); // 带e的,需要自己组装环境变量

        execle("ls", "ls", "-l", NULL, envp);

        execv("/bin/ls", argv); // 带p的,可以使用环境变量PATH,无需写全路径

        execvp("ls", argv); // 带e的,需要自己组装环境变量

        execve("/bin/ls", argv, envp);

        exit(0);

}  

 

 数组的最后一个元素最好为空。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/446927.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

5.5G的关键一跳!将数智未来照进现实

编辑&#xff1a;阿冒 设计&#xff1a;沐由 作为数字时代的三大思想家之一&#xff0c;乔治吉尔德在1993年就指出&#xff0c;未来25年内主干网的带宽每6个月增长一倍&#xff0c;其增长速度是摩尔定律预测的CPU增长速度的3倍。 这就是著名的吉尔德定律&#xff08;Gilder’s …

Qt开源项目:校医院远程诊断系统介绍

本人研一参考技术书籍开发的一款Qt程序&#xff0c;两年前已上传到GitHub&#xff0c;有兴趣的同学可以去看看。可能之前上传的项目不够完整&#xff0c;导致有一些同学没有在自己的环境上跑通&#xff0c;所以今天将整个工程都重新上传一遍&#xff0c;包括使用到的opencv的动…

Lambda 表达式中的变量必须是 final 的吗

如果我们定义了一个变量&#xff0c;想要在Lambda 表达式中修改变量的值&#xff0c;编译器会发出警告&#xff1a;“variable used in lambda expression should be final or effectively final”。 比如对一个list进行遍历&#xff0c;遍历的过程中对i进行操作 Java 规范中…

浅理解 ES6 新增的数组方法Array.of() 和 Array.from()

文章目录 &#x1f4cb;前言&#x1f3af;Array.of() 方法&#x1f3af;Array.from() 方法&#x1f3af;二者区别&#x1f4dd;最后 &#x1f4cb;前言 在前端开发的面试过程中&#xff0c; ES6 新增是一个很常见的考点&#xff0c;比如说箭头函数、模板字符串、let 和 const …

宁波汽车运输集团:引入二维码技术,实现车辆精细化管理

宁波市汽车运输集团有限公司是宁波市道路货运业的龙头企业之一&#xff0c;主营全国各地的普通货运以及货物专用运输&#xff08;集装箱、罐式&#xff09;。 作为汽车运输集团&#xff0c;车辆的安全问题极其重要。因此&#xff0c;公司设备安全部门要求每个驾驶员在作业之前…

netfilter filter表(二)

这次继续分析filter表&#xff0c;不同与之前的分析方式&#xff0c;这次通过将内核中的数据打印出来&#xff0c;对比结构关系图来分析。这是本次分析涉及的几个数据结构&#xff1a; struct xt_table { struct list_head list; /* What hooks you will enter on */ unsigned …

4、SpringBoot接收和响应xml报文请求

背景 平时开发的接口&#xff0c;基本是使用 json 格式的请求报文。然而&#xff0c;有时候也避免不了有 xml 报文请求的场景&#xff0c;最近就遇到了这种情况&#xff0c;在此记录下。另外&#xff0c;工程中使用的是 controller-service……这种结构。 xml请求报文&#x…

链表(JS实现)

&#x1f4dd;个人主页&#xff1a;爱吃炫迈 &#x1f48c;系列专栏&#xff1a;数据结构与算法 &#x1f9d1;‍&#x1f4bb;座右铭&#xff1a;道阻且长&#xff0c;行则将至&#x1f497; 文章目录 链表链表的分类创建链表LinkedList类的骨架 实现链表的方法push尾部添加元…

chatgpt智能提效职场办公-ppt怎么蒙层

作者&#xff1a;虚坏叔叔 博客&#xff1a;https://xuhss.com 早餐店不会开到晚上&#xff0c;想吃的人早就来了&#xff01;&#x1f604; 在 PowerPoint 中添加蒙版图层&#xff0c;可以在幻灯片中创建一个半透明的矩形或形状&#xff0c;并在其上方添加或放置其他对象。 下…

FPGA终于可以愉快地写代码了!Vivado和Visual Studio Code黄金搭档

如果你是一位FPGA开发者&#xff0c;那么你一定会对VIvado这款软件非常熟悉。但是&#xff0c;对于vivado兼容的第三方编辑器软件&#xff0c;你知道Visual Studio Code吗&#xff1f;这是个非常不错的选择&#xff0c;Visual Studio Code搭配众多插件&#xff0c;能让你FPGA开…

【SpringBoot】一:SpringBoot的基础(下)

文章目录 1.外部化的配置1.1 配置文件基础1.1.1 配置文件格式1.1.2 application文件1.1.3 application.properties1.1.4 application.yml1.1.5 environment1.1.6 组织多文件1.1.7多环境配置 1.2 绑定Bean1.2.1 简单的属性绑定1.2.2 嵌套Bean1.2.3 扫描注解1.2.4 处理第三方库对…

【移动端网页布局】移动端网页布局基础概念 ② ( 视口 | 布局视口 | 视觉视口 | 理想视口 )

文章目录 一、视口1、布局视口 ( 网页大小 | 网页大小 > 设备大小 )2、视觉视口 ( 设备大小 | 网页大小 > 设备大小 )3、理想视口 ( 网页大小 设备大小 ) 一、视口 浏览器 显示 网页页面内容 的 屏幕区域 被称为 " 视口 " ; 视口分为以下几个大类 : 布局视口…

项目协同中的git

在远程代码仓库&#xff08;云效&#xff0c;gitee&#xff0c;github&#xff0c;Coding等&#xff09;新建一个代码库&#xff0c; 我使用的云效 新建一个develop分支&#xff0c;后续所有人的提交代码都合并到develop分支上面&#xff0c;一般develop分支是用来开发用的&…

尚融宝22-提交借款申请

目录 一、需求介绍 二、图片上传 &#xff08;一&#xff09;前端页面 &#xff08;二&#xff09;实现图片上传 三、数据字典展示 &#xff08;一&#xff09;后端 &#xff08;二&#xff09;前端 四、表单信息提交 &#xff08;一&#xff09;后端 1、VO对象&…

嵌入式工程师如何快速的阅读datasheet的方法

目录 ▎从项目角度来看datasheet ▎各取所需 ▎最后 Datasheet&#xff08;数据手册&#xff09;的快速阅读能力&#xff0c;是每个工程师都应该具备的基本素养。 无论是项目开始阶段的选型还是后续的软硬件设计&#xff0c;到后期的项目调试&#xff0c;经常有工程师对着英…

06-Node.js—模块化

目录 1、介绍1.1 什么是模块化与模块 ?1.2 什么是模块化项目 ?1.3 模块化好处 2、模块暴露数据2.1 模块初体验2.2 暴露数据2.2.1 module.exports value2.2.2 exports.name value 3、导入&#xff08;引入&#xff09;模块4、导入模块的基本流程5、CommonJS 规范参考 1、介绍…

使用RabbitMQ的手动接收模式:消息第二次入队Failed to declare queue

问题&#xff1a;在rabbitMQ测试使用手动接收模式时发生 Failed to declare queue错误 : Channel shutdown: channel error; protocol method: #method<channel.close>(reply-code406, reply-textPRECONDITION_FAILED - unknown delivery tag 1, class-id60, method-id80…

C++ 命名空间、域、缺省参数、函数重载、引用、auto、内联函数的知识点+完整思维导图+基本练习题+深入细节+通俗易懂建议收藏

绪论 从本章开始我们正式进入到C的内容&#xff0c;对此如果没有学习过C语言的建议先将C语言系统的学习一遍后再来&#xff08;已经更新完在专栏就能看到&#xff09;。 话不多说安全带系好&#xff0c;发车啦&#xff08;建议电脑观看&#xff09;。 附&#xff1a;红色&#…

Linux运维基础

一.vim编辑器 1.编辑器介绍 vi/vim是visual interface的简称,是Linux中最经典的文本编辑器&#xff0c;同图形化界面中的文本编辑器一样&#xff0c;vi是命令行下对文本文件进行编辑的绝佳选择&#xff0c;粗暴理解相当于windows下的记事本。 vim是vi的加强版本,兼容vi的所有…

java版UWB人员定位系统源码,提供位置实时显示、历史轨迹回放、电子围栏、行为分析、智能巡检等功能

运用UWB定位技术开发的人员定位系统源码 文末获取联系&#xff01; 本套系统运用UWB定位技术&#xff0c;开发的高精度人员定位系统&#xff0c;通过独特的射频处理&#xff0c;配合先进的位置算法&#xff0c;可以有效计算复杂环境下的人员与物品的活动信息。 提供位置实时显…