【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


相关文章
|
5月前
|
Web App开发 Linux 程序员
获取和理解Linux进程以及其PID的基础知识。
总的来说,理解Linux进程及其PID需要我们明白,进程就如同汽车,负责执行任务,而PID则是独特的车牌号,为我们提供了管理的便利。知道这个,我们就可以更好地理解和操作Linux系统,甚至通过对进程的有效管理,让系统运行得更加顺畅。
135 16
|
4月前
|
监控 Shell Linux
Linux进程控制(详细讲解)
进程等待是系统通过调用特定的接口(如waitwaitpid)来实现的。来进行对子进程状态检测与回收的功能。
84 0
|
4月前
|
存储 负载均衡 算法
Linux2.6内核进程调度队列
本篇文章是Linux进程系列中的最后一篇文章,本来是想放在上一篇文章的结尾的,但是想了想还是单独写一篇文章吧,虽然说这部分内容是比较难的,所有一般来说是简单的提及带过的,但是为了让大家对进程有更深的理解与认识,还是看了一些别人的文章,然后学习了学习,然后对此做了总结,尽可能详细的介绍明白。最后推荐一篇文章Linux的进程优先级 NI 和 PR - 简书。
115 0
|
4月前
|
存储 Linux Shell
Linux进程概念-详细版(二)
在Linux进程概念-详细版(一)中我们解释了什么是进程,以及进程的各种状态,已经对进程有了一定的认识,那么这篇文章将会继续补全上篇文章剩余没有说到的,进程优先级,环境变量,程序地址空间,进程地址空间,以及调度队列。
81 0
|
4月前
|
Linux 调度 C语言
Linux进程概念-详细版(一)
子进程与父进程代码共享,其子进程直接用父进程的代码,其自己本身无代码,所以子进程无法改动代码,平时所说的修改是修改的数据。为什么要创建子进程:为了让其父子进程执行不同的代码块。子进程的数据相对于父进程是会进行写时拷贝(COW)。
81 0
|
26天前
|
Unix Linux 程序员
Linux文本搜索工具grep命令使用指南
以上就是对Linux环境下强大工具 `grep` 的基础到进阶功能介绍。它不仅能够执行简单文字查询任务还能够处理复杂文字处理任务,并且支持强大而灵活地正则表达规范来增加查询精度与效率。无论您是程序员、数据分析师还是系统管理员,在日常工作中熟练运用该命令都将极大提升您处理和分析数据效率。
101 16
|
17天前
|
Linux
linux命令—stat
`stat` 是 Linux 系统中用于查看文件或文件系统详细状态信息的命令。相比 `ls -l`,它提供更全面的信息,包括文件大小、权限、所有者、时间戳(最后访问、修改、状态变更时间)、inode 号、设备信息等。其常用选项包括 `-f` 查看文件系统状态、`-t` 以简洁格式输出、`-L` 跟踪符号链接,以及 `-c` 或 `--format` 自定义输出格式。通过这些选项,用户可以灵活获取所需信息,适用于系统调试、权限检查、磁盘管理等场景。
|
3月前
|
监控 Linux 网络安全
Linux命令大全:从入门到精通
日常使用的linux命令整理
663 13
|
4月前
|
Linux 网络安全 数据安全/隐私保护
使用Linux系统的mount命令挂载远程服务器的文件夹。
如此一来,你就完成了一次从你的Linux发车站到远程服务器文件夹的有趣旅行。在这个技术之旅中,你既探索了新地方,也学到了如何桥接不同系统之间的距离。
551 21
|
4月前
|
JSON 自然语言处理 Linux
linux命令—tree
tree是一款强大的Linux命令行工具,用于以树状结构递归展示目录和文件,直观呈现层级关系。支持多种功能,如过滤、排序、权限显示及格式化输出等。安装方法因系统而异常用场景包括:基础用法(显示当前或指定目录结构)、核心参数应用(如层级控制-L、隐藏文件显示-a、完整路径输出-f)以及进阶操作(如磁盘空间分析--du、结合grep过滤内容、生成JSON格式列表-J等)。此外,还可生成网站目录结构图并导出为HTML文件。注意事项:使用Tab键补全路径避免错误;超大目录建议限制遍历层数;脚本中推荐禁用统计信息以优化性能。更多详情可查阅手册mantree。
linux命令—tree