【Linux】:进程控制1(创建、终止、等待)

news2024/9/28 9:19:30

目录

1.进程创建

2.进程终止(退出)

2.1 什么是进程终止

2.2 进程退出的场景(原因)

2.3 进程退出码

2.4 错误码errno

2.5 进程常见的退出方法

正常终止

从main函数返回

调用库函数exit

系统接口_exit

3.进程等待

3.1  进程等待的概念

3.2 僵尸进程为什么要被回收

3.3 wait和waitpid

wait

waitpid 

参数:status

宏WIFEXITED、WEXITSTATUS

3.4 等待失败

3.4 进程等待的原理 

3.5 非阻塞轮询

1.进程创建

在Linux系统中,我们一般用系统调用接口fork创建进程。

当前进程调用fork()创建一个子进程,当前进程则是该子进程的父进程。一个父进程可以有多个子进程,但一个子进程在同一时间段只能有一个父进程(父进程可以改变)。

更详细的信息请查看:浅析fork()和底层实现 - tp_16b - 博客园 (cnblogs.com)

注意:当系统中有太多的进程或实际用户的进程数超过了限制 ,fork调用可能会失败,这时需要释放进程资源。

2.进程终止(退出)

2.1 什么是进程终止

结束一个进程的生命可以分为两个步骤:进程退出和进程销毁。进程退出主要是释放进程的资源,使进程成为僵死(TASK_ZOMBIE)状态;进程销毁主要是通过父进程,释放僵死进程的各种信息。

当前进程被设为TASK_ZOMBIE僵死状态后会向其父进程发生SIGCHLD信号,父进程收到SIGCHLD信号后,才会销毁僵死状态的子进程,。

父进程通过调用wait()waitpid()函数等待SIGCHLD处理僵死进程

 进程退出和销毁过程分析 - 66Ring's Blog

终止进程的结果如下:

  • 进程中的任何剩余线程都标记为终止。
  • 将释放进程分配的任何资源。
  • 所有内核对象都已关闭。
  • 进程代码将从内存中删除。
  • 设置进程退出代码。
  • 进程对象已发出信号。

终止进程 - Win32 apps | Microsoft Learn

2.2 进程退出的场景(原因)

根据程序是否运行完毕,我们可以把进程划分为运行完毕正常退出运行异常非正常退出

对于正常退出的进程,我们又可以根据程序运行的结果,划分为结果正确和结果不正确。

那么我们是如何判断退出的结果正不正确呢?以及不正确有哪些原因呢?

2.3 进程退出码

任何进程退出时,都会留下退出码,操作系统根据退出码可以知道进程是否正常运行。退出码是0到N的整数(不同的操作系统N可能不同),通常0表示正常退出,其他数字表示不同的错误。

在Linux系统中,我们可以用“$?”获取上一个进程的退出码。我们编写一个程序,main函数的返回值设置为3,我们执行该源文件生成的可执行文件,可以用“echo”将“$?”的内容打印到终端。退出码是一个整数,不同的整数对应着不同的退出信息,我们可以用strerror打印退出码对于的退出信息。

CentOS7系统中一共有134个退出码。 

我们用ls列出一个当前目录下不存在的文件yls610,发现终端显示报错,报错信息刚好是退出码2对应的信息,我们用“echo $?”输出上一个进程返回的退出码也是2,

 再“echo $?”输出,结果是0,这是为什么呢?因为上一个“echo $?”是success退出,虽然echo是内建命令,不创建进程,但它还是会调用接口。

2.4 错误码errno

C语言标准库errno.h中有个宏errno,该宏定义为一个int类型的左值, 存储最近一次函数调用产生的错误码,也就是最近一次返回的错误码。

在纯C语言的环境下,我们可以利用这个errno和strerror,用strerror读取错误码对应的错误信息,并将错误码作为进程的退出码,让父进程也知道错误信息。我们编写一个程序,验证一下,

我们讨论了程序运行完毕的情况。但是对于一个进程,我们一般先判断程序是否异常,再判断程序运行的结果是否正确,因为程序出现了异常,程序就无法正常运行,就难以运行完毕(不会产生错误码和退出码),这时我们又该怎么办呢?(两点:为什么发生异常,以及是什么样的异常)

我们知道,C语言中,我们不能对野指针解引用,如果访问野指针,可能抛异常,这是因为访问野指针,本质上是对虚拟地址的访问,但这个虚拟地址在页表中并没有建立映射关系,或者建立了映射关系,但权限为只读;C语言中我们不能对0整除,整除时会触发浮点异常,状态寄存器会发出对应的信号。

而这些异常问题,都是通过发出信号告诉操作系统使进程提前退出。 

状态寄存器_百度百科 (baidu.com)

浮点异常 - 活着的虫子 - 博客园 (cnblogs.com)

 如何证明异常是发送信号使进程退出的呢?

我们之前学习了用“kill -9”指令杀死一个进程,“kill -9”就相当于一个信号,信号8SIGFPE是“Floating point exception”,浮点数异常;信号11SIGSEGV是“Segmentation fault”,段错误,我们可以用信号8和信号11使一个进程退出。执行该文件生成的可执行文件,在root权限下用kill -8杀死该进程。

用kill -11杀死进程, 

2.5 进程常见的退出方法

正常终止

  • 从main函数返回
  • 调用库函数exit

在main函数中,exit中的参数status表示退出码;在进程中调用exit,表示退出该进程。

  • 系统接口_exit

参数:status 定义了进程的终止状态,父进程通过wait来获取该值。

说明:虽然status是int,但是仅有低8位可以被父进程所用。所以_exit(-1)时,在终端执行$?发现返回值是255,

 exit和_exit都可以终止进程,他俩有什么区别呢?

其实exit最后会调用_exit, 但在调用_exit之前,还做了其他工作:

1. 执行用户通过 atexit或on_exit定义的清理函数。

2. 关闭所有打开的流,所有的缓存数据均被写入

3. 调用_exit 

由于_exit不刷新缓冲区,我们可以得出一个结论,缓冲区不在内核区,在用户区,因为操作系统不做浪费资源的事。

3.进程等待

3.1  进程等待的概念

狭义的进程等待是指,当子进程退出,进入僵尸状态,父进程会对子进程的状态进程检测并回收,如果子进程没有进入僵尸状态,父进程会等待子进程进入僵尸状态再回收。

3.2 僵尸进程为什么要被回收

基本概念: 

 僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。僵尸进程无法被“kill -9”。

背景: 

父进程创建子进程,目的就是让子进程执行自己创建的任务,任务的执行情况最终需要父进程知道。 

unix提供了一种机制保证只要父进程想知道子进程结束时的状态信息, 就可以得到。这种机制就是: 在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。 但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等)。直到父进程通过wait / waitpid来取时才释放。

冲突:

但这样就导致了问题,如果父进程不调用wait / waitpid的话, 那么保留子进程的那段信息就不会释放,一直不释放,就会造成内存泄漏。 进程ID - 维基百科,自由的百科全书 (wikipedia.org)

所以,为什么要有进程等待?

一是因为父进程要识别子进程执行任务的最终情况;

二是因为不进行“进程等待”(调用系统接口wait或waitpid回收子进程)会造成”内存泄漏“。

3.3 wait和waitpid

wait

 pid_t wait(int*status);

返回值:

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

参数:

输出型参数,获取子进程退出状态,不关系可设置为NULL。

我们编写一个程序,

执行,

可以看到,子进程15580打印五次后退出,变为僵尸进程,最终被父进程调用wait成功回收。

waitpid 

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是如何获取子进程的退出信息呢?

 我们是通过输出型参数status获得进程退出的结果。

输入型参数是指这个参数的值已知,由函数外传给函数里使用.输入型参数是指这个参数的值未知,要通过函数传出来。

我们编写一个可执行文件,来检测一下,

 对于这个问题,我们必须认识一下status,

参数:status

wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。如果传递NULL,表示不关心子进程的退出状态信息。非空,操作系统会将子进程的退出信息用该参数反馈给父进程。

由于status返回的是进程退出信息,根据进程退出的场景,我们可以将退出信息分类两类,一类是异常退出,一类是运行完毕后退出。

由于进程中异常在先,一般程序执行中出现了异常,就不会运行完毕,而status是一个整形,有32位,采用位图的算法思想,不同的bit位代表不同的退出原因,其中后低7位表示进程异常的信息,低7位全为0,表示进程没有异常,第低8位是core dump标志;次低8位(15-8)表示程序正常执行完毕返回的错误码,如果没有出现异常(低8位全为0),且次低8位全为0,表示程序运行结果正确,上面子进程的错误码(退出码)为1,status的后16位为0000 0001 0000 0000,转换为十进制就是256。具体细节如下图(只研究status低16比特位)

核心转储 - 维基百科,自由的百科全书 (wikipedia.org)

关于上面的程序,有些同学可能有疑惑,为什么要用wait的参数status呢?我们直接将status定义成全局变量不行吗?exit返回全局变量不可行吗?

不可行,因为进程具有独立性,进程之间的数据、代码、PCB实例、内存空间等都是相对独立的,进程数据之间的传递一般都要通过调用系统接口实现。 

我们将上面代码中第二个if语句改为:

重新生成可执行文件并执行, 

退出码就是“1”了。

我们编写一个“对空指针”解引用,看看打印结果,

在子进程中整除0,

宏WIFEXITED、WEXITSTATUS

如果用"status&0x7F"表示是否异常,“(status>>8)&0x7F”表示错误码,一些用户可能看不懂,所以标准库中定义了两个宏WIFEXITED、WEXITSTATUS,分别代表这两个运算,

我们用这两个宏代替移位运算,

 执行,

结果无误。

3.4 等待失败

什么时候父进程会等待失败呢?(waitpid返回值为-1)

父进程等待到的进程不是它想等待的进程,父进程的儿子是张三等来的却是李四,父进程就会等待失败。我们看下执行结果,

3.4 进程等待的原理 

父进程通过且必须通过操作系统管理的调用接口wait或waitpid获取子进程的PCB实例中的成员exit_code和exit_signal。

上面我们演示的“进程等待”,全是阻塞式等待,父进程等等待期间,只能单一地等待子进程退出变为僵尸进程,不能进行或参与其他任何操作。这种等待效率低,“我等的时候啥事都不能干,手机都不能刷”,但实现简单。还有一种等待方式,就是隔一段时间询问一次子进程是否退出了,没退出父进程就去干自己的事,不会一直等待子进程退出。这种等待方式叫做非阻塞轮询。

3.5 非阻塞轮询

waitpid函数的第三个参数option,设置为0就表示“阻塞等待”,设置为“WNOHANG”就表示非阻塞等待。

夯住(Hang)是指程序仍在运行,卡在某个方法调用上,没有返回也没有异常抛出;卡住时间从几秒到几小时不等。

 

子进程和父进程在交叉打印。如果我们把WNOHANG改为0,就是阻塞等待, 这样父进程会在子进程运行完才会继续运行,

 

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

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

相关文章

[qt] 多线程应用02

源码: 点击此处 一 UI 1.1 效果 1.2 代码 首先定义一系列的控件和按钮,用来显示Tcp连接数据信息。 QLabel *m_serverNameLabel;QLineEdit *m_serverLineEdit;QLabel *m_portLabel;QLineEdit *m_portLineEdit;QDateTimeEdit *m_d…

【书生大模型实战营第三期】基础岛 第3关 浦语提示词工程实践

欢迎大家参与第三期书生大模型实战营!!! 1. 基础任务 背景问题:近期相关研究发现,LLM 在对比浮点数字时表现不佳,经验证,internlm2-chat-1.8b(internlm2-chat-7b)也存在这…

数据结构——排序(2):选择排序+交换排序

目录 一、选择排序 (1)直接选择排序 ①思路 ②过程图示 ③代码实现 ④代码解释 ⑤优化 1.代码实现 2.过程图示 3.代码解释 4.注意 ⑥直接选择排序的复杂度 (2)堆排序 ①注意 ②代码实现 二、交换排序 &#xff08…

一键生成!AI绘画、视频制作与写作神助攻

市面上有各种各样的AI助手,它们覆盖了文字处理、图像编辑、视频制作到语音识别等众多领域。这些工具设计得既实用又友好,几乎每个人都能找到适合自己的那一款。 1. 文字处理助手 文本生成:帮你快速创作文章、博客等内容。 内容优化&#xff…

操作ArkTS页面跳转及路由相关心得

本文为JS老狗原创。 当前端不得不关注的点:路由,今天聊一聊鸿蒙相关的一点心得。 总体上套路不意外,基本就是(尤其是Web)前端那些事:维护路由表、跳转带参数、历史堆栈操作,等等。 历史原因&…

越秀·星汇城|大城好生活

建筑,是美好生活的载体。而户型则是住宅的灵魂,一处好的居所,承载理想生活盛放。 细腻的美好藏在生活各个角落,星汇城以24小时贯穿的细节享受,重新定义幸福该有的舒适。诉说生活的达观,臻藏岁月静好。 8:…

windows系统获取网卡信息

在抓包或者使用socket,或者监听端口时,如果使用的是pcap4j类库,就会用到网卡信息,那么怎么查看本机的网卡信息呢,Linux的比较方便,直接通过ifconfig命令就能看到, windows的比较麻烦一点&#x…

【名单】山东省2024年度第一批DCMM贯标试点企业名单

​各市工业和信息化局: 为深入贯彻全省工业经济头号工程推进会议有关部署,全面落实《关于加快数字经济高质量发展的意见》《2024年“促进经济巩固向好、加快绿色低碳高质量发展”政策清单(第一批)》等文件要求,充分发…

从根儿上学习spring 十 之run方法启动第四段(4)

我们接着上一节已经准备开始分析AbstractAutowireCapableBeanFactory#doCreateBean方法,该方法是spring真正开始创建bean实例并初始化bean的入口方法,属于核心逻辑,所以我们新开一节开始分析。 图12 图12-530到536行 这几行的主要就是创建b…

先天亏钱圣体!谢瑜、陈梦、全红婵夺冠后,我看到了最残酷的社交真相——早读(逆天打工人爬取热门微信文章解读)

我怎么寻思这是普通事故 引言Python 代码第一篇 洞见 谢瑜、陈梦、全红婵夺冠后,我看到了最残酷的社交真相第二篇 亏麻了结尾 没想到是辆切糕车 引言 昨天文章的数据不错呀 200 的阅读 20的收藏 10:1已经是很高的比例了 再来干货吧 以后大家要是做视频 心中看到这…

Spring Cloud微服务项目聚合Swagger文档

在微服务架构中,每个服务通常都有自己独立的 API 文档。为了方便管理和查看所有服务的接口文档,我们需要将这些文档进行聚合。Spring Cloud 与 Swagger 的结合可以帮助我们实现这一目标。本文将介绍如何在 Spring Cloud 微服务项目中聚合 Swagger 文档&a…

72 成员方法、类方法、静态方法、抽象方法

在面向对象程序设计中,函数和方法这两个概念是有本质区别的。方法一般指与特定实例绑定的函数,通过对象调用方法时,对象本身将被作为第一个参数自动传递过去,普通函数并不具备这个特点。 class Demo:passt Demo()def test(self,…

html+css网页设计公司网站模版3个页面 无js 静态页面

htmlcss网页设计公司网站模版3个页面 无js 静态页面 网页作品代码简单,可使用任意HTML编辑软件(如:Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作)。 获取源…

推送给女朋友让她自己学习打光去(Stable Diffusion进阶篇:Imposing Consistent Light)

大家好我是极客菌!!! 对于学过stable diffusion的小伙伴来说,forge UI和Comfy UI会更加熟悉一些。在IC-Light发布后,Openpose editor的开发者将其制作成了一个Forge UI上的插件。 **https://github.com/huchenlei/sd-…

国内有哪些可以交易上证50etf期权的平台?

随着期权交易的普及,越来越多的投资者开始关注期权交易app平台。期权开通方式有券商和期权分仓平台两种,目前期权交易费用是7元左右一张,期权佣金是可以根据券商的证券范围进行调整的,下文为大家科普国内有哪些可以交易上证50etf期…

接口入门(企业常见使用,一分钟搞定版)

目录 1、接口的定义 定义位置 接口内容 2、接口的使用 正常实现接口 接口当做函数参数 匿名实现接口 3、OPPO便签接口具体分析 总结一下: 1、接口的定义 定义位置 可以写在类中,但注意现在接口名字是 类名.接口名 可以单独写在一个文件 接口内…

Linux系统使用Docker安装RStudio服务并实现任意浏览器远程访问

文章目录 前言1. 安装RStudio Server2. 本地访问3. Linux 安装cpolar4. 配置RStudio server公网访问地址5. 公网远程访问RStudio6. 固定RStudio公网地址 前言 RStudio Server 使你能够在 Linux 服务器上运行你所熟悉和喜爱的 RStudio IDE,并通过 Web 浏览器进行访问…

OpenCV图像滤波(8)getGaborKernel()函数的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 该函数返回 Gabor 滤波器系数。 Gabor 滤波器在图像处理中非常有用,特别是在纹理分析、特征提取和边缘检测等领域。 函数原型 Mat c…

借助树状数组的思想实现cuda版前缀和

昨天面试快手,面试官出了一个cuda编程题–实现前缀和。当时没有做出来,一直在思考是否有类似于规约树这样的解法,感觉好难……面试结束后搜了一下cuda前缀和的介绍,发现该问题是一个经典的cuda编程问题,NVIDIA很早之前…

论文解读,神经网络全梯度表示《Full-Gradient Representation for Neural Network Visualization》

导语 这篇论文介绍了一种新的工具,称为全梯度,用于解释神经网络的响应。这个全梯度的概念将神经网络的响应分解为两个部分:输入灵敏度和每个神经元的灵敏度分量。 输入灵敏度:输入灵敏度指的是对于神经网络输出的影响程度。它反…