我们电脑中的.exe
文件,如果我们不去双击运行它的话,他只会静静的躺在你的硬盘空间里,也就是说在我们双击它之前,它不会对我们的系统产生任何影响。
总感觉文章不连贯,于是便添加了这一节,首先进程其实内部还是有很多东西的,并不是我们表面看到的皮毛,其次,如果想弄清楚进程调度的过程,必须要清楚进程在操作系统中扮演的角色。由于是理解性的知识,文字有点多,耐心看完必有收获。
首先我要给大家介绍一下“PCB
”(进程控制块)~注意并不是那个参见的PCB
板(这个是硬件中的),在操作系统里面主要是通过c/c++
来实现相关属性,此处描述其实就是用的c语言中的“结构体”(也就和Java
中的类差不多)。操作系统中描述进程的这个结构体,称为“PCB
”,
就是使用双向链表来把每个进程的PCB
给串起来~~,注意这里指的是在Linux
这个系统为例,每个操作系统的类型不同,内部的实现也是各有不同的,具体windows
和ios
是如何我们也不得而知,毕竟不像linux
是开源的。
再给大家简单分析一下所谓的一些进程操作
所谓的“创建进程”,就是先创建出PCB
,然后把PCB加到双向链表中。
所谓的“销毁进程”,就是找到链表上的PCB
,并且从链表上删除。
所谓的“查看任务管理器”,就是遍历链表~~
这个很好理解,就是进程的身份标识(进程的身份证号)
指明了这个进程要执行的代码/指令在内存的哪里,以及这个进程执行中依赖的数据都在哪里~~当运行一个exe
,此时操作系统就会把这个exe
加载到内存中,变成进程。
简而言之内存指针就像航海指南,能够给进程指一条明路~
程序运行过程中,经常要和文件打交道,进程每次打开一个文件,就会在文件描述符表上多增加一项(这个文件描述符表可以视为一个数组,里面的每个元素又是一个结构体,就对应一个文件的相关信息)一个进程只要一启动,不管你代码中是否打开/操作文件的代码都会默认的打开三个文件~~(系统自动打开的):标准输入(System.in
)标准输出(System.out
)标准错误(System.err
)
这个文件描述符表的下标,就称为文件描述符~
进程的调度,其实就是,操作系统在考虑CPU资源如何给各个进程分配~
上面的属性是一些基础的属性,下面的一组属性,主要是为了能够实现进程的调度~
这个状态就描述了当前这个进程接下来应该怎么调度~~
就绪状态:随时可以去CPU
上执行。
阻塞状态/睡眠状态:暂时不可以去CPU
上执行。
Linux中的进程状态还有很多其他的。
先给谁分配时间,后给谁分配时间,以及谁分的多,给谁分的少。
统计了每个进程,都分别被执行了多久,分别都执行了哪些指令。分别都排队等了多久,目的是给进程调度提供指导依据的。
表示上次进程被调度出CPU
的时候,当时程序的执行状态~~,下次进程上CPU
的时候,就可以恢复之前的状态,然后继续往下执行。
进程被调度出CPU
之前,要把CPU中的所有的寄存器中的数据都给保存到内存中(PCB
的上下文字段中)
相当于玩单机游戏的时候的存档~~
下次进程再被调度上CPU
的时候,就可以从刚才的内存中恢复这些数据到寄存器中
相当于读档继续玩游戏~
存档+读档~存档存储的游戏信息,就称为“上下文”
来个小故事加深加深印象,顺带讲讲到底什么是进程调度~
假设你是一个妹子,长的巨好看,身材一流,还会讲话~~
导致你有很多的追求者~,但是原则上讲,你在同一时刻只能谈一个男朋友。
但是呢现实中,有钱,长得还帅,还巨能舔,同时满足这三点,现实中基本不存在哈哈哈~(电视剧常驻男演员)
于是你为了满足自己的这些要求,同时谈了三个男朋友~
A:有钱的~
B:长得帅的~
C:比较会舔的~
因此,一个时间管理大师就横空出世了,你需要合理安排自己的时间,避免同一时刻,这三人碰面~
这时候你首先要做的事情就是给自己排一个时间表~
宏观上看起来,你是同时谈了三个男朋友。
微观上看起来,同一时刻,你只是和一个男朋友在一起
插入:显然这就是并发,规划时间表的过程,也就是“调度”的过程~
正常情况下,A B C三个人你都能随叫随到~~他们都是就绪状态
假设A要出差一个月~就可以认为A现在是阻塞状态/睡眠状态。
因此处于这样状态的进程就暂时不进行调度(暂时不把这个A给排到时间表上)
在给A B C排时间表的时候,比如:就会优先给B排,其次给A排,最后给C排
周一到周三,都和B甜蜜蜜的在一起。
周四周五,都和A甜蜜蜜的在一起。
周六,和C在一起。
周天给自己休息一天~
如果长此以往,和C在一起的时间太少了,C对你的好感度就会降低~舔的也不够卖力了
这时候你翻开你的时间表发现给C排的太少了
接下来就适当给点甜头~
作为手握三位男朋友的你,把每周的时间都排的很紧,并且要随时转换角色,渐渐的有一天你就不小心串台了~比如:答应A的事情却给B做了,C过生日你却给A送了生日礼物。
为了区分,你养成记录日记的好习惯,把每次约会的重要信息都记录下来~下次和他们见面就能够对上号,哪怕时间长你也能查到。
相比大家对于进程或多或少有点儿了解了~接下来给大家讲讲线程
线程是啥?为啥要有线程?
因为我们的系统支持多任务了~(远古版本还是单任务)程序猿也就需要“并发编程”
通过多线程,是完全可以实现并发编程的~
但是有个小问题:
如果需要频繁的创建/销毁进程,这个事情的成本还是比较高的。
如果需要频繁的调度进程,这个事情成本也是比较高的。
那么如何解决这个问题呢?
主要有两个思路:
1.进程池~(数据库连接池,字符串常量池)
进程池虽然能解决上面的问题,提高效率,同时也存在问题,池子里的闲置进程,不使用的时候也在消耗系统资源,消耗的系统资源太多了~
2.使用线程来实现并发编程~
线程比进程更轻量,每个进程可以执行一个任务,每个线程也能执行一个任务(执行一段代码),也能进行并发编程。
这时候有人又有问题了,凭什么说进程重?线程为什么又要轻量些?
首先,进程重在资源申请释放(在仓库中找东西)
线程是包含在进程中的,一个进程中的多个线程,共有同一份资源(内存+文件)
只是创建进程的第一个线程的时候(由于要分配资源),成本是相对高的,后续这个进程中再创建其他线程~这个时候成本都是要更低一些,不必再分配资源了。
1.进程包含线程,一个进程里可以有一个线程,也可以有多个线程。
2.进程和线程都是为了处理并发编程这样的场景。
但是进程有问题,频繁创建和释放的时候效率较低,相比之下,线程更轻量,更高效。
3.操作系统创建进程,要给进程分配资源,进程是操作系统分配资源的基本单位。
操作系统创建的线程,是要在CPU上调度执行,线程是操作系统调度执行的基本单位。
(前面的例子我们讲的是进程的调度过程,其实准确来说应该是线程的调度过程)
4.进程具有独立性,每个进程都有各自独立的虚拟地址空间,一个进程挂了,不会影响到其他进程。
然而,同一个进程中的多个线程,共有同一个内存空间~一个线程挂了,可能影响其他线程,甚至是整个进程崩溃。