为什么要有进程和线程
引入这个概念是为了解决”并发编程“的问题,现在的CPU都有多个核心,一个CPU可以有8核甚至更多,这么多核心每个核心都相当于一个CPU,我们想要进一步提高程序运行的速度,就要把这么多的CPU核心运用起来。
什么是进程
进程:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
但看进程的解释好像不是那么的直观,我们运用我们的电脑给大家举个例子。
我们打开电脑上的任务管理器->找到进程
我们可以看到进程菜单下面会有很多的任务,这样的一个一个的任务就是多个进程。我们可以看到每个应用就是一个进程,我们双击打开的应用是一个进程,系统后端也会调用进程。
进程是操作系统资源分配的基本单位
如何去理解这一句话呢?
在我们或者后台运行一个进程的时候,操作系统就会把CPU和内存等资源分配给这个进程,供进程完成它的任务,操作系统是以进程为资源分配的基本单位的。
进程的描述和组织
PCB(进程控制块)描述
PCB描述是讲清楚PCB中都有那些属性。
进程使用PCB来进行描述。PCB是一个结构体,那为什么不是Java中的类或者其他语言中的一类东西,而是C语言中的结构体呢?因为操作系统是使用C语言编写的,所以就会使用C语言中的结构体来构造PCB。
进程的组织
进程之间会使用双向链表来进行组织,双向链表把PCB之间进行了链接。
创建一个进程本质上是创建一个PCB的结构体,把这个结构体插入到双向链表之中。
销毁一个进程本质上是把链表上的我这个PCB进行删除。
任务管理器上查看进程的列表本质上就是遍历这个PCB链表。
并发
我们的进程不可能是线性执行的,很多人有着打代码的时候听歌曲的习惯,当打开idea的时候,就会启动一个idea的进程,这时候我们启动另一个进程网易云音乐,就相当于启动了idea和网易云音乐两个进程。这两个进程是并发执行的才可以使得我们能够一边打代码一边听歌曲,那么并发怎么实现的呢?
并行:微观上同一时刻,两个核心同时进行的称之为并行,就是我们通常意义上来说的一起执行两个进程。
并发:微观上,运用一个核心运行,但是切换的速度是非常快的,比如先运行idea,在运行网易云音乐,虽然是来回切换的,但是切换的速度都是非常迅速的,宏观上感受不到切换的时间间隔,就给人并发的感觉。
因此我们把并行和并发统称为并发。
PCB中描述了那些进程的特性?
pcb中包含着很多进程的属性,这些属性都是用来表述进程的
一、PID
这是进程的身份标识符,含有唯一的数字,用于识别进程。
二、内存指针
因为在创建内存的时候操作系统会给进程分配一定的内存和CUP等资源,内存指针表明了这个进程正在使用的是哪一部分的内存。
三、文件描述表
文件描述表表明了进程含有的硬盘上的文件的资源
内存指针和文件描述表共同指明了进程持有哪些硬件资源。
四、进程调度相关的属性
1、进程的状态
(1)就绪状态:进程随叫随到,随时准备好去CPU上执行,我们把进程准备就绪的状态叫做进程的就绪状态
(2)运行状态:进程正在CPU上运行的状态叫做运行状态。
(3)阻塞状态:进程不能被CPU调度,如正在执行某些硬盘上的IO操作就会被阻塞
2、优先级
PCB中还有描述进程优先级的属性,哪个进程重要,不可缺失,哪个进程的优先级就高。
3、上下文
按照逻辑来想一想,当我们的idea软件调用之后,CPU就会进行运行。当我们又打开网易云音乐,进程进行来回切换的时候,要做到连续,就需要保存进程的进度然后再切换,如果不保存进程上下文的状态,每次切换都从头开始,那我们什么工作都不能做。
所以上下文就是操作系统进行切换进程的时候为该进程保存的一种中间状态,等到下一次该进程又被运行的时候,就可以接着之前的进度运行。
进程的上下文其实保存在CPU的寄存器中。
4、记账信息
统计每个进程在CPU上执行指令的数目和运行的时间来决定下一个阶段该如何调度。
五、内存管理
说到内存管理我们就不得不说说内存的虚拟地址空间
在程序中所获得的内存其实并不是真正的物理内存地址,而是一个虚拟的地址空间
内存
内存上可以存储很多的数据,内存可以想象成宿舍里面的走廊,每个宿舍表示所占的空间是Byte,每个房间上还有编号,这个就是地址,这个地址也就是物理地址,这是真正的内存的地址,内存访问地址有个了不起的特性,就是随机访问。
那说了这些我们还是不明白为什么要在程序中使用虚拟地址空间。
虚拟地址
我们的程序正常来讲会单独的分配一个内存,因为一个程序可以理解为一个进程,会有一段单独的内存空间用来被使用,但是我们的程序一旦出现bug,这个bug又正好会访问到其他进程的地址,那么这个程序就会非常的危险,你运行着自己的程序,结果歌听不了了,或者操作系统崩溃了,这就很可能是自己写的程序有bug,这个bug访问到了歌曲或者操作系统的内存地址,进行了更改,我们就会造成很多所损失,所以就引入了虚拟地址的概念。
程序所更改的都是虚拟地址,计算机中的MMU组件会把虚拟地址转换到真正的物理内存地址,当MMU检测到程序所更改的地址超出了操作系统给这个程序分配的空间的时候,就会把程序强制崩溃,阻止这个程序更改其他进程的地址。
解决进程间相互影响的问题
有的时候我们需要不同的进程之间相互的交流信息,但是由于进程之间的隔离,我们不能直接进行进程之间的通信,我们就需要开辟一块共用的地址空间以便两个需要互相通信的进程的访问,这样就实现了进程之间的安全的通信。
线程
线程与进程的关系
线程与进程之间是包含关系,即进程包含线程,一个进程里面可以有多个线程,但是一个线程不能包含多个进程。
线程的优点
我们有了进程可以用来多进程编程,那我们为什么还需要线程呢?
我们设想这样一个场景,我们买回来了两盒冻牛肉,一盒放在冰箱里,另外一盒现在有两种选择。
1、与另一盒一起放在冰箱里。
2、再买一个冰箱,与另外一盒各自放在一个冰箱里。
显而易见,第一种的方法肯定要优于第二种方法,我们可以将冰箱看作是进程,而冻牛肉我们可以看作是线程。之前说过,进程是操作系统分配资源的基本单位,创建一个进程需要分配新的资源,而线程就可以同时在一个进程里创建多个,共同使用一个进程的资源,这样就不需要新的资源了,这样就实现了资源的共用,节省了资源的开销。
也就是说进程相比于线程来说是一个非常重量级的工具,不管是开创,调度,还是销毁一个进程所消耗的资源都远远超过一个线程的开创调度和销毁,所以我们就需要线程这一概念。
线程的描述
一个进程内部至少要有一个线程,当有有且只有一个线程的时候,这个线程PCB就代表着这个进程,也就是上面介绍进程所说的,但是当一个进程里面有多个线程的时候,就会发生改变,一个进程中每一个线程就会成为一个PCB,其中的pid,内存指针,文件描述表和是共享的,但是每个线程有自己的独特的线程调度的相关属性,就比如状态、优先级、上下文、记账信息等。操作系统实际调度过程是以线程为单位进行调度的。
那么线程是越多越好吗?
线程的增加可以提高程序运行的效率,那么线程是不是越多越好?
线程的执行是在CPU的核心上的,CPU的核的数量是有限的,所以线程的数量也不应该太多,线程的执行是抢占是执行的,当线程太多的时候CPU就会不断的去调度线程,这样就把时间全部浪费在了调度线程的过程当中了,所以程序执行的效率也不会有多大的提升。
并且,线程也有这样的一个特性,当一个线程崩溃的时候,一个线程所在的进程可能也会随之崩溃,造成整个进程的终止,线程多了之后,线程发生异常的概率就增大了,因此线程并不是越多越好的。