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

Linux——进程终止/等待/替换

前言

  本章主要对进程终止,进程等待,进程替换的详细认识,根据实验去理解其中的原理,干货满满!

1.进程终止

概念:进程终止就是释放进程申请的内核数据结构和对应的代码和数据

进程退出的三种状态

  • 代码运行完毕 结果正确  
  • 代码运行完毕 结果错误
  • 代码中止异常

我们在学习C语言的时候,写main函数,一般都会写return 0;main函数的返回值,通常就代表程序的执行情况,0代表成功,非0代表代码运行完毕,结果错误,不同的值代表不同的错误

当父进程创建子进程是为了让子进程完成某种任务,当子进程结束,肯定要将执行结果返回给父进程,让父进程知道是什么情况,我们知道子进程结束会保留task_struct,等待父进程获取结果信息,此时子进程就是僵尸进程,执行结果,也就是返回值就会存放到task_struct中,被父进程获取

1.1退出码

进程结束返回的一个状态 通常是一个整数值 

echo $?

打印最近一个进程退出时的退出码

errno:当库函数调用失败或者系统调用失败 他们通常会将一个特定的错误代码赋值给errno

 strerror是C语言的一个库函数,该函数接收一个errno的错误代码作为参数, strerror负责将这些错误代码转换为具体的错误描述信息,一共有134条

我们可以看到0表示成功  1表示操作不被允许  2表示没有这个目录或文件 3表示没有这个进程

相信后面的大家也可以读懂  这样我们就可以根据退出码知道我们的错误是什么了

 

此时在当前目录并没有c.txt文件,ls是C语言写的一个程序,当执行完程序,发现文件不存在,此时的退出码是2,不就是上面的2号找不到文件吗 

1.2进程常见的退出方法

1.2.1从main返回

我们知道main函数是程序的入口,当main函数结束,也return了,进程也就结束了

其他的函数只表示调用函数完成了 返回退出码 并不代码进程结束

1.2.2exit

exit手册内容   exit是直接结束进程,引起进程终止  它需要一个参数,就是状态(进程退出码)

1.2.3_exit 

_exit手册内容  用于终止进程的系统调用  通过实验我们发现和exit一样都可以终止进程

 1.2.4exit和_exit区别

补充:我们知道只有OS才可以杀掉进程 库和系统调用是上下层的关系 库调用系统调用

相同点:都可以终止进程

不同点:

  • exit是标准C库函数 _exit是系统调用
  • exit会刷新缓冲区 _exit不会刷新缓冲区

实验:通过下面的实验我们也可以验证exit不会刷新缓冲区

2.进程等待

在理解进程等待前,我们先来想一下进程为什么要进行等待呢?

  • 回收子进程资源(处理僵尸进程)
  • 获取子进程退出信息

我们知道当子进程退出时,task_struct不会被释放,需要父进程回收资源,当父进程一直不管时,就可能会造成僵尸进程,可能会出现内存泄露

2.1wait 

手册内容:wait的参数status是一个输出型参数 在后续的waitpid我们会详细了解

wait会等待任意一个子进程 回收成功会返回回收的pid

接下来我们来使用一下wait 

创建一个子进程 让他跑五次 当子进程结束时,让程序暂停10s,这个时候父进程还没有回收资源,所以子进程此时就是僵尸进程,根据子进程状态我们可以看到是Z,父进程回收子进程资源,解决了僵尸进程,返回了子进程的pid,子进程资源被全部释放,剩余父进程,程序再休眠10s,最后父进程进程也结束

 2.2waitpid

pid_t waitpid(pid_t pid, int *status, int options);  waitpid共有三个参数

接下来会一个一个拆开分析:

 2.2.1pid_t pid

pid_id的值有四种

等待指定pid或者等待任意子进程 

 如果等待失败 会出现什么样的情况呢?

2.2.2int *status

输出型参数 存储进程退出时的状态信息 不关心状态信息设置NULL

我们来进行测试一下:发现出现了一些问题

status不能当做整型看待,可以当做位图来看待,前16位不考虑,次8位是退出状态,那么此时应该前7位是0,然后是1,后面还有8个0,因为是2进制,所以应该是2的8次方,也就是256!

当除数为0时就会使程序出现异常

        其实exit code 和 exit signal都存放在子进程的task_struct中,当子进程是僵尸进程,等待父进程通过操作系统调用回收子进程资源,获取子进程的退出信息

        我们在上述的实验是通过位操作来提取信息的,真正的OS是使用宏,其实就是封装了一下位操作

  • WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是
否是正常退出,程序是否异常,=0为真)
  • WEXITSTATUS(status): WIFEXITED非零,提取子进程退出码。(查看进程的
退出码)

2.2.3int options 

补充:

  • 阻塞调用:当一个程序发起操作时,程序会暂停操作,直到这个操作完成并返回结果,在执行操作时,程序不能做任何事,也就是子进程在执行任务时,父进程一直在wait阻塞
  • 非阻塞调用:当一个程序发起操作,不会等操作完成,而是立即返回,执行后续的操作,程序可以定期检查这个操作是否已经完成,这个操作叫做非阻塞轮询

我们从手册里可以看到options是有几个选项的

  • 默认是0:阻塞调用,如果子进程还没有退出父进程就会一直阻塞在那里
  • WNOHANG:非阻塞调用

waitpid的返回值可以是 -1  0  >0

-1表示失败

0表示 调用结束 但是子进程没有退出

>0 子进程结束

非阻塞轮询实验,将waitpid的第三个参数设置为WNOHANG,非阻塞调用,这样父进程就不会一直等待子进程完成任务,而是立即返回,然后定期去询问子进程完成任务了吗?

非阻塞调用实验:我们可以通过函数指针回调函数来操作

通过实验我们可以看到在子进程执行任务时,父进程也在执行其他任务

3.进程替换 exec

进程替换就是OS根据指定的程序文件路径和参数,将新程序的代码和数据加载到当前进程的地址空间中,覆盖原来的进程内容,从而实现进程替换

程序替换错误返回-1 没有成功返回值 

当我们知道程序替换会将原先的程序进行覆盖,我们原先的程序就没有了,所以我们一般会创建子进程去执行程序替换,这样父进程的代码数据也不会丢失了!! 

3.1进程替换的原理

我们先来使用一下execl,我们可以发现第一句printf执行了,然后执行程序替换,然后就没有输出了,我们来了解一下程序替换的原理!其实在上面就已近谈到了,就是将新程序的代码和数据加载到当前进程的地址空间中,覆盖替换原来的进程内容,从而实现进程替换

在程序替换的过程中,并没有创建新的进程

3.2程序替换接口函数

我们在上面的手册中可以看到6个接口函数,接下来我们学习四个 后续的大家肯定就都懂

还有一个是系统命令 execve  我们在上面看到的6个接口函数其实都是需要去调用execve

在上层进行封装 为了应对各种各样的场景 最后会统一转化 调用系统调用execve

3.2.1execl

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

execl的l可以看做是一个list,第一个参数就是路径+程序名,第二个参数就是命令,在命令行怎么写,这里我们就怎么写,...是可变参数列表的意思,因为我们不知道命令有几个,最后要以NULL结尾,表明参数已传完!!

 3.2.2 execlp

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

 execlp的l看做list,p看做PATH,第一个参数就是要执行的文件名,execp会自动在环境变量PATH中查找命令,第二个参数就是命令,同上!

3.2.3execv 

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

execv的v可以看做是一个vector,第一个参数是要执行的路径+文件名,第二个参数是一个指针数组,也就是命令行参数表 

3.2.4execvp

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

execvp的v可以理解为vector,p理解为PATH,第一个参数就是要执行的文件,第二个是指针数组,就是指命令行参数表,相信大家看到这里一看就看懂了!!

3.2.5  execvpe

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

execvpe的v可以看做vector,e看做environment,第一个参数就是要执行的文件,第二个就是命令行参数表,第三个也是一个指针数组,是环境变量表,这里的环境变量表会进行覆盖替换父进程的环境变量表,当然也有方法在原始的环境变量表的基础上进行添加!!

补充 

putenv 添加环境变量的参数

envrion 访问当前环境的整个环境列表

解决方案 :

  • 不使用需要传带env的参数 直接进行putenv
  • 使用传env的参数 putenv  envrion

相关文章:

  • # 前后端分离象棋对战项目开发记录
  • AE/PR插件 转场创建大师专业版 Transition Master Pro v2.0.2 Win+使用教程
  • 2025年- H21-Lc129-160. 相交链表(链表)---java版
  • Spring Boot 集成 Elasticsearch 的详细步骤
  • new的几种形式
  • Python 数据智能实战 (7):智能流失预警 - 融合文本反馈
  • 创意效率双提升,AIGC让增长更轻盈
  • Spring Boot中集成Guava Cache或者Caffeine
  • 第 1 篇:起点的选择:为何需要超越数组与链表?
  • 菲索旋转齿轮法:首次地面光速测量的科学魔术
  • 信息收集新利器:SSearch Chrome 插件来了
  • ROPE(旋转位置编码)简述
  • 当神经网络突破摩尔定律:探索大模型时代的算力新纪元
  • AimRT从入门到精通 - 03Channel发布者和订阅者
  • PDF智能解析与知识挖掘:基于pdfminer.six的全栈实现
  • 【论文阅读一】掌握高效阅读法,开启学术研究新旅程:S. Keshav教授论文阅读的三遍法
  • 华为OD机试真题 Java 实现【水库蓄水问题】
  • 杭电oj(1180、1181)题解
  • 【算法应用】基于鲸鱼优化算法WOA求解VRPTW问题
  • 《缓存策略:移动应用网络请求的“效能密钥” 》
  • 陈芋汐世界杯总决赛卫冕夺冠,全红婵无缘三大赛“全满贯”
  • 马上评|提供情绪价值,也是文旅经济的软实力
  • 2025上海车展圆满闭幕,共接待海内外观众101万人次
  • 高速变道致连环车祸,白车“骑”隔离栏压住另一车,交警回应
  • Meta一季度净利增长三成:上调全年资本支出,受关税影响亚洲出口电商广告支出减少
  • 解放日报:硬科企业由此迈出“市场第一步”