各位看官早安午安晚安呀
如果您觉得这篇文章对您有帮助的话
欢迎您一键三连,小编尽全力做到更好
欢迎您分享给更多人哦
今天我们来学习进程——多任务计算的开始
在进程开始之前先给大家稍微讲一点计算机的基础知识吧~~~
说到计算机我们就不得不提到我们的冯大佬:冯诺依曼

一:CPU
当然要了解计算机我们首先就是要了解计算机的真正核心CPU(中央处理单元)(人类的巅峰之作)
说他厉害,那我们怎么衡量他的厉害呢?主要有两个指标可以
1.主频(右键此电脑的选中属性就可以查看处理器)
3.20Ghz:模糊的可以理解为一秒钟cpu可以处理32亿条指令
不过这里的频率是随时进行改变的叫:睿频
我们打开任务管理器
但是也不是无限变化的(根据当前的cpu负载情况动态变化),不同的cpu上限不一样(越好的cpu上限越高),甚至有的cpu可以超频,给cpu吃更多的电,cpu就会有更强的表现
2.核心数
说到核心数不得不提到:cpu里包含大量的计算单元,但是随着cpu的集成程度不断提高,想要进一步的提高集成程度就变得越来越困难。减小晶体管的体积的话,技术就要取得很大的突破。但是这技术不是说突破就能突破的了的。
那我们能不能换其他的方法提高cpu的性能呢?我们一个cpu上面多搞几个核心吧!
而且现在的多线程技术让cpu一个顶两个。
二:指令
不同的架构的cpu支持的指令不同,一个cpu在设计的时候就会提供一些指令:譬如加法指令,条件判定,跳转,写内存…………(这些指令都是用二进制来表示的)
主要有
1.x862.x86_64(64位的x86系列)3.arm(功耗低,主要用于嵌入式,和移动端)4.mips5.RISC-V架构……
三:操作系统

一台计算机上可以同时运行很多应用程序,但是他们不会相互干扰,各自完成好自己的工作(有需要通信的再说) (不能一个程序出了一个bug,其他程序也崩溃了,这肯定是不行的)
但是市面上的硬件太多了,操作系统又不能全部都认识。这个时候就需要硬件厂商提供需要的驱动程序 (包含了驱动硬件设备API提供给操作系统或者其他软件调用)

3.1:内核
一个操作系统 = 内核 + 配套的应用程序(文件资源管理器,设置…………)
内核是一个操作系统里面最核心的工作模块
上述我们说的操作系统对上,对下都是在在内核里面完成的!!!
内核提供给应用程序的一些API让应用程序可以操作系统里面的一些功能,但是问题是现在的操作系统又不只是一种,我们每一种api都要学习都要学习吗?
回答:JVM已经讲这些操作系统的api封装好了,一统江湖!!!(类似于jdbc)
刚才说过的进程的隔离性,进程是操作系统内核里面的一个重要性概念,我们以后学到的并发编程就是从这里开始的!!!
四:进程
一个已经跑起来的应用程序就是一个进程(系统资源分配的基本单位)
我们打开任务管理器就可以看到很多百十个进程(我们只是启动电脑,什么也不干,就有百八十进程,系统自动帮我们创建的)
4.1:进程的管理和组织
我们上述可以看到就算我们只是启动电脑就会有百八十个进程,何况再加上我们已经跑起来的应用程序。这么多进程,肯定不能不是一个类,不然就太乱了。我们肯定要搞一个类进行描述这些进程的属性。这个类/结构体就叫做PCB(进程控制块)
有了这么多的进程对象,我们就要想着用数据结构讲他们组织起来!
我们用的数据结构大概就是类似于双向链表这样的数据结构来组织多个PCB
譬如展示进程列表就是遍历进程的每一个节点,创建进程就是把PCB插入到链表里面,销毁进程极速把这个PCB从双向链表里面删除
4.2.PCB的一些关键属性
要了解PCB,我们可以先了解他的几个关键属性
1.pid(进程的身份标识)(右键点击PID这个属性解决就显示出来了)
2.内存指针(是一组属性)
进程在运行的时候,肯定是要吃内存的。分配给进程的内存是在哪里?这个内存空间都有哪些部分,每一部分都是用来干嘛的?(灵魂三连问哈哈哈)
这个时候就需要一组内存指针来表示了!
就譬如内存空间里面有一处空间用来存储CPU的指令,以及一些指令要依赖的数据…………
一张图带领大家理解
总之一句话:这些内存指针描述了进程所持有的内存资源是什么样的
那都有内存资源了,持有的硬盘资源是什么样的呢?(文件描述符表)
3.文件描述符表
小故事
我们说是进程的系统分配资源的基本单位,但是怎么体现出来的的呢,这个时候就得谈谈以前的故事了~~
以前的操作系统都是都是“单任务操作系统”单核cpu,一次只能执行一个进程(没有进程的调度)。实在是不行,后来就演化出了我们可以通过分时复用的方法搞成并发执行(让一个进程运行一下就下来,其他进程搞上去吧)只要我轮转的够快,你就看不出来
看起来就像是很多进程在同时运行
pcb以下的这些属性共同用来完成进程调度(和进程持有的CPU资源密切相关)
为啥要进程调度呢?
其实就是进程太多了,CPU核心太少(狼多肉少)
4.进程的状态
阻塞状态(进程某种执行条件不具备,导致这个进程不发参与CPU的调度执行)
就绪状态(两种状态,进程正在CPU正在cpu上面只能执行或者这个进程正在等待CPU的调度随时准备去执行)………………状态(我们主要的是讲述线程)
5:进程的优先级
不同的进程的优先级不同,就比如我正在打永劫无间,那么qq现在的优先级就没有永劫无间的优先级高(不可能所有的线程优先级相同,事情的重要程度不听嘛)
6.上下文(就是读档存档)
一个进程里面的程序还没有执行完毕,但是要轮转走了,我们就需要把寄存器的状态都记录到内存里面(内存指针兄弟们),等到下次轮转到这个进程的时候再沿着上次执行的位置,继续执行。
一些寄存器譬如:
1.程序计数器(保存当前执行到了哪一条指令)
(里面保存的是一个内存地址(程序下一条指令所在的位置),譬如遇到跳转的指令jmp,call……就会跳转到下一条指令所在的位置)
可执行文件加载到内存 => 程序计数器从内存里面读取指令和数据
2.维护栈相关的计数器
Java程序运行时,每个线程都有一个私有的方法调用栈。每当线程调用一个方法时,就会在该栈中创建一个新的栈帧(Stack Frame),用于存储局部变量、操作数栈、方法出口地址等信息。当方法执行完毕后,栈帧会从栈中弹出,线程继续执行前一个栈帧中的代码。
3.一些通用寄存器(保存计算的中间结果)
总之:一个cpu的寄存器没有多少,几十个字节到几百个字节(数据不多,我们就可以一股脑的把这些寄存器都打包进内存就可以了(内存指针嘛))
7.记账信息
记录当前这个进程在cpu上面执行多久了,作为操作系统调度进程的参考依据
4.3.虚拟地址空间
我们那么多进程,如果是我们手动给内存分配空间,肯定会出现内存越界的行为(那么操作系统就引出了虚拟地址空间的概念)
早起操作系统,程序运行时分配的内存就是物理内存,但是我们程序员写代码出现了bug,内存越界访问
先给B分配一个虚拟内存,再通过操作系统进行映射的方式查看是否会出现内存越界的问题,如果不存在就给B分配实际的内存,存在报异常。
通过上述方式我们就把进程给隔离开了,那么如果后续需要进程之间的相互配合怎么办呢???
这个时候就需要进程之间的通信。
1.通过文件,2.通过网络(socket)。
五.上述小结
总的来说就是,进程太多了,我们就需要一个类/结构体介绍进程的属性--PCB(进程控制块),给进程统一起来(我们讲述了这些进程的属性);再对进程进行组织管理。
进程这么多,我们核心不够。就需要进行调度,就引出来PCB关于进程调度的属性,然后这么多进程,我们代码有bug就会内存访问越界,这个时候再引出虚拟地址空间
六.线程
引入多线程是为了并发编程
我们上述讲解了进程,但是每一次创建一个进程,就需要申请一系列的资源(譬如分配内存,硬盘资源…………),如果我们频繁地创建销毁进程开销就会很大了(譬如早期的服务器)
单单就是一个分配内存就是一个大活(空闲的内存块被操作系统用数据结构组织起来了,我们申请内存操作系统就需要找到一个内存大小合适的内存返回给我们,尽管这个数据结构会很比较高效,但是这么多内存块,整体之下还是一个耗时的操作)(相对之下,占据了很大一部分时间)
为了解决这个问题,我们就引出来线程这个概念了
线程虽然比进程轻量, 但是人们还不满足, 于是又有了 "线程池"(ThreadPool) 和 "协程"(小编目前也不太了解,后续给大家讲解吧,我们先讲述线程这个概念)
6.1线程自己的PCB
线程:也叫轻量级进程,依附于进程,不能独立存在,线程是系统调度的最小单位。
线程的优点:
创建线程比创建进程更快 .销毁线程比销毁进程更快 .调度线程比调度进程更快 .
毕竟每一个线程的PCB里都有属于自己的,(线程的状态,优先级,上下文,记账信息……)但是,一个进程下:里面线程的pid是相同的,他们的内存指针和文件描述符表也是相同的(即他们的共用一份内存/硬盘资源)
总结: 进程包含线程 =》 一个进程最少有一个PCB (每一个PCB用来表示一个线程)》每一个线程都有属于自己的调度信息:状态,上下文,优先级,记账信息…每一个线程都可以去CPU上参与调度 = 》这些PCB共用一份pid,内存资源和硬盘资源 =》创建线程不需要重新申请资源 = 》创建,销毁以及调度进程的效率更高了。
给大家举一个例子
总之,我们前面铺垫这么多的东西,完全都是为了引出来我们的多线程编程!!!
上述就是进程的"黑匣子":PCB如何记录着任务的一生
的全部内容了,进程的出现是我们多任务计算的开始,我们的效率更到了很大的提升~~~
能看到这里相信您一定对小编的文章有了一定的认可。
有什么问题欢迎各位大佬指出
欢迎各位大佬评论区留言修正~~