友们!我们乘胜追击、承上启下、攻坚克难、勇往直前…… 好吧,废话不多说,之前总是说道线程安全,今天就讲一下线程安全的铺垫知识,咱们从根来:
首先,我们要知道在CPU物理硬件层面上是有3级缓存的,注意这个是物理硬件存在的。三级缓存结构如图:
- 一个CPU中就有L1缓存和L2缓存,L1缓存的2部分与L2缓存可以读写连通,实现L1的两部分缓存可连通(注意这个原理也是缓存一致性的根本)。
- L3缓存不属于任何CPU,它是内存的代表,即内存的数据刷写到L3加快读写,且也通过连通性,实现了不同CPU的数据读写。
- 一般情况下,缓存的数据仅用于读取,但为了提高性能就想到了数据写入缓存中,缓存用于读与写不仅是读,可这样就会出现缓存一致性问题,顾提出解决:
(1).写直达:同时刷写缓存和内存,但无论缓存是否存在,每次都刷写内存,性能低。
(2).写回:要写入的数据,已在缓存中就更新缓存并标记;要写的数据不在缓存中并且无标记,则写入内存后再刷写缓存并标记;要写的数据不在缓存中并且有标记,被标记数据和要写的数据都写入内存,再将要写的数据刷写缓存中并标记。但在多CPU下,L1/L2的数据仅本身CPU可见,数据未写入L3/内存,其他CPU可能读旧数据,仍存在一致性问题。
(3).写传播 (总线嗅探):解决写回问题,1个CPU更新缓存,通过广播,其他CPU监测到也更新自身缓存,但无论数据是否相同都更新,性能下降。
(4).MESI事务串行化 (总线锁Lock/缓存锁-实现):保证CPU的顺序操作对其他CPU可见。MESI协议有4种状态:已修改(缓存已更新-内存未更新)、已失效(非最新)、独占 (因单CPU占用-写操作不广播)、共享(此状态下需要先广播再修改); 独占时,其他CPU从内存读最新数据时会降为共享,比写传播性能高。
(5).以上解释了为了性能去实现写入缓存操作、为了解决写入缓存的一致性问题在4种解决方法中选择最完善的MESI协议。
- MESI协议的缓存一致性底层都是缓存锁和总线锁Lock完成的,Lock指令相当于内存屏障(内存栅栏),内存屏障3功能: (注:volatile用内存屏障底层Lock实现)
(1).避免指令重排:防止系统可能会将后面的指令先执行仅仅是结果有序的状况。
(2).强制缓存无效:将数据写入内存并让所有缓存无效,CPU只能去内存中读取在。
(3).缓存锁:是对L3级缓存的锁,但对于跨L3缓存、不支持缓存锁、不在缓存中的数据还是要用Lock总线锁。(后面再细化)
操作系统(即内核态):
- 功能:决定哪个进程/线程使CPU、管理内存、管理硬件、提供-程序与系统间的接口。
- 操作系统分区:用户空间(其代码仅访问此空间)、用户空间-供应用程序访问-用户态;
内核空间(其代码可访问全空间)、 内核空间-供内核程序访问-内核态。
- 程序在系统调用时,CPU中断,切换到内核态执行,完成后CPU切回用户态中断处执行。
- 虚拟内存:多进程共用内存时,为了互不影响会分配虚拟内存 (映射不同段/页的内存)。
(1).内存分段:程序在虚拟内存中被分-(栈, 堆, 数据, 代码)4段,加偏移量找对应内存位置,缺点是内存碎片化、内存交换效率低。
(2).内存分页:程序用的虚拟内存为整体,通过页表映射内存,进程将虚拟内存页数据存到对应内存页中,内存连续。但都有缺页异常问题。
(3).缺页异常:访问的页数据在磁盘中(不在内存中/内存不足未载入),操作系统则复制数据到内存,更新页表,恢复进程运行。如果是内存不足就会涉及页面置换。
(4).页面置换:内存不足时,将(最近未使用)数据存到磁盘,腾出内存空间,供新数据使用。默认(最近未使用的页面置换算法)。
(5).页面置换算法:LRU (老化, NRU, NFU)算法:最近未使用的先置换; FIFO算法:先进先出,最早数据先置换(即便正在使用); 第二次机会算法:FIFO的优化,要置换数据正在使用会保留; 时钟算法:第二次机会算法优化,更少时间执行算法; 工作集/工作集时间算法:合理开销,性能提高但实现复杂; 工作集算法:类似滑动窗口的,将局部的工作空间利用。
(6).简单页表:用于映射内存,多进程下,每个进程都有自己的页表,造成占内存大。
(7).多级页表:(页表分级) 一级页表含全映射,当虚拟内存在一级中但未找到内存映射时,先不创建,使用时创建二级页表。多级页表占内存逐级减小,一级占用最大,二级次之,内存整体可用空间提高。但多级页表进行虚拟内存与内存转址更繁琐 (消耗时间)。
(8).TLB (页表缓存/转址旁路缓存/快表):封装在CPU中的转址对应表,CPU寻址先查TLB,因为常访问的页较固定,TLB命中率高。
(9).段页式内存管理:分段/分页并非对立,分段与分页+TLB 组合使用,提高内存利用率,但增加硬件成本和系统开销。
- DMA:通过CPU授权IO权限在无CPU下可执行,通过系统和内存总线传数据-并发强。
(1).通常:CPU将数据从磁盘拷到内存,读写并复制到用户进程,用户进程读写并复制到socket缓存区,socket复制到网卡。其中读写共4次,复制4次,且期间CPU等待。
(2).DMA:取代CPU职能,上述操作CPU不用等待,但仍4读写4复制。
(3).mmap+write:磁盘(DMA缓冲)复制到内存,内存用mmap映射到用户进程(读写未变),内存复制到socket缓存区,socket(DMA缓冲)复制到网卡。4读写3复制。
(4).sendfile:磁盘(DMA缓冲)复制到内存,内存用sendfile读写同时操作到用户进程,用户进程也用sendfile读写到socket缓存区,内存复制到socket,socket(DMA缓冲)复制到网卡。2读写3复制,因为sendfile管道是读写都可进行的。
(5).SG-DMA:若网卡支持SG-DMA,上述中,内存可直接复制到网卡。2读写2复制。
GB级大文件不适用零拷贝:缓存易占满,预读命中低, 应使用 直接IO+异步IO
Linux网络协议(简单提一下,后期说):
- 网络协议由TCP/IP模型实现-(4层) 应用层—传输层—网络层—网络接口层
- 程序发送数据包—系统调用—切至内核且数据拷至socket缓冲区—4层协议—网卡。
- 发送需从上到下经历4层封数据,接收则从下到上剥离4层拿数据。
- 网络性能指标:带宽(链路最大传输速度b/s)、延时(发送请求到收到响应时间)、吞吐量(单位时间内成功传输的数据量b/s)、PPS(网络包的传输速率—包/s)、是否正常连通、TCP连接数、丢包率、重传率(重传网络包比例)。
- 网络配置查看 (ip,ifconfig)、socket信息查看 (netstat,ss)、网络吞吐率和PPS (sar)、连通性和延时性(ping)。
Linux系统:
- CPU负载-(可运行与不可中断-进程数和)、 CPU利用率-(正运行进程-实时占CPU百分比)。
- CPU负载高利用率低即等待任务多时,使用IO密集型任务:核心线程数=CPU数X2。
- CPU利用率高负载低即任务执行时间长时,使用CPU密集型:核心线程数=CPU数+1。
- 混合密集型:核心线程数=((线程等待时间+线程CPU时间)/线程CPU时间)* CPU数。
- CPU使用率100%—top找占CPU最高 -(top可找负载高、top可找利用率高)
CPU飙升
- 代码中读取数据量大时,系统内存耗尽,导致Full GC次数过多,系统缓慢(系统不可用)
- 代码中有耗CPU操作,导致CPU过高,系统运行缓慢,(系统不可用)
- 代码有阻塞-耗时、代码线程进入wait等待、代码进入死锁
声明:因文章是个人笔记,顾偏向于知识总结,文中提到的相关知识还需细化学习,因篇幅有限无法逐一讲解,最后谢谢支持与指正。