【Linux操作系统】进程控制

目录

  • 一、进程创建
    • 1.1 认识fork
    • 1.2 写时拷贝
  • 二、进程终止
    • 2.1 进程退出
    • 2.2 函数退出
    • 2.3 exit
  • 三、进程等待
  • 四、程序替换

一、进程创建

1.1 认识fork

fork函数是系统调用接口,用来创建子进程的
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
根据进程的pid,可以看出父进程fork后分为父进程和子进程,分开后的父进程就是原来的自己,然后多了一个子进程。父进程的父进程是bash
在这里插入图片描述

那么fork之后多创建一个子进程有啥用呢?既然多了一个进程,肯定是要做事情的,父进程一个不够,让子进程去做,同时父进程去做另一件事。怎样让它们同时各自做不同的事情?fork是有返回值的,让返回值对接下来的代码进行分流,给父进程和子进程不同的任务。
在这里插入图片描述

现象:
在这里插入图片描述
给父进程的id是子进程的pid,给子进程的id是0,为什么?后面再谈论

父子进程各自做不同的事情,验证:两个while循环:

在这里插入图片描述
在这里插入图片描述
两个进程可以同时进行,为什么?分析原理:进程是等于内核数据结构(PCB)+ 可执行程序(代码和数据),在fork之前,父进程有自己的PCB和代码数据,fork之后,该函数会多创建一个子进程,这个子进程也同时会有PCB,它是以父进程为模板创建的。那代码和数据呢?首先要清楚的一点是:代码和数据都是父进程提供的。也就是说子进程运行的代码是父进程给的,父子进程共享父进程的代码和数据。那它们两个执行不同的事情是怎样的呢?根据返回值id,id为0是子进程的执行流,id大于0,是父进程的执行流,父进程的代码被分开同时执行。还有一点,CPU只要遇到运行起来的进程,不管是谁,都是可以同时进行的(同时打开浏览器、音乐、游戏等)。总结一下就是:fork之后,代码共享父进程的

fork返回值的问题:

一、给父进程的id为什么是子进程的pid,给子进程的id为什么是0?

父进程与子进程是一对多的关系,即一般一个父进程有n个子进程,父进程要找到某个子进程对它进行控制等操作的话,必须要有该子进程的pi信息,否则怎么找到要找的子进程;而父进程只有一个,子进程找父进程是唯一的,所以返回0即可。

二、两个进程能同时执行,说明id从fork函数那里返回的有两个值,以前我们的认识是一个函数一个返回值,为什么fork有两个返回值呢?

fork函数的作用是多创建一个子进程(也可以创建多个),在其函数内部的代码中就已经实现好了,也就是说,父进程和子进程在fork函数内部就已经分流了,各自执行不同的任务,所以也就可以返回两个返回值,父进程返回父进程的id,子进程返回子进程的id

三、就算有两个返回值,可是只有一个变量id,为什么一个id既可以等于0,又可以大于0?

与进程地址空间有关,进程之间是具有独立性的,互不影响对方。

1.2 写时拷贝

为什么父子进程代码共享?

本质是通过页表指向物理内存同一块区域。磁盘中的可执行程序加载到内存,物理内存开辟空间放代码和数据。父进程在地址空间申请的虚拟地址,代码区的的虚拟地址通过页表的映射关系找到物理内存的代码,数据区同理。fork之后,子进程是被按照以父进程位模板创建的,所以地址空间和页表是以拷贝发方式给子进程,父子进程的虚拟地址是相同的,页表也是,在没有对一方进行写入的情况下,默认子进程的地址空间的虚拟地址通过页表指向的是前面父进程在物理内存中开辟的代码和数据,所以父进程和子进程共享代码。

在这里插入图片描述

如果对子进程重新写入,会发生写时拷贝,父子进程各自的一份数据
在这里插入图片描述

为什么要有写时拷贝?

1.为什么子进程被创建,父进程不直接把数据给子进程?主要有两个原因:资源问题和成本问题。假如一个子进程创建出来后,用户并没有使用它,或者是没有完全使用,那么如果子进程被创建要单独给它数据的话,不就是资源浪费了吗,所以默认先让子进程与父进程的资源共享,当需要用子进程时,再按需申请资源。如果每次创建的子进程都要有自己的数据,创建成本增加,更何况有时候该子进程没有被使用。2.写时主要是修改,申请的空间拷贝父进程的数据,对数据重新进行写入,有了写时拷贝可以增加程序运行确定性,除了申请空间的位置不一样,其他的是一样的,这样可以提高使用性。

如何做到写时拷贝?

该过程与页表有很大的关系。首先,页表不仅有前面说的只放虚拟地址和物理地址,还有很多选项,其中有一个选项是权限,r、w、rw。看以下代码,我们输入一个字符串,然后对它的首字符修改,结果运行出错,不能修改。原因:从语言上的角度是,该字符串在常量区,具有常属性,不能被修改。在操作系统的角度是,该字符串常量其实是虚拟地址,对字符串的首字符修改,也是虚拟地址,修改就是写入,这个过程必离不开虚拟地址到物理地址的转换——通过页表,但是页表上表示该虚拟地址的权限是只读,也就是不能写入,所以操作系统就不让用户修改。

在这里插入图片描述

为什么数据段是只读?

fork后子进程被创建,数据段是只读,那么转换就会出问题,这个就是缺页中断,于是操作系统就会采取措施,解决这个问题。只要有一方有写入,操作系统就会把页表中权限选项改为读写,此时就可以进行写入了。也就说有转换的问题,即缺页中断,因为是只读的,操作系统知道这个情况,就会发生写时拷贝,重新开一块空间给子进程,同时将权限变为读写。(告诉操作系统有进程要写入了,让操作系统发生写时拷贝;怎么告诉操作系统,因为某一方有写入,但权限是只读,页表转换出现问题,即缺页中断,此时这个消息会给操作系统;操作系统的做法:写时拷贝,在物理内存新开空间连同父进程的数据给子进程,子进程可以重新写入数据,同时页表的权限改为读写,修改权限是在子进程重新写入数据之前已经做好工作了)

fork失败原因: 一、系统中有太多进程;二:实际用户的进程数超过了限制

二、进程终止

2.1 进程退出

main函数的返回值,叫做进程的退出码。一般情况下0表示进程执行成功,非0表示失败。非0表示失败的错误码比如:1、2、3、4、5等,不同的数字表示不同的错误原因。可以使用echo $?查看退出码:后面一次的退出码是0表示该指令执行成功,它的默认退出码是0
在这里插入图片描述
在这里插入图片描述
错误码要转化为错误描述。 有两种:一是语言和系统自带的方法,转化为错误码
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二是自定义:
在这里插入图片描述
在这里插入图片描述
main函数return,表示进程退出;其他函数退出,表示该函数调用完毕

2.2 函数退出

errno查看函数的执行情况——成功、失败、错误原因。只读不会在当前目录创建新的文件
在这里插入图片描述
在这里插入图片描述
所以进程退出的情况有三种:进程代码执行完,结果是正确的、不正确的和未执行完,进程出异常的。进程出异常的本质是收到了异常信号,每个信号都有自己的编号,不同的信号编号表示异常的原因,下面见见信号:
在这里插入图片描述
其实评估一个进程的最终情况,只要看两个数字,一个是信号编号,另一个是退出码,对应前面的三种情况:结果正不正确看退出码,有没有异常看信号。只要信号异常,不管结果正不正确都不能正常执行。

2.3 exit

查看接口信息:
在这里插入图片描述
可以使进程终止,参数是退出码;在任何地方调用,都可以终止进程
在这里插入图片描述
在这里插入图片描述
除了exit库函数,还有一个叫做_exit的系统调用接口,它的功能和前者基本类似,不同的是exit会支持刷新缓冲区,_exit不会。通过代码看现象:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

exit其实是_exit封装的库函数

三、进程等待

为什么要有进程等待?

  1. 子进程退出,如果父进程不管,子进程变僵尸,导致内存泄漏问题
  2. 回收子进程资源,获取退出信息

wait函数有两个,两个差不多是一样的,只是参数数量不同。
在这里插入图片描述
下面看看wait函数父进程回收子进程资源:wait的参数先设置为NULL
在这里插入图片描述
在这里插入图片描述
fork之后,谁先运行不确定,由调度器决定。但是最后一个退出,一定是父进程,因为它要回收子进程的资源。

获取子进程退出信息,同时解决僵尸:使用waitpid,参数中的pid解释如下,这里我们的代码直接用id来表示,第二个参数是输出型参数,获取退出信息。
在这里插入图片描述
在这里插入图片描述

获取status:
回收的子进程就是fork后父进程创建的子进程,rid表示的就是子进程的pid;但是status退出码为什么是256?因为status是局部使用的,有自己的格式,根据exit(1),所以退出码是1,格式如下。
在这里插入图片描述

1️⃣通过位运算来获取子进程status:
在这里插入图片描述
在这里插入图片描述

2️⃣通过宏获取子进程status:
在这里插入图片描述
在这里插入图片描述

可以通过变量获取子进程的退出信息吗?

不能,因为进程具有独立性,父子进程改了对方的数据之后,无法看到对方的数据。前面说过,退出信息主要是两个:进程退出码和信号编号。这两个是数据是放在进程的PCB对象中,PCB对象由操作系统管理,也就是说,要得到数据必须通过访问操作系统,访问操作系统必须通过系统调用接口来实现。

阻塞等待与非阻塞等待
调用wait和waitpid的三种情况:

  • 如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获取退出信息
  • 如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞
  • 如果不存在该子进程,则立即出错返回

当子进程还在运行时(子进程还没结束,结束后再由父进程回收),父进程在做什么?阻塞等待。与前面的进程状态中的阻塞相似,父进程在等待子进程结束,它什么也没做,只是单纯的等,此时父进程把自己的状态设置为S睡眠状态。

非阻塞等待
子进程正在运行,父进程通过系统调用(waitpid)访问子进程,子进程没有结束,返回值为0,告诉父进程子进程还在运行中,然后再访问,以此循环,多次访问。直到某一个访问子进程运行完了,父进程获取子进程的退出信息。注意,阻塞是一直正在访问,即一次访问,父进程一直在等待,没有做其他事情。但是非阻塞等待,父进程访问次数有多次,只要每次访问完子进程还没结束,父进程可以去做别的事情,不会干等。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

四、程序替换

程序替换原理(什么是程序替换)

一般来说,我们自己写的程序,执行这个程序执行的是自己的代码。但是如果我们创建的程序想执行其他程序的代码行不行?其实是可以的,通过程序替换可以执行别的程序的代码。过程:磁盘中有多个可执行程序,比如程序A,是我们自己写的;程序B,是系统自带的(例如指令ls,指令运行起来也是进程)。程序A的代码和数据对应放在物理内存中的代码段和数据段,如果程序A要执行程序B的代码和数据,核心工作是将程序B的代码和数据在物理内存中覆盖程序A的,然后相应的虚拟内存的区域大小也会调整,页表重新建立映射关系,CPU调度的时候就可以做到执行程序A,但代码是程序B的,也就执行程序B了。注意:替换过程中没有创建新的进程

在这里插入图片描述

怎么替换? 通过系统调用接口。在物理内存中覆盖数据和代码是操作系统做的,要知道操作系统的工作后数据必须要通过系统调用接口——exec*
在这里插入图片描述

先来第一个execl,它的第一个参数是程序的路径,第二个是参数是说明程序如何执行,第三个参数是可变参数。第二个参数是程序名称,后面的可变参数是选项,所以命令行怎么写,参数怎么传。注意,不管参数有几个,参数最后一个是NULL,不是"NULL"。
在这里插入图片描述
在这里插入图片描述

细节1:只要替换成功,那么exec* 后续的代码就不会执行。细节2:exec* 只有失败返回值,没有成功返回值。细节3:替换完成,不会创建新的进程。细节4:可以按非标准传参,但是尽量用标准传参。

创建一个进程,先PCB、页表、地址空间还是先把程序加载到内存?

先前者,因为就算先把程序加载到内存,PCB对象的属性和页表的映射关系都没有准备好。如果是是前者先,即使没有把程序加载到内存,当用户有请求的时候,操作系统通过缺页中断在内存中把程序加载进来,然后页表再重新建立映射关系,进程就拿到了代码和数据。

程序加载是什么?为什么?怎么做的?

程序加载是指把程序加载到内存中去。因为是冯诺依曼体系规定的。程序替换。

多进程版本
通过代码观察现象:多创建一个子进程,父进程等待,子进程执行中程序替换
在这里插入图片描述
在这里插入图片描述

父进程可以拿到执行结果,并没有像子进程那样被其他程序替换掉。子进程被创建时它的页表指向的代码和数据和父进程是一样的(默认情况下),但是要进行程序替换,此时内存中发生写时拷贝,不仅数据拷贝了,代码也拷贝了,父进程还是原来的代码和数据不影响,子进程的代码和数据被其他程序替换了。所以总结下:进程具有独立性,替换时发生写时拷贝。

关于xshell如何运行起来的

bash进程创建子进程,bash等待子进程,我们输入的指令替换子进程,bash正常运行不影响,子进程运行的是我们的输入的指令,然后得到结果(替换原来的子进程)

各种exe接口
第一个是最简单的,在上面已经展示了。接下来主要再学习4种:以带l、v、p、e的进行区分
在这里插入图片描述

  • e:与环境变量有关
  • p:不需要路径,只要给程序名字就行,系统替换的时候,会自动去环境变量种查找
  • l:列表的形式,主要是在参数列表中,有多个参数的
  • v:数组,参数直接传数组名就行了

在这里插入图片描述

一个程序调用其他不同语言的程序
修改makefile,创建一个c++文件,使用execl函数调用c++文件。无论什么语言,只要能在linux下运行,都可以去程序替换
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

环境变量的传递:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

我们并没有传环境变量,但是子进程默认就拿到了,为什么?因为默认可以通过地址空间继承的方式,让所有的子进程拿到环境变量。进程程序替换,不会替换环境变量数据。

所以子进程拿到环境变量的方式有以下3种:
一、以继承的方式,直接全部拿到(上面的代码有展示)
二、新增环境变量——putenv。bash本地看不到,但是可以被子进程继承拿到,显示的时候就能看到了。
在这里插入图片描述
在这里插入图片描述
三、设置全新的环境变量——execle(覆盖)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

也可以把环境变量表以参数的形式传递过去
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

找到你的任务管理伙伴:待办事项软件终极指南

国内外主流的10款待办事项管理软件对比:PingCode、WorktileTodoist、TickTick、Teambition、 Microsoft To Do、. Asana、Tower、番茄ToDo、飞书。 在面对日益复杂的工作和个人任务时,找到一款能够有效帮助你管理日常待办事项的软件,变得越来…

[RCTF2015]EasySQL1

打开题目 点进去看看 注册个admin账户,登陆进去 一个个点开看,没发现flag 我们也可以由此得出结论,页面存在二次注入的漏洞,并可以大胆猜测修改密码的源代码 resoponse为invalid string的关键字是被过滤的关键字,Le…

blender内置树木的UV

之前听说用这个内置插件可以创建树木 我这边的默认配置出来的树木长这样,不得不想一下,他的uv怎么展,看起来好多树 我尝试看了一眼默认的UV 结果如下 好像每个树枝都已经是平铺的样子了,那么如果需要改的话,就根据…

大模型预训练与微调之间的关系

1. 引言 大语言模型的兴起与训练范式 在过去的十年中,随着计算能力和数据规模的显著提升,大语言模型(Large Language Models, LLMs)在自然语言处理(Natural Language Processing, NLP)领域掀起了一场革命…

基于Python的火车票售票系统/基于django的火车购票系统

摘 要 随着信息技术和网络技术的飞速发展,人类已进入全新信息化时代,传统管理技术已无法高效,便捷地管理信息。为了迎合时代需求,优化管理效率,各种各样的管理系统应运而生,各行各业相继进入信息管理时代&…

stripe Element 如何使用

这里要准备好几个东西: 一个支付成功过后的回调 还有一个下单的接口 一旦进入这个下单界面,就要去调下单的接口的,用 post, 这个 接口你自己写,可以写在后端中,也可以放到 nextjs 的 api 中。 首先说的是这个下单…

算法【Java】—— 双指针算法

双指针算法 常见的双指针有对撞指针,快慢指针以及前后指针(这个前后指针是指两个指针都是从从一个方向出发,去往另一个方法,也可以认为是小学学习过的两车并行,我也会叫做同向指针),在前后指针…

vscode中使用官方推荐的编程字体Cascadia Code字体

字体样式 > 和 有特殊效果 很多字体都支持使用连字,Cascadia Code 、Jetbrains Mono 、Fira Code 等 安装Cascadia Code 下载完成后解压安装 选中右键安装,static文件里也一样安装 VS Code 中配置设置字体和连字设置 Cascadia Code, Consolas, Couri…

计算机的错误计算(六十七)

摘要 计算机的错误计算(五十六)与(六十六)分别探讨了大数与 附近数的正切函数值的错误计算。本节讨论第三种类型数值: 附近数 的正切函数的计算精度问题。 例1. 已知 计算 先用 Go语言计算: packag…

STL介绍以及string类

什么是STL 是C标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。 STL的六大组件 为什么要学习string类 C语言中的字符串 C语言中,字符串是以\0结尾的一些字符的集合,为了操作方便&am…

模具监视器的技术参数有哪些

模具监视器的技术参数涵盖了多个方面,这些参数对于确保模具监视器的性能、稳定性和检测精度至关重要。以下是一些主要的技术参数: 一、显示器参数 屏幕尺寸:常见的模具监视器显示器尺寸为12.5英寸至13.5英寸,具体尺寸可能因不同…

HTTP?HTTPS?HTTP2.0

Http HTTP(HyperText Transfer Protocol,超文本传输协议)是一种用于分布式、协作式、超媒体信息系统的应用层协议。它基于TCP/IP通信协议来传递数据,如HTML文件、图片文件等。以下是HTTP的详细解析: 一、HTTP的基本…

手撕C++入门基础

1.C介绍 C课程包括:C语法、STL、高阶数据结构 C参考文档:Reference - C Reference C 参考手册 - cppreference.com cppreference.com C兼容之前学习的C语言 2.C的第一个程序 打印hello world #define _CRT_SECURE_NO_WARNINGS 1 // test.cpp // …

Unity Dots学习 (一)

先学习怎么使用,再研究底层代码。Dots大家都有所耳闻。一直没时间研究,最近研究一下 看上图可知,哪怕是CPU的第三级缓存也比内存要快2-5倍。 资料: 《DOTS之路》第零节——前导课(1)——DOTS的5W1H问题_哔哩哔哩_bilibili 《DOT…

景联文科技:图像标注的类型有哪些?

图像标注是计算机视觉领域中一个非常重要的步骤,它是创建训练数据集的关键组成部分,主要用于帮助机器学习算法理解图像内容。 以下是图像标注的一些主要类型: 1. 边界框标注: • 这是最常见的标注方式之一,通常用于…

第四范式发布AI+5G视频营销产品 助力精准获客与高效转化

产品上新 Product Release 今天,第四范式AI5G视频电话互动营销产品全新发布。 相较于以往销效率低、互动差、转化差的传统电话外呼和短信营销方式,视频电话互动营销基于AI、5G等技术,可让用户接听电话时观看个性化视频广告并实时互动&#xf…

Unity的UI设计

目录 创建和布局 布局与交互 性能优化 最佳实践 学习资源 Unity UI Toolkit与uGUI和IMGUI之间的具体区别和适用场景是什么? Unity UI Toolkit uGUI IMGUI 如何在Unity中实现响应式UI设计以适应不同设备尺寸? Unity UI性能优化的最新技术和方法…

机器学习:逻辑回归算法实现鸢尾花预测和银行数据处理

1、鸢尾花预测 1、特征选择 2、对特征处理 trainpd.read_excel("鸢尾花训练数据.xlsx") testpd.read_excel("鸢尾花测试数据.xlsx") x_traintrain[["萼片长(cm)","萼片宽(cm)","花瓣长(cm)","花瓣宽(cm)"]] y_tr…

Vue 生命周期详解含demo、面试常问问题案例

Vue 生命周期详解、面试常问问题案例 含 demo 文章目录 Vue 生命周期详解、面试常问问题案例 含 demo一、Vue 生命周期是什么二、Vue 中如何使用生命周期钩子1. **beforeCreate**2. **created**3. **beforeMount**4. **mounted**5. **beforeUpdate**6. **updated**7. **beforeD…

Python编写Word文档

目录 0. 安装依赖 1. 创建word文档 2. 添加标题、居中、字体16大小 3. 添加标题一 4. 添加一段话并设置字体颜色 封装函数 5. 换页 6. 插入表格 0. 安装依赖 python-docx1.1.2 1. 创建word文档 from docx import Documentdoc Document() 2. 添加标题、居中、字体1…