结论:CPU的一个核心同一时间只能运行一个线程,考虑可能有线程会睡眠等待事件完成,另一个线程正好在此期间运行,CPU也不会空闲,所以CPU的核心数*2的线程数比较适合IO密集型操作
图灵机是计算机的最初概念,在一张无限长的纸带上有一个一个的方格,机器头在纸带上移动,读取方格中的信息,根据机器头存储的程序处理输出结果到方格上。其中涉及到存储程序、逻辑计算、输入、输出等功能。图灵机只是一个抽象的模型,而冯诺依曼提出了计算机的组成,这是现代计算机的基础,所以冯诺依曼被称之为现代计算机之父,而图灵被称之为计算机之父。
冯诺依曼体系结构:
从计算机宏观角度来看CPU承担了运算器和控制器的作用,内存和硬盘承担存储器的作用,又由于二进制数基础,CPU执行的二进制指令。
为啥说从宏观角度?CPU内部也有这五部分组件,运算器 - 运算单元(ALU)、存储器 - 寄存器组(MU)、控制器 - 控制单元(CU)、输入输出 - 内部总线
CPU执行程序,程序又存储在内存,在单机的情况下CPU和内存是瓶颈。假设只有一个程序运行在CPU(单核)上,程序不会有延迟,也不会有瓶颈问题,顶多是内存不够用。但实际上,不可能只有一个程序,所以在多程序(任务)的情况下CPU如何运行?假设内存无限(不考虑内存瓶颈)
分布式情况下要加上IO瓶颈
假设三个任务A、B、C,CPU运行一秒,如何分配时间?
给出两个概念
两种情况各有优劣:
在设计和开发中,两种情况都会遇到,我们称之为CPU密集型和IO密集型,
注意:现代CPU都是时间片模式,第一种情况只是抽象,N个时间片都给一个任务使用,不就相当于一秒只执行一个任务么
现在的计算机基本都是多CPU(或多核)模式,那么一个CPU同一时间只能运行一个任务。要运行其他任务如何进行切换的呢?
进程切换会以简洁的方式说明,具体的知识点会在后续文章详解
CPU执行的任务是由二进制组成,二进制输入,CPU逻辑处理,然后二进制输出。这跟什么很像?图灵机。CPU是机器头,任务指令是纸袋。计算机是按指令的顺序执行,一系列顺序的指令组成指令流(方法或函数)。
由于存储器材料和设计的不同,越靠近CPU的存储器访问速度越快,但价格也越贵,那么其容量也越小。容量从大到小排列(访问速度从慢到快)硬盘>内存>CPU缓存(L3>L2>L1)。硬盘是永久存储的,内存是掉电就失的,指令流最终肯定要存在永久存储的设备中。但访问速度会限制CPU的效率,需要将硬盘的指令流暂存到内存,再暂存到CPU缓存中。
CPU缓存无法存放完整的指令流,那么指令流就要放到内存或硬盘(代码帧),CPU操作指令流产生的数据为私有数据(栈帧)、CPU操作多指令流产生的数据为共享数据(堆栈帧)、无需通过CPU产生的数据为静态数据(数据帧)。一个任务有多个指令流,帧组合在一起形成段,即代码段、栈段、堆栈段、数据段。
指令流在CPU中执行,实际上就是与四个帧交互,为了加速CPU运行,需要将帧的元数据(描述信息)存储在CPU的存储单元(寄存器组)中。帧组成段,段组成任务(进程)。寄存器组存储任务的元数据。
段的元数据为啥不存储在缓存中?寄存器具有更快的访问速度,也可以进行权限校验。缓存中都是数据或指令。
程序之间要保持独立性,不能直接通过物理内存访问。Intel提出虚拟内存和特权级的概念。虚拟内存保证进程的隔离性,就是假定每个进程都拥有一整片内存,进程占用的空间可以很大(只要不超过CPU的位数(32位或64位),2^32 byte或2^64 byte)。特权级保证进程的安全性,用户进程特权级低于内存进程,不能随意访问,不然内核容易崩溃。
进程在CPU运行给定的时间,当时间片运行完需要切换到其他进程。这就需要时间机制来计算运行时间。再使用中断机制暂停当前运行的进程,切换到其他进程。
寄存器组存储进程的元数据,进程切换时要恢复下一个要运行的进程的元数据,那么原来的数据将被覆盖,此时就要在内存保存原先进程的元数据。Intel将保存寄存器组数据的段称之为任务段(TSS - Task State Segment)
虚拟内存是虚拟出来的,与物理内存不一样,CPU要将虚拟内存映射为物理内存。进程的不同,两者映射关系也不同。进程切换时也必须重新映射。
切换寄存器组就是进程的上下文切换。进程的上下文就是运行时的环境信息
说了这么多,进程切换的流程总结一下:
由此可见进程切换需要很多操作
CPU密集型主要是依赖于CPU性能的任务,会涉及到大量的计算操作、数据处理、逻辑运算等。进程数一般为CPU的核心数,正好一个核心执行一个进程,能最大程度利用CPU的性能。若进程数多于CPU核心数,需要耗费一些性能用于进程切换。反之进程数少于CPU核心数,又不能充分利用CPU。
IO密集型主要是依赖于输入输出(I/O)的任务。涉及磁盘、网络、数据库等外部设备或资源。进程数一般为CPU核心数的两倍,也就是一个CPU核心管理两个进程。假定一个进程由于访问外部资源时无法立即获取反馈,进程可能处于阻塞状态,那么CPU要切换到另一个进程执行。这样可以不让CPU歇着,一直处理业务进程。为啥进程数不能为CPU核心数的两倍以上?还是由于进程切换耗费性能,而频繁的切换会进一步导致性能下降。
Netty的NioEventLoopGroup明显是IO密集型,进程数更适合为CPU核心数的两倍