目录
1. 操作系统
2. 进程/任务(Process/Task)
2.1 进程
2.2 进程管理
2.2.1 进程的结构体
2.2.2 进程调度
2.3 并行 和 并发
2.4 内存分配 -- 内存管理(Memory Manage)
2.5 进程间通信
3. 线程
1. 操作系统
操作系统是一组做计算机资源管理的软件的统称。目前常见的操作系统有:Windows系列、Unix系列、Linux系列、OSX系列、Android系列、iOS系列、鸿蒙等。
操作系统有两个基本功能:
1) 防止硬件被时空的应用程序滥用(管理好各种硬件设备)
2) 向应用程序提供简单一致的机制来控制复杂而又通常大相径庭的低级硬件设备。(给软件提供稳定的运行环境)
2. 进程/任务(Process/Task)
2.1 进程
运行起来的程序就是进程.
使用任务管理器就可以看见当前设备正在运行的程序,这些程序就是进程,
2.2 进程管理
进程管理就是分为两部分
1.描述一个进程:使用结构体/类,将一个进程有哪些信息表示出来.
2.组织这些进程:使用一定的数据结构,把这些结构体/对象放在一起.
举例:
2.2.1 进程的结构体
进程的结构体(PCB)有那些属性?(核心)
1.pid : 每一个进程需要有一个唯一的身份标识.
2.内存指针:当前这个进程使用的内存是哪一部分.进程要跑起来,就要消耗一定的资源.
3.文件描述符表:
文件: 硬盘上的存储的数据,往往都是以文件为单位进行整理的.进程每打开一个文件就会产生一个文件描述符(标识被打开的这个文件),一个进程可能会打开很多个文件,就对应了一组文件描述符,把这些文件描述符放在一个顺序表这样的结构,就构成了文件描述符表.就可以知道进程在运行的过程中使用了哪些资源.
4.进程是操作系统进行资源调度的基本单位.
2.2.2 进程调度
接下来这一组属性,都是描述和CPU资源相关的属性,这些属性都是辅助进行进程调度.
1.进程状态: 简单的认为,进程的状态主要是有两个:就绪态(该进程已经准备好,随时可以上CPU进行执行),阻塞态(该进程暂时无法上CPU进行执行)
2.进程的优先级:进程执行之间是有优先顺序的.
3.进程的上下文:
上下文,就是描述了,当前进程执行到哪里这样的"存档记录",进程在离开CPU的时候就要把当前运行的中间结果进行存档,等到进程再次回到CPU上的时候,再恢复之前的存档信息,从上次的结果继续往后执行.
4.进程的记账信息:
统计了每个进程在CPU上执行的多久,可以作为调度的参考信息.
补充:
1. 这里的8核16线程是什么意思?
2.操作系统往往使用双向链表的形行来组织PCB的.
创建了一个进程,就是创建了链表的节点
销毁一个进程就是把链表的节点进行删除
遍历进程列表就是在遍历链表.
2.3 并行 和 并发
并行:同一时刻,两个核心同时执行两个进程,此时这两个进程就是并行执行的
并发:一个核心,先执行进程1,执行一会儿之后,再去执行进程2,再执行一会儿之后,再去执行进程3,此时只要这里的切换速度足够快,看起来,进程1 2 3就是“同时”执行的。这就是并发。
上述电脑只有8个核心,也可以同时执行115个任务,这是通过并发+并行的方式来完成的。
这个过程完全是操作系统自身控制的,程序员是感知不到的。所以很多时候,就把并行+并发统称为并发。
并发程度更高的话,就可以成为高并发。比如一个核心(主体)并发的执行1w个任务就可以成为高并发。不过,这是没有具体的数据限度的。
2.4 内存分配 -- 内存管理(Memory Manage)
操作系统对内存资源的分配,采用的是空间模式 —— 不同进程使用内存中的不同区域,互相之间不会干扰。
操作系统给进程分配的内存是以“虚拟地址空间”的方式进行分配的。每个进程访问的内存地址,都不是真实的物理内存的地址。
如图所示:
如上图所示:如果每个进程都直接访问物理内存的地址,如果进程1,出现了bug(比如数组下表越界,野指针等) ,可能进程2的内存也会受到影响.就不能保证操作系统的稳定性.对此:操作系统给进程分配的内存是以“虚拟地址空间”的方式进行分配的。每个进程访问的内存地址,都不是真实的物理内存的地址。如下图所示:
站在这两个进程的角度看,他们的代码中操作的内存地址就是0x00-0xff这一段。这里访问的内存就会被操作系统自动映射到真实的物理内存上,但是进程自身感知不到实际的物理地址是啥。这时,遇到野指针就没事了。任何一个内存操作,都通过页表来翻译一下。如果遇到野指针,0x2ff,拿着这个地址,发现页表上就没有这个地址,无法翻译,也就无法真正的修改物理内存,也就不会对别的进程的内存数据造成干扰。
一个进程无法直接干预另一个进程的内存内容,“进程的独立性”,“每一个进程有自己独立的地址内容”,大大提升了操作系统的“稳定性”。
2.5 进程间通信
有时候,需要进程之间进行交互,互相配合。如果每一个进程直接访问物理内存,其实是没有隔离性,也就不需要进程通信,进程1直接把算好的结果,写到进程2的内存就行,那跟入室抢劫,没啥区别。
所谓进程间通信,就是在隔离性的前提霞,找一个公共的区域,让两个进程借助这个区域来完成数据交换。就是在隔离的前提下,做了一个小小的妥协~
操作系统提供的进程间通信具体实现方式比较多,有很多种:管道、消息队列、共享内存、信号……
在Java圈子里,主要使用文件,socket这两种方式来完成进程通信。
举例:
3. 线程
进程,是比较“重量的”,速度慢、消耗资源多。
创建一个进程,成本比较高。销毁一个进程,成本也比较高。调度一个进程,成本也比较高,多进程编程可以解决并发编程的问题,但不是一个高效的选择。
为什么进程是“重量”的呢?
主要是体现在资源分配上。资源分配往往是一个耗时操作。
线程,则是更轻量的进程。(轻量级进程)
资源分配:
比如系统要给进程分配一块内存
(1)系统就需要遍历自己的空闲内存的表(数据结构)找到一个大小差不多的空间,进行分配。
(2)很多个进程都在问系统申请资源,系统进行资源分配的时候,要一个一个来。
线程如何解决资源分配这一个耗时操作呢?
线程约定,一个进程中可以包含多个线程。此时这多个线程每个线程都是一个独立可以调度执行的“执行流”这些执行流之间本身就是并发的。同时,这些线程共用同一份进程的系统资源。
意味着,对于线程而言,系统资源是已经分配好了的,创建线程就省下了分配资源开销。