【Linux笔记】进程等待与程序替换

这篇具有很好参考价值的文章主要介绍了【Linux笔记】进程等待与程序替换。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、进程的终止

1、进程退出码

在讲解进程的终止之前,先要普及一下进程的退出码概念。

我们父进程之所以要创建子进程,就是为了让子进程运行不一样的任务,那么对于子进程执行的这个任务执行完毕后的结果是否正确或者是否出差错,我们父进程要对这个结果进行验收。

从而就引出了进程退出码和进程退出信号的概念,其中退出码就是main函数的返回值:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

我们在命令行中可以使用“echo $?”这串指令来查看最近一个进程的退出码:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

如果我们将main函数的返回值改成其他数,那echo $?查出来的值也会随之变化:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

一般默认的退出码为0则表示程序运行正确,是其他数则表示程序出了问题,即运行失败。

所以0表示成功,非0就表示失败,具体非零的数字那个对应的是哪种错误,我们可以自己定义。

当然C语言内置的也有一套错误码对应的字符串解释,我们可以调用strerror这个函数来查看:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

2、进程终止的方法

在main函数中直接return

在main函数中直接return的方法其实上面已经演示过了,这个其实很容易理解,main函数是程序的入口也是程序结束的地方,所以我们父进程需要回收的就是main函数的返回情况。

使用exit接口

还还有一种方式就是,使用exit接口,这是一个C语言提供的接口:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

如果仅放在main函数中,其实它和return是同级的,但是它其实比return的级别还要高,因为它放在任何地方都可以让程序直接结束:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

从其运行的结果我们可以看出exit的级别是要比return高的,因为程序的退出码为5,而且后面的打印语句也没有执行。

使用_exit系统调用

还有一个与exit类似的接口_exit也可以用来结束进程:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

它是一个系统调用,它的正常使用其实是和exit接口执行的结果一样的,比如把上一段程序中的exit改成_exit后,我们会发现它们执行的结果一模一样:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

exit与_exit的区别

那它两到底有什么区别呢?

它两的区别其实就在于它们前者是C库函数,后者是系统调用。

我们先来看一个现象:
【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

我们会发现,程序执行后确实是成功了,因为退出码正确了,但是程序并没有输出任何结果。

不急我们再来对程序进行微微的改动,在printf里面加上\n:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

加上\n之后就打印出来了。

然后我们可以再对比一下exit:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

经过观察会发现,exit不论是否加上\n它都有结果打印出来,那这是怎么一回事呢?

其实上面的结果的本质就是exit在退出前会刷新缓冲区,而_exit在退出前不会刷新缓冲区。因为缓冲区的刷新条件之一就是遇到了\n,所以只要加上了\n,exit和_exit都会有结果输出,而从_exit退出之前没有刷新缓冲区的结果来看,我们可以推测出缓冲器肯定不在操作系统内部。

道理就是_exit是系统调用肯定是比库函数exit更接近操作系统内部的,但是偏偏exit刷新了,而_exit没有刷新,如果缓冲区是在操作系统内部,那exit都刷新了,为什么更接近操作系统的_exit没有刷新呢?

二、进程等待、

我们知道,如果子进程先于父进程退出,那么子进程就会变成僵尸进程,如果僵尸进程一直不被回收,就会造成内存泄漏问题。

所以,进程等待就是为了回收子进程从而解决子进程僵尸问题造成的内存泄漏而生的。

1、如何进程进程等待

wait的使用

解决进程等待,我们使用的是wait系统调用:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);
返回值:
成功返回被等待进程 pid ,失败返回 -1
参数:
输出型参数,获取子进程退出状态 , 不关心则可以设置成为 NULL
其中的它的参数我们可以先不管,直接传NULL即可。
【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++
运行结果:
【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

解释wait的阻塞等待

而且wait的等待方式为阻塞等待,所谓阻塞等待即父进程在等待子进程的过程中会一直停滞着,不会执行任何任务,下面以一个例子来演试一下:

代码:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

运行结果:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

从结果我们可以看出,在子进程执行期间,父进程其实也已经在执行了,但是一直到子进程执行完毕之后父进程才执行结束。所以在子进程执行期间,父进程一直在做阻塞等待。

waitpid的使用

waitpid相比于wait来说选择性更多,wait只能等待任意一个进程,而waitpid既能等待任意一个进程也能等待指定进程,wait只能阻塞等待,而waitpid既能阻塞等待也能非阻塞等待。

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

pid_ t waitpid(pid_t pid, int *status, int options);
返回值:
    当正常返回的时候 waitpid 返回收集到的子进程的进程 ID
    如果设置了选项 WNOHANG, 而调用中 waitpid 发现没有已退出的子进程可收集 , 则返回 0
    如果调用中出错 , 则返回 -1, 这时 errno 会被设置成相应的值以指示错误所在;
参数:
   pid
      Pid=-1, 等待任一个子进程。与 wait 等效。
      Pid>0. 等待其进程 ID pid 相等的子进程。
   status:
      WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常        退出)
      WEXITSTATUS(status): WIFEXITED 非零,提取子进程退出码。(查看进程的退出           码)
  options:
      WNOHANG: pid 指定的子进程没有结束,则 waitpid() 函数返回 0 ,不予以等待。若正常        结束,则返回该子进
      程的 ID
比如在上面的代码中,我们将wait直接改成waitpid,然后将第一个参数改成-1或子进程的id值,结果都是一样的:
【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++
其中第三个参数设置为0表示的是阻塞等待,第二个参数先不管。

解释waitpid的第二个参数

上面说过进程等待其实是为了解决子进程僵尸状态所造成的内存泄漏问题,但是进程等待还有第二个目的,就是检查子进程的运行结果,即获取子进程的退出码和终止信号。而这个工作就是有waitpid的第二个参数来做的。

wait和waitpid都有这个status参数,它其实是一个输出型参数,即通过外部传参的方式,在内部对参数进程修改,然后外部得到对应的状态。

这个status虽然是一个32位的整型,但我们并不整体使用它,而是只是用它的低16位:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

但是我们想直接通过打印出status的只来判断退出信息的话,是有点奇怪的,例如:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

正如上面所说的,status并不是整体使用的,而是在进程正常退出时,退出码“直接被放到次低八位”的:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

如果想要提取出10来,有两种方法,一种是直接进行位运算,一种是使用上面提供的接口:WEXITSTATUS。

位运算有点儿不好记,而且久了也容易忘,所以我这里就只演示使用函数的情况:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

再来演示一下进程异常退出的情况,进程异常退出时,退出码其实是没有意义的,所以我们主要看的是,终止信号。

比如我们可以设置一个常见的空指针异常:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

这里就只能使用位运算了,这里的位运算没什么,记住就行。

如果不想记的话,也可以用上面提到的WIFEXITED函数来直接判断子进程是否正常退出,只是不能看出进程异常对应的信号值:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

waitpid等待多个进程

那么如何使用waitpid来等待多个子进程呢?

其实很简单,我们创建多个子进程使用的是循环的方式,那我们在等待的时候也使用循环等待即可,先演示阻塞等待的。

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

演示waitpid的轮训等待(非阻塞等待)

waitpid的第三个参数options默认支持两种等待方式,阻塞等待和非阻塞等待。设置为0表示阻塞等待,而设置成另一个数(宏)WNOHONG则表示,非阻塞等待。

而如果我们直接像使用wait一样使用waitpid的非阻塞等待的话,就会发现父进程直接就结束了,并没有继续等:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

显然父进程是并没有成功回收子进程的。因为它先于子进程退出了。

如果我们想让如进程成功回收子进程,就必须使用循环的方式:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

三、程序替换

1、程序替换与创建子进程的区别

我们之前是通过创建子进程的方式来让进程“分流”的执行不同的任务,但是子进程执行的任务本质上还是父进程代码的一部分。如果我们想要让子进程执行新的代码和访问新的数据,不再和父进程有瓜葛,就得使用进程替换的方式。

2、进程程序替换的接口

先介绍execl接口

execl这个接口可以为我们执行进程替换的工作,所谓的进程替换其实并不是创建新进程,而是将进程的代码和数据替换:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

它的第一个参数表示你要替换的进程的路径+文件名,而后面的可变参数列表则表示你想要怎样执行这个进程,因为各种进程执行所对应的执行选项不同,所以这里需要用可变参数列表,需要注意的是在可变参数列表的最后,一定要传一个NULL,表示参数传递完毕。

先拿我们最常用的“ls”这个指令来演示:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

这样我们就可以在我们自己写的C语言代码中调用我们系统的指令了。

然后我们再来看一个现象:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

上面的结果中只打印了replace before而没有打印replace after,这其实就说明了进程替换其实是将代码替换掉的。代码都不同了,当然就没有打印啦。

然后我们再来看看它有没有创建新进程:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

从结果可以看出,前面创建的子进程后后面付进程等待到的子进程id是一样的,所以进程替换并没有创建新进程。

execlp:

execlp与execl的区别在于,execl需要传文件路径+文件名,而execlp只需要传文件名就行了,多出来的这个p可以理解为自带路径path的含义:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

execv:

这个接口,与之前两个不同之处在于,它后面的命令函参数是一个一个数字的形式传递的。他后面的这个v其实就是vector的意思:

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

【Linux笔记】进程等待与程序替换,Linux操作系统,linux,笔记,c语言,c++

剩下的几个接口其实在介绍完上面的三个接口之后,就可以类推出来他们的含义和用法了,如上所述,如果带‘l’则表示后面的命令函参数一列表的形式传递,如果带‘p’则表示它是自带路径的,传第一个参数的时候就只需要传文件名即可,如果是带'v'的,则表示他后面的命令行参数是以数组的形式传递。

以此类推即可。文章来源地址https://www.toymoban.com/news/detail-812615.html

到了这里,关于【Linux笔记】进程等待与程序替换的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [Linux]进程控制详解!!(创建、终止、等待、替换)

            hello,大家好,这里是bang___bang_,在上两篇中我们讲解了进程的概念、状态和进程地址空间,本篇讲解进程的控制!!包含内容有进程创建、进程等待、进程替换、进程终止!! 附上前2篇文章链接: Linux——操作系统进程详解!!(建议收藏细品!!)_bang___ba

    2024年02月15日
    浏览(11)
  • 【Linux】Linux进程控制 --- 进程创建、终止、等待、替换、shell派生子进程的理解…

    柴犬: 你好啊,屏幕前的大帅哥or大美女,和我一起享受美好的今天叭😃😃😃 1. 在调用fork函数之后, 当执行的程序代码转移到内核中的fork代码后 ,内核需要分配 新的内存块 和 内核数据结构 给子进程, 内核数据结构包括PCB、mm_struct和页表,然后构建起映射关系 ,同时

    2024年01月16日
    浏览(24)
  • Linux进程控制【进程程序替换】

    ✨个人主页: Yohifo 🎉所属专栏: Linux学习之旅 🎊每篇一句: 图片来源 🎃操作环境: CentOS 7.6 阿里云远程服务器 Good judgment comes from experience, and a lot of that comes from bad judgment. 好的判断力来自经验,其中很多来自糟糕的判断力。 子进程 在被创建后,共享的是 父进程 的代码

    2024年01月17日
    浏览(18)
  • [Linux 进程控制(二)] 进程程序替换

    首先,我们要认识到,我们之前fork()所创建的子进程,执行的代码,都是父进程的一部分(用if-else分流或者执行同样的代码)! 如果我们想让子进程执行新的程序呢? 执行全新的代码和访问全新的数据,不再和父进程有瓜葛,这种技术就叫做程序替换 ,下面我们就来学习一

    2024年03月14日
    浏览(25)
  • 软考学习笔记--操作系统-进程管理

    进程管理是一个具有独立功能的程序关于数据集合的一次可以并发执行的运行活动,是系统进行资源分配和调度的基本单位。相对于程序,进程是动态的概念,而程序是静态的概念,是指令的集合。进程具有动态性和并发性,需要一定的资源来完成任务。在大多数操作系统中

    2024年01月18日
    浏览(10)
  • 【Linux】操作系统&&进程概念

    冯·诺依曼结构也称普林斯顿结构,是一种将程序指令存储器和数据存储器合并在一起的存储器结构 。数学家冯·诺依曼提出了计算机制造的三个基本原则,即采用二进制逻辑、程序存储执行以及计算机由五个部分组成( 运算器、控制器、存储器、输入设备、输出设备 ),这

    2024年01月16日
    浏览(15)
  • Linux操作系统篇:进程

    我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系 为什么计算机要采用冯诺依曼体系呢? 在计算机出现之前有很多人都提出过计算机体系结构,但最终选择冯诺依曼是因为用比较少的钱就可以做出效率不错的计算机 截至目前,我们

    2024年03月18日
    浏览(16)
  • 【Linux】详解进程程序替换

             用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。 调用exec并不创建新

    2024年04月18日
    浏览(20)
  • 【Linux】进程程序替换

    👑作者主页:@安 度 因 🏠学习社区:安度因 📖专栏链接:Linux 进程创建时有两个目标: 执行父进程的部分代码,由自己编写的,通过 if else 分流,让子进程执行的对应任务。 执行和父进程完全不同的程序 执行和父进程完全不同的代码,

    2024年01月16日
    浏览(19)
  • 【Linux】—— 进程程序替换

    目录 序言 (一)替换原理 1、进程角度——见见猪跑  1️⃣ 认识 execl 函数 2、程序角度——看图理解 (二)替换函数 1、命名理解  2、函数理解 1️⃣execlp 2️⃣execv 3️⃣execvp 4️⃣execle 5️⃣execve 6️⃣execve (三)自制shell 总结 在前面的文章中,我已经详细的讲解了进程

    2024年02月12日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包