【Linux修炼】11.进程的创建、终止、等待、程序替换(一)

简介: 【Linux修炼】11.进程的创建、终止、等待、程序替换(一)

进程的创建、终止、等待、程序替换


本节重点

1. 进程的创建

1.1 fork函数初识

1.2 fork的返回值问题

1.3 写时拷贝

1.4 创建多个进程

2. 进程终止

2.1 进程退出码

2.2 进程如何退出

3. 进程等待

3.1 进程等待的原因

3.2 进程等待的方法

3.3 再谈进程退出

3.4 进程的阻塞和非阻塞等待

4. 进程的程序替换

4.1 见见猪跑

4.2 理解原理(是什么、为什么、怎么办)

4.3 一个一个调用对应的方式

4.4 应用场景:模拟shell命令行解释器



本节重点

进程的创建,终止,等待,进程的程序替换(和进程地址空间强相关)


1. 进程的创建


1.1fork函数初识


在之前的进程创建中,已经提到过fork,因此在这里的初识是在原有基础上进一步了解。


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

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

那么在调用fork函数之前只有一个进程,当进程调用fork时,当控制转移到内核中的fork代码后,内核做:


  • 分配新的内存块和内核数据结构给子进程(内核数据结构:PCB地址空间+页表,构建对应的映射关系)

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

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

  • fork返回,开始调度器调度


对于第三点的添加系统进程列表,我们在之前的进程的章节中介绍是由链表存储,而实际上当时是为了便于理解,操作系统实际上没有那么笨,其实际上是由哈希表存储的,通过struct task_struct类型的指针数组存储,当运行需要的进程时则将会通过指针找到对应的进程控制块。


微信图片_20230224213011.png


1.2fork的返回值问题



对于这个问题,从三个层次去理解。


1. 如何理解fork函数有两个返回值问题?


微信图片_20230224213059.png

对于fork函数,当调用时,fork函数内部会有两个执行流,对应父进程和子进程,当fork函数内部代码执行完毕后,子进程也就被创建好了并有可能在OS的运行队列中准备被调度了,父进程和子进程各自执行return,这样在main()函数中调用fork函数时,从fork返回的两个执行流就会分别执行main()调用fork之后的代码,因此我们之前所了看到的两个结果就是父子进程对应的执行流所造成的。


2. 如何理解fork返回之后,给父进程返回子进程pid,给子进程返回0?


父亲:孩子 = 1:n, n>=1,因此孩子找父亲具有唯一性。而由于子进程多,父进程想具体调用某一个子进程时就需要这个子进程得有一个名字才能调用这个子进程,因此给父进程返回对应子进程的pid。


3. 如何理解同一个id值,怎么会保存两个不同的值,让if else if同时执行?


对于pid_t id = fork(),我们知道返回的本质就是写入,所以谁先返回,谁就先写入对应的id,由于进程具有独立性,因此进程就会进行写时拷贝(上一篇详细描述了),因此同一个id,地址是一样的,但内容却不同


1.3写时拷贝


上一篇的进程地址空间中,我们已经提到过什么是写时拷贝,但不是单独分一个专题去写的,因此,这里总结一下写时拷贝。


通常,父子代码共享,父子再不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本。(虚拟内存就是进程地址空间)


微信图片_20230222015540.png

即当我们不修改数据时,父子进程的虚拟内存所对应的物理内存都是同一块物理地址(内存),当子进程的数据被修改,那么就会将子进程修改所对应数据的物理内存出进行写时拷贝,在物理内存中拷贝一份放在物理内存的另一块空间,将子进程虚拟内存与这个新的地址通过页表进行关联。


1.4创建多个进程


创建多个进程,可以使用如下代码:


微信图片_20230222015639.png

由于开的进程过多,会导致整个OS崩掉,只需要重启服务器就可以解决了。

2.进程终止



2.1进程退出码


我们在C/C++中,在代码最后都会写上return 0;,对于这个返回值我们称它为进程退出码。对于正确的进程一般都以0作为进程退出码,而非0就作为错误的进程的退出码,因此不同的错误对应的退出码也是不同的。


退出码的意义: 0:success, !0:表示失败。!0具体是多少,即表示不同的错误。——数字对人不友好,对计算机友好。


对于如下代码:


微信图片_20230224213252.png

这个函数的返回值是4950,因此退出码是1。当进程执行之后可以通过一个命令查看具体的进程退出码:echo $?

微信图片_20230224213339.png

但当继续执行这个命令时,发现结果是0,这是因为这个命令只会显示最近一次的进程退出码,而下一个为0的原因就是echo本身也是一个进程,并且正确执行因此显示的是0。


在这里回顾一下之前的函数:strerror(n),n为自然数,即n的不同的值就代表着不同的错误。那我们就可以执行这样的一段代码:


for(int i=0; i<200; i++)
{
    printf("%d: %s\n", i, strerror(i));
}

微信图片_20230224213416.png

执行结果发现,只有0代表着success,其他的都对应不同的错误,并且有133个不同的错误,一共有134个进程结果。


微信图片_20230224213437.png

而对于我们指定指令的随意选项造成的错误:No such or diectory就就对应着数值为2的错误。


总结一下:


  • ./mytest ———— 运行一个进程
  • echo $? ———— $?永远记录最近一个进程在命令行中执行完毕时对应的退出码(main->return?😉


进程退出的情况:


  1. 代码跑完了,结果正确 ——— return 0;
  2. 代码跑完了,结果不正确———return !0; (退出码这个时候起效果。确定对应的错误)
  3. 代码没跑完,程序异常了,退出码无意义。


那么进程如何退出呢?接下来就来解释一下(前两种情况)


2.2进程如何退出


1. main函数return返回


这也是我们经常用的方式


2. 任意地方调用 exit(code)退出


微信图片_20230224213545.png

结果显而易见,当我们查看这个进程是如何结束的,直接观察退出码:


微信图片_20230224213620.png

此外,在函数内部exit时,进程也会直接结束,函数也不会有返回值,下面就来看看这个例子:

微信图片_20230224213650.png

微信图片_20230224213654.png

到exit语句就会将进程结束,后面的代码也就不会再去执行了。


3. _exit()退出


我们看一下_exit()是如何退出的。

微信图片_20230224213757.png


微信图片_20230224213804.png

我们发现其也是和exit()一样的功能。事实上,_exit()是系统调用的函数,也就是OS,而exit()是库函数,库函数是OS之上的函数,调用exit实际上就是exit内部调用_exit,但二者之间也会有区别,我们将换行符去掉,来演示一下:exit

微信图片_20230224213845.png

结果:


微信图片_20230224213850.gif


可以看出,进程结束后,会刷新缓冲区,打印的结果暂停2秒也会显示出来。再来看看_exit:


微信图片_20230224213940.png

微信图片_20230224213943.gif

这样并没有打印出结果,也就是说_exit并没有刷新缓冲区。


因此总结一下二者:


  1. exit终止进程,主动刷新缓冲区

  2. _exit终止进程,不会刷新缓冲区



微信图片_20230224214026.png


因此用户级的缓冲区一定在系统调用之上,具体位置会在基础IO的时候说明。

微信图片_20230224214056.png


相关文章
|
20天前
|
缓存 监控 Linux
linux进程管理万字详解!!!
本文档介绍了Linux系统中进程管理、系统负载监控、内存监控和磁盘监控的基本概念和常用命令。主要内容包括: 1. **进程管理**: - **进程介绍**:程序与进程的关系、进程的生命周期、查看进程号和父进程号的方法。 - **进程监控命令**:`ps`、`pstree`、`pidof`、`top`、`htop`、`lsof`等命令的使用方法和案例。 - **进程管理命令**:控制信号、`kill`、`pkill`、`killall`、前台和后台运行、`screen`、`nohup`等命令的使用方法和案例。
53 4
linux进程管理万字详解!!!
|
10天前
|
存储 运维 监控
深入Linux基础:文件系统与进程管理详解
深入Linux基础:文件系统与进程管理详解
50 8
|
8天前
|
Linux
如何在 Linux 系统中查看进程占用的内存?
如何在 Linux 系统中查看进程占用的内存?
|
19天前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
53 4
|
20天前
|
缓存 负载均衡 算法
Linux内核中的进程调度算法解析####
本文深入探讨了Linux操作系统核心组件之一——进程调度器,着重分析了其采用的CFS(完全公平调度器)算法。不同于传统摘要对研究背景、方法、结果和结论的概述,本文摘要将直接揭示CFS算法的核心优势及其在现代多核处理器环境下如何实现高效、公平的资源分配,同时简要提及该算法如何优化系统响应时间和吞吐量,为读者快速构建对Linux进程调度机制的认知框架。 ####
|
9天前
|
监控 Linux
如何检查 Linux 内存使用量是否耗尽?这 5 个命令堪称绝了!
本文介绍了在Linux系统中检查内存使用情况的5个常用命令:`free`、`top`、`vmstat`、`pidstat` 和 `/proc/meminfo` 文件,帮助用户准确监控内存状态,确保系统稳定运行。
79 6
|
10天前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
44 3
|
10天前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
32 2
|
18天前
|
缓存 监控 Linux
|
5天前
|
运维 监控 网络协议
运维工程师日常工作中最常用的20个Linux命令,涵盖文件操作、目录管理、权限设置、系统监控等方面
本文介绍了运维工程师日常工作中最常用的20个Linux命令,涵盖文件操作、目录管理、权限设置、系统监控等方面,旨在帮助读者提高工作效率。从基本的文件查看与编辑,到高级的网络配置与安全管理,这些命令是运维工作中的必备工具。
24 3
下一篇
无影云桌面