Linux进程控制(一)---进程创建和终止(写时拷贝,exit与_exit等)

news2024/11/24 3:07:03

目录

进程创建

fork()函数

子进程如何继承父进程的数据

1.创建时拷贝分离

2.写时拷贝★

进程终止

进程终止时,操作系统做了什么?

进程终止的常见方式

代码运行完毕,结果正确

退出码★

代码运行完毕,结果不正确

代码异常终止

用代码如何终止一个进程

return

exit和_exit★


进程创建

fork()函数

这个函数我在上一个文章已经把用法讲的很详细了,所以本文章主要是从一个更加深入的角度来看待fork().

在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。

#include <unistd.h>
pid_t fork(void);
返回值:子进程中返回0,父进程返回子进程id,出错返回-1

现在我们将从一个更加系统的角度来讲解:

请描述一下,fork()创建子进程,操作系统都做了什么?

1.首先这个问题的切入点是:fork()创建子进程,系统里就会多了一个进程!

2.然后回答什么是进程:

进程 = 内核数据结构(OS) + 进程代码和数据(一般从磁盘来,也就是C/C++程序加载后的结果)

3.之后,对子进程会有以下操作:

        a.分配新的内存块和数据结构给子进程

        b.将父进程部分数据结构内容拷贝至子进程

        c.添加子进程到系统进程列表当中

        d.fork()返回,开始调度器调用.

子进程如何继承父进程的数据

创建子进程,给子进程分配对应的内核结构,必须是子进程独有,因为进程具有独立性。理论上,子进程也要有自己的代码和数据!

可以是一般而言,子进程没有加载的过程,也就是说子进程没有自己的代码和数据,所以子进程只能使用“父进程代码和数据”

这不对啊,不是说进程具有独立性吗,怎么可以使用父进程的代码和数据呢

这里也要分情况讨论:

代码:都是不可被改写的,只能读取,所以父子共享,没有问题!

数据:可能被修改,所以必须分离!

那么对于数据而言,该如何分离呢?

1.创建时拷贝分离

先来看第一种方法:创建子进程的时候,就直接拷贝分离,那这样会有什么问题呢?

1.当我们创建子进程后,我们能立马运行它吗?这是其一.

2.即便可以立马运行,你会访问所有的数据吗?这是其二.

3.即使你会访问所有的数据,那么你对所有数据的访问都是写入吗?不是写入就根本没必要拷贝. 

所以这个问题是可能拷贝子进程根本就不会用到的数据空间,即便用到了,也可能只是读取!

这种情况也很好的说明了:

 两个不同的字符串常量,当我们打印地址的时候,会发现它们的地址是一样的!

因为编译器知道这个const修饰变量里面的内容不可以被修改了,所以后面和它内容相同的变量都会直接指向它.

 这个只是想告诉大家:编译器编译程序的时候,尚且知道节省空间。更何况这种直接使用内存的系统接口,会更加注重.

所以创建子进程,不需要将不会被访问或者只会读取的数据拷贝一份.

但是问题来了,什么样的数据值得拷贝,什么样的数据必须被拷贝?

一定是将来会可以被父进程或子进程写入的数据.

但是,一般而言即便是OS,也无法提前知道哪些空间可能会被写入.

但是即便知道,提前拷贝了,你会立马使用吗?

答案是那么多可被写入的数据,肯定不会,但是空间已经给你了,却不用,所以这就造成了空间的浪费.

所以OS选择了一种技术:写时拷贝.来进行父子进程的数据进行分离.

2.写时拷贝★

所以结合以上所说的,写时拷贝就是当你需要可被写入的数据时,OS再给你对应的空间.

为什么OS采用写时拷贝技术,进行父子进程数据的分离:

1.用的时候,再进行分配,是高效使用内存的一种表现.

2.OS无法在代码执行前预知哪些空间会被访问.

 这里还有一个问题:

那么父进程fork()之前的代码,子进程共享吗?

答案是共享的,子进程共享父进程fork()之前所有的代码.

下面将一张图来解释.这张图我在进程概念与状态中的并发执行中也很详细的说过了.

 即EIP里有下一行代码的地址,这些存储的数据叫做上下文数据.

当子进程继承父进程时,发生写时拷贝,将父进程的上下文数据拷贝给了子进程.

后面父子进程虽然各自调度,代码不同,各自会修改EIP,但是已经不重要了,因为子进程已经认为自己的EIP代码起始值,就是fork()之后的代码.

所以子进程虽然是从fork()之后运行,但是不代表fork()之前的代码子进程看不到!

进程终止

进程终止时,操作系统做了什么?

我们知道一个程序运行起来变成一个进程,一个进程又有进程代码和数据 + 内核数据结构组成.

所以进程终止是要释放进程申请的内核数据结构和对应的数据和代码.本质是释放系统资源.

进程终止的常见方式

代码运行完毕,结果正确

这个情况很常见,当我们写算法题的时候,比如力扣,一个题如果结果正确,最后提交后会显示通过.

或者自己在编译器下写的,最后输出也符合我们的预期结果等等.

那不知道我们有没有注意过,main函数会有返回值,return 0,它返回值的意义是什么,为什么总会返回0?

其实main函数返回值并不总是0,只是我们平常写的时候经常写0而已.

main函数最后return的值叫做进程的退出码.

一般0代表success,表示进程运行的结果正确.

非0标识的运行结果不正确,后面会细说.

例如我们返回一个10.

 然后我们利用$?来输出最近一个进程的退出码.

 可以发现第一次运行,得到的退出码是最近返回的10.

第二次之所以是0,是因为上一次的echo $?也是一个进程,它成功执行了,所以返回0.

那么main函数返回的意义是什么呢?

用来返回给上一级进程,用来评判该进程的执行结果,可以忽略. 

 例如我们写一个加法求和的程序,如果答案正确就返回0,答案错误就返回1.

此时我们的sum是正确的流程,所以结果应该返回0

而当我们将sum的计算逻辑写错得不到正确结果时,便会返回1,标识程序最后的结果不正确,这也是main函数返回值的意义. 

退出码★

回到刚才所说的非0退出码,非零值有无数个,可以用来标识不同的错误原因.

给我们的程序运行结束之后,方便定位错误的原因细节.

这些原因linux也定义了,我们可以打印来看一下,这里需要用到一个函数strerror()

它的作用是返回错误码的字符串描述.

我们输入代码:

 

 然后输出结果:

 

直至100,我们发现有很多错误类型.

当然,我们可以自己使用这些退出码和含义,但是你如果想自己定义,也可以自己设计出一套退出方案. 

代码运行完毕,结果不正确

同上。

代码异常终止

程序异常终止时,退出码便在没有意义,一般而言退出码对应的return语句没有被执行!

那么程序为什么会崩溃呢?

用代码如何终止一个进程

return

首先我们知道,return 退出码 就是终止进程的,当然只有main函数内的return语句才是终止进程的.

exit和_exit★

先来man看一下介绍

 看到它的作用是让进程正常终止。

然后函数参数是status,这个就是用来标识退出码的.

它与main函数不同的是:exit函数在任何地方调用,都是直接终止进程!

看下面例子:

 

 如果是return 200的话,它只会将200返回给a,程序并不会结束.

 可以看到func()后面输出world语句没有被执行,而且退出码是第一次的200.

那么_exit是什么呢,又有什么区别呢?

 描述了一大堆,也挺抽象的,我下面用一个例子和图片来解释它和exit的区别.

首先还是刚才的例子,我们把exit改为_exit.

可以发现结果和exit并没有区别:

 

但是我们把程序换成如下这样:

 

这个时候我们预期的是由于printf没有'\n'换行符进行缓冲区刷新,此时内容存储在缓冲区,所以程序运行1s后才会将内容输出到屏幕上,然后退出码返回111. 

由于是静态图无法演示效果,但是是1秒后才出的结果.

但如果此时我们把exit改成_exit.

 

 却发现什么都没有输出出来.

这里就直接说结论了,exit在程序结束时会刷新缓冲区的内容到屏幕上,而_exit是直接结束,而不会刷新缓冲区的内容.

就如如下这张图所表示的

但我们平常还是推荐用exit。

我们知道库函数是封装的系统接口,而这里面的库函数其实是exit,而系统接口是_exit .

而我们平常所说的缓冲区在哪里呢,这个后面再讲,但是一定不在操作系统内部! 

如果在操作系统内部,是由操作系统维护的,那么_exit也照样可以刷新出来,而它刷新不出来,说明一定不在操作系统内部,而是C语言库给我们提供的.

关于进程的创建与终止也就到此为止了.

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

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

相关文章

Redis 读写分离 使用redisTemplate执行lua脚本时,报错处理

项目框架说明 项目配置 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.4</version></parent>....<dependency><groupId>org.springfra…

Mac苹果装机工作必备软件推荐,十大效率神器让你的 Mac 雄起

Mac笔记本固然好用&#xff0c;但还是会有一些使用中的痛点&#xff0c;下面给大家推荐10款最常用的提高mac使用效率的软件 一、Magnet Pro - 窗口大小布局位置控制 这款软件可以让你的Mac像Windows一样&#xff0c;通过拖动窗口实现窗口最大化、左右半屏、上下半屏、1/4窗口…

Ubuntu18.04下的labelImg使用教程

Ubuntu18.04下的labelImg使用教程 1、安装 # 下载labelImg git clone https://github.com/Ruolingdeng/labelImg.git# 配置环境 sudo apt-get install pyqt5-dev-tools sudo pip3 install -r requirements/requirements-linux-python3.txt make qt5py3# 运行 python3 labelImg.…

mysql 1 -- 数据库介绍、mysql 安装及设置

Linux 安装 mysql 1、数据库&#xff08;mysql&#xff09; 数据文件 - 数据库过了系统 2、c/s mysql 服务器 mysql 客户端 ip port : 3306 3、关系型 于 非关系型数据库&#xff08;nosql&#xff09; nosql可以解决一些关系型数据库所无法实现的场景引用。 一、数据库介绍 …

什么是 Elasticsearch 索引?

作者&#xff1a;David Brimley 索引这个术语在科技界已经被用满了。 如果你问大多数开发人员什么是索引&#xff0c;他们可能会告诉你索引通常指的是关系数据库 (RDBMS) 中与表关联的数据结构&#xff0c;它提高了数据检索操作的速度。 但什么是 Elasticsearch 索引&#xff…

Spark(29):Spark内存管理

目录 0. 相关文章链接 1. 堆内和堆外内存规划 1.1. 堆内内存 1.2. 堆外内存 2. 内存空间分配 2.1. 静态内存管理 2.2. 统一内存管理 3. 存储内存管理 3.1. RDD 的持久化机制 3.2. RDD的缓存过程 3.3. 淘汰与落盘 4. 执行内存管理 4.1. Shuffle Write 4.2. Shuffl…

【Fiddler】Fiddler实现mock测试(模拟接口数据)

软件接口测试过程中&#xff0c;经常会遇后端接口还没有开发完成&#xff0c;领导就让先介入测试&#xff0c;然后缩短项目时间&#xff0c;有的人肯定会懵&#xff0c;接口还没开发好&#xff0c;怎么介入测试&#xff0c;其实这就涉及到了我们要说的mock了。 一、mock原理 m…

自学成才的黑客有很多,掌握方法很重要,给你黑客入门与进阶建议

建议一&#xff1a;黑客七个等级 黑客&#xff0c;对很多人来说充满诱惑力。很多人可以发现这门领域如同任何一门领域&#xff0c;越深入越敬畏&#xff0c;知识如海洋&#xff0c;黑客也存在一些等级&#xff0c;参考知道创宇 CEO ic&#xff08;世界顶级黑客团队 0x557 成员&…

如何解决VScode远程下载插件不了的问题?如何手动安装插件?

当我们在使用VScode进行远程操作时&#xff0c;在安装我们所需要的一些插件时&#xff0c;可能会出现如下图&#xff0c;一直卡在安装中....明明只有小几十MB&#xff0c;却一连好几个小时都一动不动。像这种情况&#xff0c;就需要我们进行手动安装该插件。 插件网站&#xff…

照度与感光度(Lux)

何谓照度&#xff1f;照度&#xff08;LUX&#xff09;数值达到多少为低照度&#xff1f;多少数值能适应摄取影像的周围环境&#xff1f; 照度是反映光照强度的一种单位&#xff0c;其物理意义是照射到单位面积上的光通量&#xff0c;照度的单位是每平方米的流明&#xff08;L…

智能精密配电柜在机房低压配电中的运用与发展趋势

安科瑞 华楠 摘 要&#xff1a;随着科学技术的深入发展&#xff0c;各项领域逐渐突破技术限制&#xff0c;充分发挥智能化优势&#xff0c;体现智能与精密的高端价值。以医院机房配电为研究背景&#xff0c;分析配电柜的发展趋势。目前&#xff0c;机房配电中&#xff0c;利用…

sort部分

sort主要针对文件内容的操作&#xff0c;对文件内容进行匹配或者过滤&#xff0c;排序 grep 过滤 针对文本内容进行过滤&#xff0c;也就是查找 -i&#xff1a;忽略大小写默认的&#xff0c;可以不加 -n&#xff1a;显示匹配的行号 -c&#xff1a;只统计匹配的行数 &#…

22款奔驰E350升级ACC自适应巡航系统,解放您的双脚

有的时候你是否厌倦了不停的刹车、加油&#xff1f;是不是讨厌急刹车&#xff0c;为掌握不好车距而烦恼&#xff1f;如果是这样&#xff0c;那么就升级奔驰原厂ACC自适应式巡航控制系统&#xff0c;带排队自动辅助和行车距离警报功能&#xff0c;感受现代科技带给你的舒适安全和…

web中引入live2d的moc3模型-(调整样式)

文章目录 src文件夹修改底部背景色修改背景图片修改canvas大小和定位 src文件夹 基本所有的样式都在src文件夹下的ts文件中&#xff0c;而我们每次修改样式时&#xff0c;记得重新build&#xff0c;已让页面重新加载修改打包后的js文件 npm run build修改底部背景色 默认是黑…

最强,自动化测试框架总结整理,测试进阶之路卷起来...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 自动化测试框架是…

Pytorch:搭建卷积神经网络完成MNIST分类任务:

2023.7.18 MNIST百科&#xff1a; MNIST数据集简介与使用_bwqiang的博客-CSDN博客 数据集官网&#xff1a;MNIST handwritten digit database, Yann LeCun, Corinna Cortes and Chris Burges MNIST数据集获取并转换成图片格式&#xff1a; 数据集将按以图片和文件夹名为标签的…

二、DDL-4.表操作-修改删除

一、修改 1、往表中添加字段 e.g.为emp表增加一个新的字段“昵称”为nickname&#xff0c;类型为varchar(20) alter table emp add nickname varchar(20) comment 昵称; 2、修改表中字段 e.g.将emp表的nickname字段修改为username&#xff0c;类型为varchar(30) alter table e…

TCP/IP网络编程 第十六章:关于IO流分离的其他内容

分离I/O流 两次I/O流分离 我们之前通过2种方法分离过IO流&#xff0c;第一种是第十章的“TCPI/O过程&#xff08;Routine&#xff09;分离”。这种方法通过调用fork函数复制出1个文件描述符&#xff0c;以区分输入和输出中使用的文件描述符。虽然文件描述符本身不会根据输入和输…

基于主从博弈的主动配电网阻塞管理的论文复现——附Matlab代码

目录 文章摘要&#xff1a; 编程思路&#xff1a; 研究背景&#xff1a; 基于主从博弈的电网阻塞管理&#xff1a; 算例介绍&#xff1a; Matlab运行结果展示&#xff1a; Matlab代码数据分享&#xff1a; 文章摘要&#xff1a; 随着需求侧灵活性资源在配电网中的渗透率…

SQLite编程操作

一、打开/创建数据库的C接口 ①sqlite3_open ( const char * filename , sqlite3 ** ppDb ) 打开一个指向 SQLite 数据库文件的连接&#xff0c;返回一个用于其他 SQLite 程序的数据库连接对 象。 ②sqlite3_close(sqlite3*) 关闭之前调用 sqlite3_open() 打开的数据…