【Linux 学习计划】-- 进程概念与本质 | pid & ppid | 进程创建与多进程(fork)

目录

进程是什么?

[代码和数据 与 PCB 在系统上的联动](#代码和数据 与 PCB 在系统上的联动)

[pid & ppid](#pid & ppid)

创建子进程

结语


进程是什么?

我们可以打开Windows中的任务管理器:

我们可以在上面看到,所有正在跑的程序都会在上面显示,这其实就都是一个个的进程

那么进程具体是什么?

首先,我们的代码如果要跑起来的话,就需要先加载到内存中,这是冯诺依曼体系结构规定的

那么当我们将数据和代码 加载到内存之后,这些数据和代码是否就是进程呢?

试想一下,你如何说你是一个学校的学生?因为你人在学校你就是了吗?那保安大叔也在学校里啊?你放假回家了就不是那个学校的学生了吗?显然不是,你是那个学校的学生,是因为你的信息在学校的教务系统里面被管理着

对于操作系统而言,我们光有代码和数据还不够,还需要将其管理起来,也就是:先描述,再组织

我们需要先用struct把进程给描述起来,接着就是将这一个个的struct用特定的数据结构管理起来

接着,我们对进程的管理,就转换成了对特定数据结构的管理

而我们用来描述进程的struct,就是PCB,也就是process control block(进程控制块)

但是PCB,是一个总称,而在Linux中则是task_struct,这就好比"奔驰"和"汽车",奔驰只是汽车中的一种,仅此而已

所以我们就可以得出结论了,也就是:

进程 = PCB + 代码和数据

看这张图,代码和数据加载到内存中,然后task_struct中有内存指针可以指向这部分对应的代码和数据,接着CPU就对task_struct进行管理

以上就是进程的概念了,讲完了概念,我们来演示一遍代码和数据在系统中具体是怎么和PCB联动起来的

代码和数据 与 PCB 在系统上的联动

如上图,我们的代码数据是加载到了内存中的用户空间,而我们的PCB则是在内存中的内核空间中存着

这时有新的代码和数据进来内存了,我们就在内核空间中new一块空间出来,然后PCB中的内存指针指向对应的代码和数据,这样就能将这部分内容管理起来了

接着,我们系统中不断有进程需要创建,所以task_struct就不断变多

这些task_struct就全部链接起来(数据结构),这时候就将进程管理起来了

而后面如果涉及到进程调度,等待之类的,就都让CPU和内核处理了

pid & ppid

我们在Linux敲指令时,当我们想跑程序了,都会用 ./XXX 跑代码,而这个指令的本质就是运行进程

包括我们敲的ls、pwd、cd等等指令,都是启动了进程,只不过这些进程的开始到结束都比较快

而一个进程的属性中,必然会有自己的id,也就是pid,而每一个pid都是唯一的

如果我们想知道pid的话,我们有两个办法

第一个就是用指令:

  1. ps ajx | head -1 && ps ajx | grep name

  2. ls /proc/

ps指令查我们具体某一个进程比较好,因为grep就是筛选用的

但如果想用代码查呢?

首先我们需要知道的是,只要你想知道pid,那么就一定只能在PCB中找

但是,PCB是被内核数据结构管理的,而我们用户是无法直接进内核查东西的,我们只能使用系统接口

而这时我们就有了一个系统接口:getpid()

这个pid是哪个进程的pid呢?我们程序跑起来的时候就已经是一个进程了,所以就是那个跑起来的进程,而我们的进程最后结束了,所以就没法在外面用指令查到

同时,我们除了pid之外,还有一个ppid,第一个p就是parent的意思,也就是父进程

这也就意味着,我们每一个进程都是由另一个进程创建出来的,但是每一个进程之间互相不影响

那我们这个程序自始至终都没有看到父进程的影子,那我们的父进程会是谁呢?

我们先将父进程的id打印出来,接着我们再用ps命令进行查找,最后用grep命令最筛选:

我们可以看到,启动我们程序的父进程就是bash,也就是我们的命令行解释器

创建子进程

同样的,创建子进程我们也是需要调用系统接口------fork

先来简单用一用,来看这样一段代码:

我们会看到,222居然被打印了两次,这是因为我们我们在fork之后,就已经有两个进程了,而两个进程之间是互相独立的

也就是说,fork之后,分了两个进程,一个打印了第一个222,一个打印了第二个222

那我们怎么看到是否真的有两个进程呢?还是原来那个代码,只不过我们将其改成不断循环打印,如下:

确实我们可以看到,在test运行的时候有两个进程,上面的是父进程,因为下面那个进程的ppid是他的pid

接下来我们就需要来好好讲讲fork这个函数了:

首先,我们的fork是有两个返回值的,创建完进程之后,如果创建成功,就将子进程的pid作为返回值返回给父进程,然后将 0 返回给子进程

如果没有成功创建子进程,那么fork就会将 -1 返回给父进程

听起来似乎很诡异,因为我们之前从来没有说一个函数返回两个返回值的情况,但是先不管那么多,我们先来写一个代码验证一下

顺便一提,我们为什么要有子进程呢?这是因为我们需要子进程帮我们处理不同的任务,这点我们目前暂时没处理过什么任务暂时无法理解,记着就行

现在来解释fork,为什么能返回两个参数呢?这是因为我们在fork返回也就是return的时候,我们前面的业务就已经做完了,也就是,进程已经创建出来了

那么我们的进程只需要用一下条件判断,就能知道哪个进程要返回哪个了

结语

这篇文章到这里就结束啦!!~( ̄▽ ̄)~*

如果觉得对你有帮助的,可以多多关注一下喔

相关推荐
晨曦5432106 分钟前
Flask入门指南:从零构建Python微服务
python·学习·flask
爱上妖精的尾巴1 小时前
3-18 WPS JS宏 颜色设置实例应用(按条件设置单元格颜色)学习笔记
javascript·笔记·学习·excel·wps·js宏·jsa
云墨-款哥的博客2 小时前
失业学习-前端工程化-webpack基础
前端·学习·webpack
Ivy烎4 小时前
STM32学习笔记
笔记·stm32·学习
八一考研数学竞赛6 小时前
第十七届全国大学生数学竞赛初赛模拟试题
学习·数学·latex·全国大学生数学竞赛
蓝胖子不会敲代码6 小时前
跟着AI学习C# Day12
学习·microsoft·c#
future14127 小时前
FairyGUI学习
学习·游戏·ui·unity
Chef_Chen8 小时前
从0开始学习R语言--Day27--空间自相关
学习
虾球xz8 小时前
CppCon 2017 学习:10 Core Guidelines You Need to Start Using Now
开发语言·c++·学习
AgilityBaby9 小时前
UE5创建蒙太奇动画并播放和在动画蒙太奇中创建动画通知状态
笔记·学习·ue5·游戏引擎·蓝图·蒙太奇动画