【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`等命令的使用方法和案例。
19 4
linux进程管理万字详解!!!
|
1天前
|
缓存 负载均衡 算法
Linux内核中的进程调度算法解析####
本文深入探讨了Linux操作系统核心组件之一——进程调度器,着重分析了其采用的CFS(完全公平调度器)算法。不同于传统摘要对研究背景、方法、结果和结论的概述,本文摘要将直接揭示CFS算法的核心优势及其在现代多核处理器环境下如何实现高效、公平的资源分配,同时简要提及该算法如何优化系统响应时间和吞吐量,为读者快速构建对Linux进程调度机制的认知框架。 ####
|
2天前
|
消息中间件 存储 Linux
|
9天前
|
运维 Linux
Linux查找占用的端口,并杀死进程的简单方法
通过上述步骤和命令,您能够迅速识别并根据实际情况管理Linux系统中占用特定端口的进程。为了获得更全面的服务器管理技巧和解决方案,提供了丰富的资源和专业服务,是您提升运维技能的理想选择。
10 1
|
20天前
|
算法 Linux 调度
深入理解Linux操作系统的进程管理
【10月更文挑战第9天】本文将深入浅出地介绍Linux系统中的进程管理机制,包括进程的概念、状态、调度以及如何在Linux环境下进行进程控制。我们将通过直观的语言和生动的比喻,让读者轻松掌握这一核心概念。文章不仅适合初学者构建基础,也能帮助有经验的用户加深对进程管理的理解。
16 1
|
5月前
|
监控 Linux 应用服务中间件
探索Linux中的`ps`命令:进程监控与分析的利器
探索Linux中的`ps`命令:进程监控与分析的利器
118 13
|
4月前
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能
|
4月前
|
弹性计算 Linux 区块链
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
144 4
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
|
3月前
|
算法 Linux 调度
探索进程调度:Linux内核中的完全公平调度器
【8月更文挑战第2天】在操作系统的心脏——内核中,进程调度算法扮演着至关重要的角色。本文将深入探讨Linux内核中的完全公平调度器(Completely Fair Scheduler, CFS),一个旨在提供公平时间分配给所有进程的调度器。我们将通过代码示例,理解CFS如何管理运行队列、选择下一个运行进程以及如何对实时负载进行响应。文章将揭示CFS的设计哲学,并展示其如何在现代多任务计算环境中实现高效的资源分配。
|
4月前
|
存储 缓存 安全
【Linux】冯诺依曼体系结构与操作系统及其进程
【Linux】冯诺依曼体系结构与操作系统及其进程
167 1