当前位置: 首页 > news >正文

Linux进程等待 | 程序替换

进程终止

一个进程退出了,无非只有三种情况:

  1. 代码跑完了,结果正确
  2. 代码跑完了,结果不正确
  3. 代码没跑完,程序异常退出了

代码跑完了,我们可以通过退出码获取其结果是否正确,(这个退出码就是我们在main函数都是写上 return 0)。而进程没跑完,就出现异常退出了,那么退出码就是无意义的。


进程退出码

是什么:OS用来表示一个进程结束时候状态的一个数值。

规定:0表示代码正确,非0表示出错。那么退出的信息谁来读取?父进程读取进程的退出码。

查看:通过echo $? 查看进程的退出码(注意:这次最近一次进程退出的结果)

 在C语言中,sterror函数会提供特定的错误码信息

同时会提供一个全局的变量errno用于记录特地函数出错的结果,称为错误码。

这个全局码可以被strerror函数解析处错误的信息。

errno会记录最后一次函数的出错信息,可以手动清零。

 许多系统调用出错,就会设置errno。比如将read设置为非阻塞,如果读出错,可能真的出错了,也可能本轮没有读到数据,就可以通过errno判断是否为EAGAIN。

错误码VS退出码

  1. 错误码就是函数出错的原因,一般不会直接影响进程的结果。
  2. 退出码用来衡量进程退出的结果。

进程退出的方式

正常终止

  • return 
  • exit()
  • _exit()

异常退出

  • 信号终止

关于  exit和_exit的区别

exit是C语言提供的库函数,底层封装了系统调用。会引起一个进程正常退出,并且会将缓冲区刷新。而_exit是系统调用,也是引起进程正确退出,但不会将缓冲区刷新。

演示区别

本质的区别就是数据被保留在缓冲区中了,没有被刷新到外设上。

 


进程终止,内核做了什么?


进程=内核数据结构+进程代码 和 数据

进程终止后,释放代码+数据资源,OS会先将进程的状态修改会Z状态,等待父进程读取进程的退出信息。

随后将状态由Z修改为X,并且释放内核数据结构。


进程等待

什么是进程等待:

通过wait或者waitpid,(父进程)对子进程的资源进行回收的等待过程。

为什么进程等待:
1.解决僵尸问题带来的内存泄漏

2.必要时候,获取子进程的退出信息(退出码+退出信号)

如何等待:

  • wait()

通过系统调用函数,回收指定进程的资源。

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

其中wait的参数为整形指针,用于接收退出信息。

演示:故意让子进程先退出,父进程再等待回收。

会看到子进程退出后,状态变为Z状态,随后立马被删除,也就是被父进程回收资源了

  •  waitpid
pid_t waitpid(pid_t pid, int *_Nullable wstatus, int options);

waitpid常用于等待任意的进程,关于参数:

  • pid:指定的进程,-1表示等待任意进程。
  • wstatus:输出型参数,获取子进程的退出结果(退出码和退出信号)
  • options选项:(常常设置为0表示阻塞等待,也可以设置为WNOHANG表示非阻塞等待)

获取退出信息

退出信息是一个16位的字段

0-7位表示终止信号,8位标识核心转储,9-15表示退出码

 演示获取退出信息

  1 #include<stdio.h>2 #include<sys/wait.h>3 #include<stdlib.h>4 #include<unistd.h>5 int main(){6     pid_t  rid=fork();7     if(rid==0){8         //子进程9         printf("我是子进程,我要退出了\n");10         exit(123);11     }12 13     //父进程等待回收14     int code=0;15     pid_t id=waitpid(-1,&code,0);16     //显示退出的信息17     printf("父进程等待成功,子进程的退出信息,exit_code:%d,sig:%d\n",  (code>>8)&0xFF,code&0x7F);18     return 0;19 }

通过信号将子进程终止,父进程也能读取到

但是这样退出需要程序员自己做位运算,比较麻烦,库函数提供宏,获取退出退出信息。

  • WIFEXITED:退出码
  • WEXITSTATUS:信号

 这里就不做演示了,用法比较简单。


父进程如何得知子进程退出信息

引出一个共识:硬件设备有它的等待队列,而PCB内也会内置等待队列。

所以这就是父进程调用wait阻塞的原因。


父进程等待多个子进程

如果没有保存frok之后的id,一般都是通过waitpid(-1)一次性等待。

非阻塞等待子进

通过waitpid的op选项字段,设置为非阻塞等待

如果rid>0等待成功,rid==0无进程回收,rid<0表示出错

程序替换

程序替换常用于父子进程分离、父进程调用其它语言、进程间的协作等。

见一见进程替换

调用exel函数,替换系统进程 ls


程序替换的原理

  • 替换进程的代码+数据(包括堆栈数据),替换后的新进程从main函数开始执行。
  • OS对页表的重新映射,保证新程序的代码+数据会被正确映射到物理内存上。
  • 原本子进程可能和父进程共享代码,现在会重新申请物理内存,存放新进程的代码。实现父子进程的分离。

程序替换的方法

必须找到程序的替换的可执行进程

指定替换的方法


认识程序替换函数

C语言为程序替换提供6个函数,以满足不同的场景需求。

实际上这些函数换汤不换药,用法基本一致。

 exec表示执行

  • l:list 参数以列表的方式传递(命令行上怎么写,这里就怎么写),比如"ls" "-a "  "-l"
  • p:指定可执行程序的文件名,会自动到PATH中寻找
  • v:对应的是l,利用 char * argv[ ]数组,将参数传递
  • e:是环境变量表

演示以数组参数


程序替换我们自己的进程

利用C语言调用C++

注意:

环境变量被子进程继承是默认的行为,为什么?
程序替换只替换代码+数据,不替换地址空间中的命令行参数和环境变量表。

 

当我们执行可执行程序的时候,OS为我们做的就是创建PCB,加载代码+数据。

而这个加载的操作就是程序替换。


http://www.mrgr.cn/news/29028.html

相关文章:

  • mysql学习教程,从入门到精通,SQL 更新数据(UPDATE 语句)(17)
  • 右值生命周期的延长
  • linux gcc 静态库的简单介绍
  • 代码随想录打卡Day35
  • 计数服务怎么设计?
  • 【读书】原则
  • 牛客周赛 Round 60(A,B,C,D,E,F)
  • 等保测评:企业如何建立安全的开发环境
  • MyBatis 源码解析:Mapper 文件加载与解析
  • C++知识点示例代码助记
  • leetcode 42 接雨水
  • 深入解析:如何通过网络命名空间跟踪单个进程的网络活动(C/C++代码实现)
  • Java项目——苍穹外卖(一)
  • 01Frenet与Cardesian坐标系(基础知识)
  • echarts 自定义标注样式自定义tooltip弹窗样式
  • Linux | 进程间通信:管道、消息队列、共享内存与信号量
  • 815. 公交路线(24.9.17)
  • Cesium绘制可编辑线
  • Zabbix的安装与基本使用(主机群组、应用集、监控项、触发器、动作、媒介)
  • 【Android】Handler用法及原理解析