【Linux操作系统】进程控制

news2024/9/20 22:25:41

目录

  • 一、进程创建
    • 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.coloradmin.cn/o/2054946.html

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

相关文章

背包dp九题

文章目录 01背包代码 完全背包代码 多重背包代码 混合背包代码 二维费用的背包问题代码 分组背包问题代码 有依赖的背包问题代码 背包问题求方案数代码 背包问题求具体方案代码 01背包 有 N N N 件物品和一个容量是 V V V 的背包。每件物品只能使用一次。 第 i i i 件物品的…

影刀工程师认证考试_考试试题_初级_影刀IT速成课程_操作题_题型D

下载待查询表格.xlsx表格至本地(手动操作)启动“待查询订单.xlsx”表格打开影刀商城——订单管理页面 影刀商城 循环查询“Sheet1”中的所有订单编号:搜索当前循环到的订单号使用【获取相似元素列表(web)】获取该订单的全部信息根据查询结果…

解密巴黎奥运会中的阿里云AI技术

引言 终于,2024年巴黎奥运会也是圆满落幕了,中国体育代表团的表现极为出色,金牌数位居世界第二(与美国并列第一),运动员们在多个项目中展现了非凡的实力和拼搏精神,为中国赢得了荣誉。从射击项…

银河麒麟V10如何安装本地deb软件包?(以安装wps为例)

银河麒麟V10如何安装本地deb软件包?(以安装wps为例) 一、准备二、安装三、总结 💖The Begin💖点点关注,收藏不迷路💖 在银河麒麟V10中安装本地.deb软件包,虽然apt主要用于管理仓库中…

【Hadoop】核心组件深度剖析:HDFS、YARN与MapReduce的奥秘

🐇明明跟你说过:个人主页 🏅个人专栏:《大数据前沿:技术与应用并进》🏅 🔖行路有良友,便是天堂🔖 目录 一、引言 1、Hadoop简介 2、Hadoop生态系统概览 二、Hadoo…

【C++ 第十三章】AVL 二叉平衡树

1. AVL树的概念 普通二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。 因此,两位俄罗斯的数学家 G.M.Adelson-Velskii 和 E.M.Landis 在1962年发明…

【故障处理】- ping不通的原因

PING不通是一个非常常见的网络问题,它可能由多种原因引起。如链路故障、ARP学习失败等 以一个Ping不通的尝试示例,介绍Ping不通故障的定位思路。如下图: PC3 Ping不通PC4 PC>ping 20.1.1.20Ping 20.1.1.20: 32 data bytes, Press Ctrl_C…

产品经理-​​实习中的自我迭代(41)

实习中的自我迭代,优秀实习生必备素质 跟大家认识了之后,就要开始做事情了,那我们怎么做一个优秀的实习生呢?以下几点作为参考。 1. 目标明确 知道自己的工作为什么要做,要做到什么程度,目前存在什么问题,该…

初探:c++异步编程之std::promise和std::future【异步数据获取】

c异步编程之std::promise和std::future 1.std::future获取std::asnyc结果2.模拟一个异步函数接口i.模拟一个客户端类包含异步请求接口ii.调用异步接口获取结果 c11以后标准库提供了thread,说起异步可能会第一时间想起thread,线程确实好东西,不…

2023年人均GDP百强市分布图

2023年人均GDP百强市分布图

redis安装,redis的数据类型和使用场景,Redis事务,Redis持久化,Redis淘汰策略

Redis简介 https://redis.io/docs/data-types/ Redis(Remote Dictionary Server )远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存也可持久化的日志型、Key-Value(NoSQL)数据库。 Redis的特点 性能极高,基于内存&…

ORACLE ADG 主库的归档日志不能主动传递到备库

主库有三个节点 ,其中两个节点传递没有问题,唯独节点二的归档日志不能主动传递到备库,都是在备库恢复需要的时候一个个传递到备库。下面是备库的日志。 Media Recovery Waiting for thread 2 sequence 1204582 …

SAP和致远OA系统集成案例

一、项目介绍 重庆某控股(集团)有限公司是一家集合汽柴油动力及终端、摩托车、储能电源、汽车零部件、金融服务等产业的多元化集团公司,业务遍布全球80多个国家及地区,2021年营业收入达80亿元。 为推动集团信息化、数字化转型…

基于WonderJourney生成电影级连续的3D场景视频

在本文中,我将详细记录在Windows环境下配置和使用WonderJourney项目的完整流程,包括环境搭建、常见问题的解决方案以及如何修改源码以兼容Windows系统。WonderJourney项目能够生成高度逼真的村庄视频,并允许用户通过配置文件对视频生成过程进行精细化控制。 由于官方文档在…

选型指南:CNAPP能力成熟度评估Checklist

随着云计算服务大量使用,网络攻击面的不断扩大,那些过去为传统数据中心而设计的安全工具和运营流程将很难应对云端的安全威胁。相比过去,安全团队现在面临超过10到100倍的容器化保护需求,大量的动态云资产需要追踪,同时…

MySQL 在 Windows 和 Ubuntu 上的安装与远程连接配置简介

MySQL 是一个广泛使用的开源关系型数据库管理系统,它提供了多用户、多线程的数据库服务。本文将介绍如何在 Windows 和 Ubuntu 操作系统上安装 MySQL,并配置远程连接。 Windows 上的 MySQL 安装 1. 下载 MySQL Installer 访问 MySQL 官方网站下载 Win…

c++--类(上)

C之类(上) 一、类的定义1.1 类定义格式1.2 访问限定符1.3 类域 二、实例化2.1 实例化的概念2.2 对象大小 三、this指针 一、类的定义 1.1 类定义格式 1、class为定义类的关键字,{}中为类的主体,注意类定义结束时后⾯分号不能省略…

Ollama - Llama3 docker版本安装部署使用

项目地址:https://github.com/meta-llama/llama3 Meta 发布两款开源Llama 3 8B与Llama 3 70B模型,供外部开发者免费使用。Llama 3的这两个版本,也将很快登陆主要的云供应商。 按照Meta的说法,Llama 3 8B和Llama 3 70B是目前同体量…

threejs中实现物体阴影

在Three.js中实现阴影需要几个步骤,包括设置渲染器、光源以及物体的材质等。以下是一个基本的实现阴影的步骤: 1、设置渲染器以支持阴影: const renderer new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerH…

三大运营管理平台:打造智能化新能源数据管理的核心利器

随着全球能源结构的转型和新能源技术的快速发展,智能化新能源数据管理成为行业发展的关键。三大运营管理平台的出现,正是为了解决这一需求,它们通过整合先进的信息技术和智能算法,为新能源企业提供了全面、高效、精准的数据管理解…