张店区创业孵化中心有做网站的吗,网络设计的原理,宁波企业网站设计,合肥市建设网官方网站参考#xff1a;大佬图解文章 → 小林coding 
简介#xff1a;之前在学习小林大佬的八股文时#xff0c;摘录了一些个人认为比较重要的内容#xff0c;方便后续自己复习。【持续更新ing ~#x1f4af;】 注#xff1a;加五角星标注的#xff0c;是当前掌握不牢固的…参考大佬图解文章 → 小林coding 
简介之前在学习小林大佬的八股文时摘录了一些个人认为比较重要的内容方便后续自己复习。【持续更新ing ~】 注加五角星标注的是当前掌握不牢固的需要继续深入学习的内容  ★ \color{red}{★} ★ 文章目录 一、前言略二、硬件结构略三、操作系统结构略四、内存管理4.1 虚拟内存 说下虚拟内存的作用★★★★★ \color{red}{说下虚拟内存的作用 ★★★★★} 说下虚拟内存的作用★★★★★ 缺页中断★★★★★ \color{red}{缺页中断 ★★★★★} 缺页中断★★★★★ 4.2 malloc的内存分配malloc() 分配内存过程malloc() 分配的是物理内存吗malloc 申请的内存free 释放内存会归还给操作系统吗为什么不全部使用 mmap 来分配内存 ★ \color{red}{★} ★既然 brk 那么牛逼为什么不全部使用 brk 来分配 ★ \color{red}{★} ★free() 函数只传入一个内存地址为什么能知道要释放多大的内存 4.3 内存满了会发生什么内存分配过程  ★ \color{red}{★} ★哪些内存可以被回收可被回收的内存类型 ★ \color{red}{★} ★回收内存带来的性能影响针对回收内存导致的性能影响常见的解决方式稍微了解即可如何保护一个进程不被 OOM 杀掉呢稍微了解即可 4.4 在 4GB 物理内存的机器上申请 8G 内存会怎么样 ★ \color{red}{★} ★swap机制  ★ \color{red}{★} ★如何避免预读失效和缓存污染的问题  ★ \color{red}{★} ★  五、进程管理5.1 进程、线程基础知识进程的状态进程的上下文切换  ★ \color{red}{★} ★什么是线程线程的优缺点线程与进程的比较调度时机调度原则 5.2 进程间有哪些通信方式 ★ \color{red}{★} ★5.3 多线程冲突了怎么办互斥与同步的实现和使用 5.4 怎么避免死锁死锁产生的条件  ★ \color{red}{★} ★利用工具排查死锁问题避免死锁问题的发生 5.5 什么是悲观锁、乐观锁互斥锁与自旋锁读写锁乐观锁与悲观锁 5.6 一个进程最多可以创建多少个线程  ★ \color{red}{★} ★5.7 线程崩溃了进程也会崩溃吗为什么线程崩溃不会导致 JVM 进程崩溃总结  六、调度算法略七、文件系统7.1 文件系统全家桶略7.2 进程写文件时进程发生了崩溃已写入的数据会丢失吗 ★ \color{red}{★} ★问题结论page 与 Page Cache内核缓冲区Page Cache 的优劣势  ★ \color{red}{★} ★  八、设备管理 略九、网络系统9.1 什么是零拷贝为什么要有 DMA 技术?传统的文件传输有多糟糕如何优化文件传输的性能如何实现零拷贝 ★ \color{red}{★} ★PageCache 有什么作用大文件传输用什么方式实现 9.2 I/O 多路复用select/poll/epollI/O 多路复用  ★ \color{red}{★} ★select、poll实现多路复用的方式及区别epoll实现多路复用的方式及区别 9.3 高性能网络模式Reactor 和 Proactor9.4 什么是一致性哈希 十、Linux命令略十一、学习心得略十二、常见问题个人补充32位系统和64位系统有什么区别32位和64位针对的是CPU处理器什么是内存对齐优点是什么 ★ \color{red}{★} ★   一、前言略 
二、硬件结构略 
三、操作系统结构略 
四、内存管理 
4.1 虚拟内存 
虚拟内存 是一种计算机系统内存管理的技术它使得应用程序认为它拥有连续可用的内存一个连续完整的地址空间。而实际上它通常是被分隔成多个物理内存碎片还有部分暂时存储在外部磁盘存储器上在需要时进行数据交换。 说下虚拟内存的作用★★★★★ \color{red}{说下虚拟内存的作用 ★★★★★} 说下虚拟内存的作用★★★★★ 
第一虚拟内存可以使得进程的运行内存超过物理内存大小因为程序运行符合局部性原理CPU 访问内存会有很明显的重复访问的倾向性。而对于那些没有被经常使用到的内存我们可以把它换出到物理内存之外比如硬盘上的 swap 区域。第二由于每个进程都有自己的页表所以每个进程的虚拟内存空间就是相互独立的。进程也没有办法访问其他进程的页表这些页表是私有的这就解决了多进程之间地址冲突的问题。第三页表里的页表项中除了物理地址之外还有一些标记属性的比特比如控制一个页的读写权限标记该页是否存在等。在内存访问方面操作系统提供了更好的安全性。 缺页中断★★★★★ \color{red}{缺页中断 ★★★★★} 缺页中断★★★★★ 
当进程访问的虚拟地址在页表中查不到时系统会产生一个缺页异常。进入系统内核空间分配物理内存、将磁盘数据以页的方式加载到内存中、更新进程页表最后再返回用户空间恢复进程的运行。 
流程缺页异常虚拟地址不在页表中访问数据不在内存中 → 分配物理空间 → 加载磁盘数据到内存 → 更新进程页表虚拟内存和物理内存之间的映射关系 → 返回用户空间  恢复进程运行 
4.2 malloc的内存分配 
malloc() 分配内存过程 
实际上malloc() 并不是系统调用而是 C 库里的函数用于动态分配内存。malloc 申请内存的时候会有两种方式向操作系统申请堆内存。 
用户分配的内存小于 128 KB阈值通过 brk()系统调用从【堆】分配内存 。其实就是通过 brk() 函数将「堆顶」指针向高地址移动获得新的内存空间。用户分配的内存大于 128 KB阈值通过 mmap()系统调用在【文件映射区域】分配内存。也就是从文件映射区“偷”了一块内存。 
注意不同的 glibc 版本定义的阈值也是不同的。 
malloc() 分配的是物理内存吗 
malloc() 分配的是虚拟内存。 
如果分配后的虚拟内存没有被访问的话虚拟内存是不会映射到物理内存的这样就不会占用物理内存了。 
只有在访问已分配的虚拟地址空间时操作系统通过查找页表发现虚拟内存对应的页没有在物理内存中就会触发缺页中断然后操作系统会建立虚拟内存和物理内存之间的映射关系。 
malloc 申请的内存free 释放内存会归还给操作系统吗 
malloc 通过 brk()方式申请的内存free 释放内存的时候并不会把内存归还给操作系统而是缓存在 malloc 的内存池中待下次使用malloc 通过 mmap()方式申请的内存free 释放内存的时候会把内存归还给操作系统内存得到真正的释放。 
为什么不全部使用 mmap 来分配内存 ★ \color{red}{★} ★ 
频繁通过 mmap()分配内存的话不仅每次都会发生运行态的切换还会发生缺页中断在第一次访问虚拟地址后这样会导致 CPU 消耗较大。 
为了改进这两个问题malloc 通过 brk()系统调用在堆空间申请内存时由于堆空间是连续的所以直接预分配更大的内存来作为内存池当内存释放时就缓存在内存池中。 
等下次申请内存时就直接从内存池取出对应的内存块就行了而且可能这个内存块的虚拟地址与物理地址的映射关系还存在这样不仅减少了系统调用的次数也减少了缺页中断的次数这将大大降低 CPU 的消耗。 
既然 brk 那么牛逼为什么不全部使用 brk 来分配 ★ \color{red}{★} ★ 
随着系统频繁地 malloc 和 free 尤其对于小块内存堆内将产生越来越多不可用的碎片导致“内存泄露”。而这种“泄露”现象使用 valgrind 是无法检测出来的。 
所以malloc 实现中充分考虑了 brk 和 mmap 行为上的差异及优缺点默认分配大块内存 (128KB) 才使用 mmap 分配内存空间。 
free() 函数只传入一个内存地址为什么能知道要释放多大的内存 
malloc 返回给用户态的内存起始地址比进程的堆空间起始地址多了 16 字节这里就保存了该内存块的描述信息比如有该内存块大小及起始地址等。这样当执行 free() 函数时free 会对传入进来的内存地址向左偏移 16 字节然后从这个 16 字节的分析出当前的内存块的大小自然就知道要释放多大的内存了。  
4.3 内存满了会发生什么 
内存分配过程  ★ \color{red}{★} ★ 
应用程序通过 malloc 函数申请内存的时候实际上申请的是虚拟内存此时并不会分配物理内存。 
当应用程序读写了这块虚拟内存CPU 就会去访问这个虚拟内存这时会发现这个虚拟内存没有映射到物理内存 CPU 就会产生缺页中断进程会从用户态切换到内核态并将缺页中断交给内核的 Page Fault Handler 缺页中断函数处理。 
缺页中断处理函数会看是否有空闲的物理内存如果有就直接分配物理内存并建立虚拟内存与物理内存之间的映射关系。 
如果没有空闲的物理内存那么内核就会开始进行回收内存的工作回收的方式主要是两种直接内存回收和后台内存回收。 后台内存回收kswapd在物理内存紧张的时候会唤醒 kswapd 内核线程来回收内存这个回收内存的过程是异步的不会阻塞进程的执行。  直接内存回收direct reclaim如果后台异步回收跟不上进程内存申请的速度就会开始直接回收这个回收内存的过程是同步的会阻塞进程的执行。  
如果直接内存回收后空闲的物理内存仍然无法满足此次物理内存的申请那么内核就会放最后的大招了 ——触发 OOM Out of Memory机制。 
OOM Killer 机制会根据算法选择一个占用物理内存较高的进程然后将其杀死以便释放内存资源。如果物理内存依然不足OOM Killer 会继续杀死占用物理内存较高的进程直到释放足够的内存位置。  
哪些内存可以被回收可被回收的内存类型 ★ \color{red}{★} ★ 
可被回收的内存类型有文件页和匿名页 
文件页内核缓存的磁盘数据Buffer和内核缓存的文件数据Cache 干净页大部分文件页都可以直接释放内存需要时再从磁盘读取即可。脏页而那些被应用程序修改过并且暂时还没写入磁盘的数据脏页就得先写入磁盘然后才能进行内存释放。 匿名页部分内存没有实际载体不像文件缓存有硬盘文件这样一个载体比如 堆、栈 数据等。这部分内存很可能还要再次被访问所以不能直接释放内存。 
回收内存带来的性能影响 
【文件页】的回收对于干净页是直接释放内存这个操作不会影响性能而对于脏页会先写回到磁盘再释放内存这个操作会发生磁盘 I/O 的会影响系统性能。 
【匿名页】的回收如果开启了 Linux Swap 机制那么 Swap 机制会将不常访问的匿名页换出到磁盘中并释放内存给其他更需要的进程使用。再次访问这些内存时再从磁盘换入到内存中这个操作会发生磁盘 I/O是会影响系统性能的。 
文件页和匿名页的回收都是基于 LRU 算法也就是优先回收不常访问的内存。回收内存的操作基本都会发生磁盘 I/O 的如果回收内存的操作很频繁意味着磁盘 I/O 次数会很多这个过程势必会影响系统的性能。 
针对回收内存导致的性能影响常见的解决方式稍微了解即可 
设置 /proc/sys/vm/swappiness调整文件页和匿名页的回收倾向尽量倾向于回收文件页设置 /proc/sys/vm/min_free_kbytes调整 kswapd 内核线程异步回收内存的时机设置 /proc/sys/vm/zone_reclaim_mode调整 NUMA 架构下内存回收策略建议设置为 0这样在回收本地内存之前会在其他 Node 寻找空闲内存从而避免在系统还有很多空闲内存的情况下因本地 Node 的本地内存不足发生频繁直接内存回收导致性能下降的问题 
如何保护一个进程不被 OOM 杀掉呢稍微了解即可 
我们可以通过调整进程的 /proc/[pid]/oom_score_adj值来降低被 OOM killer 杀掉的概率 
4.4 在 4GB 物理内存的机器上申请 8G 内存会怎么样 ★ \color{red}{★} ★ 
这个问题在没有前置条件下就说出答案就是耍流氓。这个问题要考虑三个前置条件 
操作系统是 32 位的还是 64 位的申请完 8G 内存后会不会被使用 因为只有虚拟内存被真正访问后触发了缺页中断才会分配对应的物理内存操作系统有没有使用 Swap 机制 
所以我们要分场景讨论。简单总结下 
在 32 位操作系统因为进程最大只能申请 3 GB 大小的用户空间虚拟内存所以直接申请 8G 内存会申请失败。在 64位 位操作系统因为进程最大只能申请 128 TB 大小的虚拟内存即使物理内存只有 4GB申请 8G 内存也是没问题因为申请的内存是虚拟内存。如果这块虚拟内存被访问了要看系统有没有 Swap 分区 如果没有 Swap 分区因为 4 GB的物理空间不够进程会被操作系统杀掉原因是 OOM内存溢出如果有 Swap 分区即使物理内存只有 4GB程序也能正常使用 8GB 的内存进程可以正常运行  
swap机制  ★ \color{red}{★} ★ 
Swap 就是把一块磁盘空间或者本地文件充当内存来使用防止物理内存不够用。 
它包含换出和换入两个过程 换出Swap Out 是把进程暂时不用的内存数据存储到磁盘中并释放这些数据占用的内存换入Swap In是在进程再次访问这些内存的时候把它们从磁盘读到内存中来 优缺点 应用程序实际可以使用的内存空间将远远超过系统的物理内存。由于硬盘空间的价格远比内存要低因此这种方式无疑是经济实惠的。 当然频繁地读写硬盘会显著降低操作系统的运行速率这也是 Swap 的弊端。触发条件 内存不足采用直接内存回收Direct Page Reclaim。直接内存回收是同步的过程会阻塞当前申请内存的进程。内存闲置应用程序在启动阶段使用的大量内存在启动后往往都不会使用不活跃。通过运行的后台守护进程 - kSwapd我们可以将这部分只使用一次的内存交换到磁盘上为其他内存的申请预留空间。由于kSwapd 是后台进程所以回收内存的过程是异步的不会阻塞当前申请内存的进程。   
如何避免预读失效和缓存污染的问题  ★ \color{red}{★} ★ 
在这之前先介绍几个概念便于后续理解 Linux 和 MySQL 的缓存 Linux 操作系统的缓存在应用程序读取文件数据时Linux 操作系统是会对读取的文件数据进行缓存的会缓存在文件系统中的 Page Cache 。 Page Cache 属于内存空间里的数据由于内存访问比磁盘访问快很多在下一次访问相同的数据就不需要通过磁盘 I/O 了命中缓存就直接返回数据即可。 因此Page Cache 起到了加速访问数据的作用。MySQL 的缓存 MySQL 的数据是存储在磁盘里的为了提升数据库的读写性能Innodb 存储引擎设计了一个缓冲池Buffer PoolBuffer Pool 属于内存空间里的数据。 有了缓冲池后 当读取数据时如果数据存在于 Buffer Pool 中客户端就会直接读取 Buffer Pool 中的数据否则再去磁盘中读取。当修改数据时首先是修改 Buffer Pool 中数据所在的页然后将其页设置为脏页最后由后台线程将脏页写入到磁盘。  磁盘数据页预读的原因   首先操作系统出于空间局部性原理靠近当前被访问数据的数据在未来很大概率会被访问到。因此会存在磁盘数据的预读。 因此预读机制带来的好处就是减少了 磁盘 I/O 次数提高系统磁盘 I/O 吞吐量。   其次再来说下 预读失效 和 缓存污染  预读失效如果这些被提前加载进来的页并没有被访问相当于这个预读工作是白做了这个就是预读失效。缓存污染当我们在批量读取数据时由于数据被访问了一次这些大量数据都会被加入到「活跃 LRU 链表」里然后之前缓存在活跃 LRU 链表或者 young 区域里的热点数据全部都被淘汰了如果这些大量的数据在很长一段时间都不会被访问的话那么整个活跃 LRU 链表或者 young 区域就被污染了。 传统的 LRU 算法无法避免下面这两个问题 
预读失效 导致缓存命中率下降缓存污染 导致缓存命中率下降 
为了解决「预读失效」的问题Linux 和 MySQL 对传统的 LRU 链表做了改进 
Linux 操作系统实现两个了 LRU 链表活跃 LRU 链表active list和非活跃 LRU 链表inactive list。MySQL Innodb 存储引擎是在一个 LRU 链表上划分来 2 个区域young区域 和 old 区域。 
但是如果还是使用「只要数据被访问一次就将数据加入到活跃 LRU 链表头部或者 young 区域」这种方式的话那么还存在缓存污染的问题。 
为了解决「缓存污染」的问题Linux 操作系统和 MySQL Innodb 存储引擎分别提高了升级为热点数据的门槛 
Linux 操作系统在内存页被访问第二次时才将页从 inactive list 升级到 active list 里。MySQL Innodb在内存页被访问第二次时并不会马上将该页从 old 区域升级到 young 区域因为还要判断当前数据停留在 old 区域的时间 如果第二次的访问时间与第一次访问的时间 在 1 秒内默认值那么该页就不会从 old 区域升级到 young 区域如果第二次的访问时间与第一次访问的时间 超过 1 秒那么该页就会从 old 区域升级到 young 区域  
通过提高了进入 active list 或者 young 区域的门槛后就很好了避免缓存污染带来的影响。 
五、进程管理 
5.1 进程、线程基础知识 
根据任务的不同把 CPU 上下文切换分成进程上下文切换、线程上下文切换、中断上下文切换。 
进程的状态 
在操作系统的虚拟内存管理中通常会把阻塞状态的进程物理内存空间换出到硬盘等需要再次运行的时候再从硬盘换入到物理内存。这样可以避免对物理内存的浪费。 
挂起状态描述进程没有占用实际的物理内存空间的情况。 
导致进程挂起的原因不只是因为进程所使用的内存空间由于被换出到硬盘而不在物理内存还包括如下情况 
通过 sleep 让进程间歇性挂起其工作原理是设置一个定时器到期后唤醒进程。用户希望挂起一个程序的执行比如在 Linux 中用 CtrlZ 挂起进程 
进程的上下文切换  ★ \color{red}{★} ★ 
CPU 寄存器和程序计数器 是 CPU 在运行任何任务前所必须依赖的环境这些环境就叫做 CPU 上下文。 
CPU 寄存器 是 CPU 内部一个容量小但是速度极快的内存高速缓存。程序计数器 则是用来存储 CPU 正在执行的指令位置、或者即将执行的下一条指令位置。 
CPU 上下文切换就是先把前一个任务的 CPU 上下文CPU 寄存器和程序计数器保存起来然后加载新任务的上下文到这些寄存器和程序计数器最后再跳转到程序计数器所指的新位置运行新任务。 系统内核会存储保持下来的上下文信息当此任务再次被分配给 CPU 运行时CPU 会重新加载这些上下文这样就能保证任务原来的状态不受影响让任务看起来还是连续运行。 进程的上下文切换到底是切换什么呢 进程是由内核管理和调度的所以进程的切换只能发生在内核态。 所以进程的上下文切换不仅包含了 虚拟内存、栈、全局变量 等用户空间的资源还包括了 内核堆栈、寄存器 等内核空间的资源。 ★ \color{red}{★} ★  发生进程上下文切换有哪些常见场景 当某个进程的时间片耗尽进程就从运行状态变为就绪状态系统从就绪队列选择另外一个进程运行进程在系统资源不足比如内存不足时要等到资源满足后才可以运行这个时候进程也会被挂起并由系统调度其他进程运行当进程通过睡眠函数 sleep 这样的方法将自己主动挂起时自然也会重新调度当有优先级更高的进程运行时为了保证高优先级进程的运行当前进程会被挂起由高优先级进程来运行发生硬件中断时CPU 上的进程会被中断挂起转而执行内核中的中断服务程序  
什么是线程 
线程是进程当中的一条执行流程。 
同一个进程内多个线程之间可以共享进程的虚拟内存空间【代码段、数据段、打开的文件】等资源但每个线程各自都有一套独立的 【寄存器和栈】这样可以确保线程的控制流是相对独立的。 ★ \color{red}{★} ★ 
线程的优缺点 
优点 一个进程中可以同时存在多个线程各个线程之间可以并发执行各个线程之间可以共享进程的虚拟地址空间和文件等资源 缺点 当进程中的一个线程崩溃时会导致其所属进程的所有线程崩溃这里是针对 C/C 语言Java语言中的线程崩溃不会造成进程崩溃具体分析原因可以看这篇线程崩溃了进程也会崩溃吗。  
线程与进程的比较 
所谓操作系统的任务调度实际上的调度对象是线程而进程只是给线程提供了【虚拟内存、全局变量】等资源这些资源在线程间是共享的仅仅是 【栈和寄存器】等算是线程的私有数据。 
进程是资源包括内存、打开的文件等分配的单位线程是 CPU 调度的单位进程拥有一个完整的资源平台而线程只独享必不可少的资源如寄存器和栈线程同样具有就绪、阻塞、执行三种基本状态同样具有状态之间的转换关系线程能减少并发执行的时间和空间开销 
对于线程相比进程能减少开销体现在 
线程的创建速度比进程快因为进程在创建的过程中还需要资源管理信息比如内存管理信息、文件管理信息而线程在创建的过程中不会涉及这些资源管理信息而是共享它们线程的终止时间比进程快因为线程释放的资源相比进程少很多同一个进程内的线程切换比进程切换快因为线程具有相同的地址空间虚拟内存共享这意味着同一个进程的线程都具有同一个页表那么在切换线程时不需要切换页表。而对于进程之间的切换切换的时候要把页表给切换掉而页表的切换过程开销是比较大的由于同一进程的各线程间共享内存和文件资源那么在线程之间数据传递时就不需要经过内核了这就使得线程之间的数据交互效率更高了 
所以不管是时间效率还是空间效率线程比进程都要高。 
调度时机 
在进程的生命周期中当进程从一个运行状态到另外一状态变化的时候其实会触发一次调度。 
比如以下状态的变化都会触发操作系统的调度 
从就绪态 → 运行态当进程被创建时会进入到就绪队列操作系统会从就绪队列选择一个进程运行从运行态 → 阻塞态当进程发生 I/O 事件而阻塞时操作系统必须选择另外一个进程运行从运行态 → 结束态当进程退出结束后操作系统得从就绪队列选择另外一个进程运行 
因为这些状态变化的时候操作系统需要考虑是否要让新的进程给 CPU 运行或者是否让当前进程从 CPU 上退出来而换另一个进程运行。 
如果硬件时钟提供某个频率的周期性中断那么可以根据如何处理时钟中断 把调度算法分为两类 
非抢占式调度算法挑选一个进程然后让该进程运行直到被阻塞或直到该进程退出才会调用另外一个进程也就是说不会理时钟中断这个事情。抢占式调度算法挑选一个进程然后让该进程只运行某段时间。如果在该时段结束时该进程仍然在运行时则会把它挂起接着调度程序从就绪队列挑选另外一个进程。这种抢占式调度处理需要在时间间隔的末端发生时钟中断以便把 CPU 控制权返回给调度程序进行调度也就是常说的 时间片机制。 也就是根据时间片机制运行一段时间后将CPU控制权交给调度程序。 
调度原则 
针对上面的五种调度原则总结成如下 
CPU 利用率调度程序应确保 CPU 是始终匆忙的状态这可提高 CPU 的利用率系统吞吐量吞吐量表示的是单位时间内 CPU 完成进程的数量长作业的进程会占用较长的 CPU 资源因此会降低吞吐量相反短作业的进程会提升系统吞吐量周转时间周转时间是进程运行阻塞时间等待时间的总和一个进程的周转时间越小越好等待时间这个等待时间不是阻塞状态的时间而是进程处于就绪队列的时间等待的时间越长用户越不满意响应时间用户提交请求到系统第一次产生响应所花费的时间在交互式系统中响应时间是衡量调度算法好坏的主要标准。 
说白了这么多调度原则目的就是要使得进程要「快」。 
5.2 进程间有哪些通信方式 ★ \color{red}{★} ★ 
每个进程的用户地址空间都是独立的一般而言是不能互相访问的但内核空间是每个进程都共享的所以进程之间要通信必须通过内核。 
Linux 内核提供了不少进程间通信的方式其中最简单的方式就是管道管道分为「匿名管道」和「命名管道/有名管道」。 
【匿名管道】顾名思义它没有名字标识匿名管道是特殊文件只存在于内存没有存在于文件系统中shell 命令中的「|」竖线就是匿名管道通信的数据是无格式的流并且大小受限通信的方式是单向的数据只能在一个方向上流动。如果要双向通信需要创建两个管道再来匿名管道是只能用于通过 fork()产生的存在父子关系的进程间通信匿名管道的生命周期随着进程创建而建立随着进程终止而消失。 
【命名管道/有名管道】突破了匿名管道只能在亲缘关系进程间的通信限制因为使用命名管道的前提需要在文件系统创建一个类型为 p 的设备文件那么毫无亲缘关系的进程就可以通过这个设备文件进行通信。另外不管是匿名管道还是命名管道进程写入的数据都缓存在内核中另一个进程读取数据时候自然也是从内核中获取同时通信数据都遵循先进先出原则不支持 lseek 之类的文件定位操作。 
【消息队列】克服了管道通信的数据是无格式字节流的问题。消息队列实际上是保存在内核的「消息链表」消息队列的消息体是可以用户自定义的数据类型。发送数据时会被分成一个一个独立的消息体当然接收数据时也要与发送方发送的消息体的数据类型保持一致这样才能保证读取的数据是正确的。 消息队列通信的速度不是最及时的毕竟每次数据的写入和读取都需要经过用户态与内核态之间的拷贝过程。 
【共享内存】可以解决消息队列通信中用户态与内核态之间数据拷贝过程带来的开销。它直接分配一个共享空间每个进程都可以直接访问就像访问进程自己的空间一样快捷方便不需要陷入内核态或者系统调用大大提高了通信的速度享有最快的进程间通信方式之名。但是便捷高效的共享内存通信带来新的问题多进程竞争同个共享资源会造成数据的错乱。 补充现代操作系统对于内存管理采用的是虚拟内存技术也就是每个进程都有自己独立的虚拟内存空间不同进程的虚拟内存映射到不同的物理内存中。所以即使进程 A 和 进程 B 的虚拟地址是一样的其实访问的是不同的物理内存地址对于数据的增删改查互不影响。   共享内存机制就是从两个进程分别拿出一块虚拟地址空间来映射到相同的物理内存中。这样这个进程写入的数据另一个进程马上就能看到不需要拷贝来拷贝去传来传去大大提高了进程间通信的速度。   那么就需要【信号量】来保护共享资源以确保任何时刻只能有一个进程访问共享资源这种方式就是互斥访问。信号量不仅可以实现访问的互斥性还可以实现进程间的同步。信号量其实是一个计数器表示的是资源个数其值可以通过两个原子操作来控制分别是 P 操作和 V 操作。 
与信号量名字很相似的叫【信号】它俩名字虽然相似但功能一点儿都不一样。信号是进程间通信机制中唯一的异步通信机制信号可以在应用进程和内核之间直接交互内核也可以利用信号来通知用户空间的进程发生了哪些系统事件。信号事件的来源主要有硬件来源如键盘 CltrC 和软件来源如 kill命令。一旦有信号发生进程有三种方式响应信号 1. 执行默认操作、2. 捕捉信号、3. 忽略信号。有两个信号是应用进程无法捕捉和忽略的即 SIGKILL 和 SIGSTOP这是为了方便我们能在任何时候结束或停止某个进程。 
前面说到的通信机制都是工作于同一台主机如果要与不同主机的进程间通信那么就需要 【Socket】 通信了。Socket 实际上不仅用于不同的主机进程间通信还可以用于本地主机进程间通信。可根据创建 Socket 的类型不同分为三种常见的通信方式一个是基于 TCP 协议的通信方式一个是基于 UDP 协议的通信方式一个是本地进程间通信方式。 注意 TCP方式通信监听的 socket 和真正用来传送数据的 socket是「两个」 socket一个叫作监听 socket一个叫作已完成连接socket。 成功连接建立之后双方开始通过 read 和 write 函数来读写数据就像往一个文件流里面写东西一样。    对于 UDP 来说不需要维护连接那么也就没有所谓的发送方和接收方甚至都不存在客户端和服务端的概念只要有一个 socket 多台机器就可以任意通信因此每一个 UDP 的 socket 都需要 bind。    以上就是进程间通信的主要机制了。你可能会问了那线程通信间的方式呢 
同个进程下的线程之间都是共享进程的资源只要是共享变量都可以做到线程间通信比如全局变量所以对于线程间关注的不是通信方式而是关注多线程竞争共享资源的问题信号量也同样可以在线程间实现互斥与同步 
互斥的方式可保证任意时刻只有一个线程访问共享资源同步的方式可保证线程 A 应在线程 B 之前执行 
5.3 多线程冲突了怎么办 
操作系统也为每个进程创建巨大、私有的虚拟内存的假象这种地址空间的抽象让每个程序好像拥有自己的内存而实际上操作系统在背后秘密地让多个地址空间「复用」物理内存或者磁盘。 
线程之间是可以共享进程的资源比如代码段、数据段、堆空间、打开的文件等资源。但每个线程都有自己独立的栈空间 和 寄存器注意线程是不能共享进程的栈空间的。 ★ \color{red}{★} ★ 由于多线程执行操作共享变量的这段代码可能会导致竞争状态因此我们将此段代码称为临界区critical section它是访问共享资源的代码片段一定不能给多线程同时执行。 
而互斥就解决了并发 进程/线程 对临界区的使用问题。 
互斥与同步的实现和使用 
在进程/线程并发执行的过程中进程/线程 之间存在协作关系例如有互斥、同步的关系。 
为了实现进程/线程间正确的协作操作系统必须提供实现进程协作的措施和方法主要的方法有两种 
锁加锁、解锁操作信号量P、V 操作 
这两个都可以方便地实现 进程/线程 互斥而信号量比锁的功能更强一些它还可以方便地实现 进程/线程 同步。 补充 自旋锁/忙等待锁 这是最简单的一种锁一直自旋占用CPU直到锁可用。在单处理器上需要抢占式的调度器即不断通过时钟中断一个线程运行其他线程。否则自旋锁在单 CPU上无法使用因为一个自旋的线程永远不会放弃 CPU。无等待锁顾明思议就是获取不到锁的时候不用自旋。 既然不想自旋那当没获取到锁的时候就把当前线程放入到锁的等待队列然后执行调度程序把 CPU 让给其他线程执行。 5.4 怎么避免死锁 
简单来说死锁问题的产生是由两个或者以上线程并行执行时争夺资源而互相等待造成的。 
死锁产生的条件  ★ \color{red}{★} ★ 
死锁只有同时满足以下四个必要条件才会发生 
互斥多个线程不能同时使用同一个资源持有并等待线程 A 在等待资源 2 的同时并不会释放自己已经持有的资源不可抢占当线程已经持有了资源 在自己使用完之前不能被其他线程获取环路等待两个线程获取资源的顺序构成了环形链 
利用工具排查死锁问题 
C语言pstack  gdbJAVAjstack 
避免死锁问题的发生 
避免死锁问题就只需要破环四个条件其中一个条件就可以最常见并且可行的就是使用 资源有序分配法来破坏环路等待条件。 
线程 A 和 线程 B 获取资源的顺序要一样。 
首先是线程 A 是先尝试获取资源 A然后尝试获取资源 B 同样的线程 B 是先尝试获取资源 A然后尝试获取资源 B。 
也就是说线程 A 和 线程 B 总是以相同的顺序申请自己想要的资源这样就可以打破死锁了。。 
5.5 什么是悲观锁、乐观锁 
互斥锁与自旋锁 
互斥锁加锁失败后线程会释放 CPU切换给其他线程来使用有切换成本自旋锁加锁失败后线程会忙等待持续占有CPU试图抢占锁资源直到拿到锁无切换成本但持续占有CPU资源 
对于互斥锁加锁失败而阻塞的现象是由操作系统内核实现的。当加锁失败时内核会将线程置为「睡眠」状态等到锁被释放后内核会在合适的时机唤醒线程当这个线程成功获取到锁后就可以继续执行。如下图  所以互斥锁加锁失败时会从用户态陷入到内核态让内核帮我们切换线程虽然简化了使用锁的难度但存在一定的性能开销成本。 
那这个开销成本是什么呢会有两次线程上下文切换的成本 
当线程加锁失败时内核会把线程的状态从「运行」状态设置为「睡眠」状态然后把 CPU 切换给其他线程运行状态运行 → 睡眠接着当锁被释放时之前「睡眠」状态的线程会变为「就绪」状态然后内核会在合适的时间把 CPU 切换给该线程运行。状态睡眠 → 就绪 
线程上下文切换的是什么 当两个线程是属于同一个进程因为虚拟内存空间是共享的所以在切换时这些资源就保持不动只要切换线程的私有数据、【寄存器】和【线程栈】等不共享的数据。 
上下切换的耗时有大佬统计过大概在几十纳秒到几微秒之间如果你锁住的代码执行时间比较短那可能上下文切换的时间都比你锁住的代码执行时间还要长。 
所以如果你能确定被锁住的代码执行时间很短就不应该用互斥锁而应该选用自旋锁否则使用【互斥锁】。 
自旋锁是通过 CPU 提供的 CAS 函数Compare And Swap在「用户态」完成加锁和解锁操作不会主动产生线程上下文切换所以相比互斥锁来说会快一些开销也小一些。 
互斥锁和自旋锁都是最基本的锁读写锁可以根据场景来选择这两种锁其中的一个进行实现。 
读写锁 
读写锁适用于能明确区分读操作和写操作的场景适用于读多写少的场景。 
读写锁的工作原理是 
当「写锁」没有被线程持有时多个线程能够并发地持有读锁这大大提高了共享资源的访问效率因为「读锁」是用于读取共享资源的场景所以多个线程同时持有读锁也不会破坏共享资源的数据。但是一旦「写锁」被线程持有后读线程获取读锁的操作会被阻塞而且其他写线程的获取写锁的操作也会被阻塞。 
那为了避免锁资源竞争中的线程饥饿问题有了公平读写锁 公平读写锁 比较简单的一种方式是用队列把获取锁的线程排队不管是写线程还是读线程都按照先进先出的原则加锁即可这样读线程仍然可以并发也不会出现线程「饥饿」的现象。 
乐观锁与悲观锁 
悲观锁多线程同时修改共享资源的概率比较高很容易出现冲突所以访问共享资源前先要上锁。互斥锁、自旋锁、读写锁都属于悲观锁。 场景读少写多发生冲突概率高 乐观锁也可以叫做无锁编程。先修改完共享资源再验证这段时间内有没有发生冲突。如果没有其他线程修改过资源那么操作完成如果发现有其他线程已经修改过这个资源就放弃本次操作版本号机制。 场景读多写少发生冲突概率低。Git、在线文档多人编辑特性乐观锁虽然去除了加锁解锁的操作但是一旦发生冲突重试的成本非常高所以只有在冲突概率非常低且加锁成本非常高的场景下才考虑使用乐观锁。  
5.6 一个进程最多可以创建多少个线程  ★ \color{red}{★} ★ 
这个问题跟两个东西有关系进程的虚拟内存空间上限、系统参数限制 
进程的虚拟内存空间上限因为创建一个线程操作系统需要为其分配一个栈空间。如果线程数量越多所需的栈空间就越大那么虚拟内存就会占用越多。   查看进程创建线程时默认分配的栈空间大小ulimit -aLinux中默认为8M然后使用当前操作系统假设32位系统的用户态虚拟内存3G来除以一个线程占用的栈空间的大小假设为10M3G/10M ≈ 300个。   当然在64位的操作系统下理论上可以创建的线程个数为用户态虚拟内存128T/ 一个线程占用的栈空间的大小假设为10M≈ 1000w个有点夸张。。   事实上肯定创建不了那么多线程除了虚拟内存的限制还有系统的限制。 系统参数限制虽然 Linux 并没有内核参数来控制单个进程创建的最大线程个数但是有系统级别的参数来控制整个系统的最大线程个数。 
简单总结下 
32 位系统用户态的虚拟空间只有 3G如果创建线程时分配的栈空间是 10M那么一个进程最多只能创建 300 个左右的线程。64 位系统用户态的虚拟空间大到有 128T理论上不会受虚拟内存大小的限制而会受系统参数或性能限制。 
5.7 线程崩溃了进程也会崩溃吗 
为什么线程崩溃不会导致 JVM 进程崩溃 
因为 JVM 自定义了信号处理函数拦截了 SIGSEGV 信号针对这两者不让它们崩溃。 
总结 
正常情况下操作系统为了保证系统安全所以针对非法内存访问会发送一个 SIGSEGV 信号而操作系统一般会调用默认的信号处理函数一般会让相关的进程崩溃。 
但如果进程觉得 “罪不致死”那么它也可以选择自定义一个信号处理函数这样的话它就可以做一些自定义的逻辑比如记录 crash 信息等有意义的事。 
回过头来看为什么虚拟机会针对 StackoverflowError 和 NullPointerException 做额外处理让线程恢复呢针对 stackoverflow 其实它采用了一种栈回溯的方法保证线程可以一直执行下去而捕获空指针错误主要是这个错误实在太普遍了。 
为了这一个很常见的错误而让 JVM 崩溃那线上的 JVM 要宕机多少次所以出于工程健壮性的考虑与其直接让 JVM 崩溃倒不如让线程起死回生并且将这两个错误/异常抛给用户来处理。 
六、调度算法略 
七、文件系统 
7.1 文件系统全家桶略 
7.2 进程写文件时进程发生了崩溃已写入的数据会丢失吗 ★ \color{red}{★} ★ 
问题结论 
分情况要看 page cache 内核缓冲区的数据是否真正的被刷盘 
因为进程在执行 write 使用缓冲 IO系统调用时实际上是将文件数据写到了内核的 page cache它是文件系统中用于缓存文件数据的缓冲。所以即使进程崩溃了文件数据还是保留在内核的 page cache我们在读数据时也是从内核的 page cache 读取因此还是依然读的进程崩溃前写入的数据。内核会找个合适的时机将 page cache 中的数据持久化到磁盘。但是如果 page cache 里的文件数据在持久化到磁盘化到磁盘之前系统发生了崩溃那这部分数据就会丢失了。 当然 我们也可以在程序里调用 fsync()函数在写完文件时立刻将文件数据持久化到磁盘这样就可以解决系统崩溃导致的文件数据丢失的问题。 
page 与 Page Cache内核缓冲区 
page 是内存管理分配的基本单位 Page Cache 由多个 page 构成。page 在操作系统中通常为 4KB 大小32bits/64bits而 Page Cache 的大小则为 4KB 的整数倍。 
另一方面并不是所有 page 都被组织为 Page Cache。 
Linux 系统上可供用户访问的内存分为两类 
File-backed pages文件备份页也就是 Page Cache 中的 page对应于磁盘上的若干数据块对于这些页最大的问题是脏页回盘Anonymous pages匿名页不对应磁盘上的任何磁盘数据块它们是进程的运行时内存空间例如方法栈、局部变量表等属性 
Page Cache 的优劣势  ★ \color{red}{★} ★ 
Page Cache 的优势 
加快数据访问 如果数据能够在内存中进行缓存那么下一次访问就不需要通过磁盘 I/O 了直接命中内存缓存即可。 由于内存访问比磁盘访问快很多因此加快数据访问是 Page Cache 的一大优势。减少 I/O 次数提高系统磁盘 I/O 吞吐量 得益于 Page Cache 的缓存以及预读能力而程序又往往符合局部性原理。因此通过一次 I/O 将多个 page 装入 Page Cache 能够减少磁盘 I/O 次数 进而提高系统磁盘 I/O 吞吐量。 
Page Cache 的劣势 
需要占用额外物理内存空间物理内存在比较紧张时可能会导致频繁的 swap 操作最终导致系统的磁盘 I/O 负载的上升。应用层并没有对外提供很好的管理 API。 应用层即使想优化 Page Cache 的使用策略也很难进行因此一些应用选择在用户空间实现自己的 page 管理而不使用 page cache。 例如 MySQL InnoDB 存储引擎以 16KB 的页进行管理。在某些应用场景下比 Direct I/O 多一次磁盘读 I/O 以及磁盘写 I/O。 
八、设备管理 略 
九、网络系统 
9.1 什么是零拷贝 
首先说下什么是 零拷贝是指计算机执行操作时CPU不需要先将数据从某处内存复制到另一个特定区域。这种技术通常用于通过网络传输文件时节省CPU周期和内存带宽。 
为什么要有 DMA 技术? 
DMA直接内存访问Direct Memory Access 
简单理解就是在进行 I/O 设备和内存数据传输时数据搬运的工作全部交给 DMA 控制器而 CPU 不再参与任何与数据搬运相关的事情这样 CPU 就可以去处理别的事务。 
传统的文件传输有多糟糕 
传统文件传输中会发生 4 次数据拷贝其数据流动方向 磁盘 → 内核缓冲区PageCache → 用户缓冲区 → 内核socket缓冲区 → 网卡缓冲区 
其中 read()系统调用内核缓冲区PageCache → 用户缓冲区 write()系统调用用户缓冲区 → 内核socket缓冲区 
这种简单又传统的文件传输方式是有缺点的 
存在冗余的上下文切换和数据拷贝在高并发系统里是非常糟糕的多了很多不必要的开销会严重影响系统性能。内存与磁盘数据传输的工作都是由 CPU 完成的而此时 CPU 不能执行其他任务会特别浪费 CPU 资源。 
所以要想提高文件传输的性能就需要减少「用户态与内核态的上下文切换」和「内存数据拷贝」的次数。 
如何优化文件传输的性能 
① 先来看看如何减少「用户态与内核态的上下文切换」的次数呢 
读取磁盘数据的时候之所以要发生上下文切换这是因为用户空间没有权限操作磁盘或网卡内核的权限最高这些操作设备的过程都需要交由操作系统内核来完成所以一般要通过内核去完成某些任务时就需要使用操作系统提供的系统调用函数。 而一次系统调用必然会发生 2 次上下文切换首先从用户态切换到内核态当内核执行完任务后再切换回用户态交由进程代码执行。 所以要想减少上下文切换到次数就要减少系统调用的次数。 
② 再来看看如何减少「数据拷贝」的次数 
在前面我们知道了传统的文件传输方式会历经 4 次数据拷贝而且这里面「从内核的读缓冲区拷贝到用户的缓冲区里再从用户的缓冲区里拷贝到 socket 的缓冲区里」这个过程是没有必要的。 
因为文件传输的应用场景中在用户空间我们并不会对数据「再加工」所以数据实际上可以不用搬运到用户空间透传因此用户的缓冲区是没有必要存在的。 
如何实现零拷贝 ★ \color{red}{★} ★ 它通过一次系统调用sendfile()方法合并了 磁盘读取 read()方法与 网络发送 这两个操作分别降低了 上下文切换 与 数据拷贝的次数 。  ★ \color{red}{★} ★ 而之所以降低了数据拷贝的次数是因为数据拷贝都是发生在内核中的不需要通过切换用户态无需借助对应的用户缓冲区天然就降低了数据拷贝的次数。 零拷贝技术是基于 内核缓冲区PageCache的其实现方式通常有 2 种 
mmap()替代read()  write() 通过使用 mmap() 来替代 read() 可以减少一次数据拷贝的过程 mmap()系统调用函数会直接把内核缓冲区PageCache里的数据「映射」到用户空间这样操作系统内核与用户空间就不需要再进行任何的数据拷贝操作。具体过程如下 应用进程调用了 mmap() 后DMA 会把磁盘的数据拷贝到内核的缓冲区里。接着应用进程跟操作系统内核「共享」这个缓冲区应用进程再调用 write()操作系统直接将内核缓冲区PageCache的数据拷贝到 socket 缓冲区中这一切都发生在内核态由 CPU 来搬运数据最后把内核的 socket 缓冲区里的数据拷贝到网卡的缓冲区里这个过程是由 DMA 搬运的。但这还不是最理想的零拷贝因为仍然需要通过 CPU 把内核缓冲区PageCache的数据拷贝到 socket 缓冲区里而且仍然需要 4 次上下文切换因为系统调用还是 2 次仅仅是减少了一次数据拷贝的过程。  sendfile() 该系统调用函数合并了 磁盘读取 与 网络发送 两个操作减少了上下文切换及数据拷贝的次数。 在 Linux 内核版本 2.1 中提供了一个专门发送文件的系统调用函数 sendfile(int out_fd, int in_fd, off_t *offset, size_t count)。它的前两个参数分别是目的端和源端的文件描述符后面两个参数是源端的偏移量和复制数据的长度返回值是实际复制数据的长度。 首先它可以替代前面的 read() 和 write() 这两个系统调用这样就可以减少一次系统调用也就减少了 2 次上下文切换的开销。其次该系统调用可以直接把内核缓冲区PageCache里的数据拷贝到 socket 缓冲区里不再经过到用户态这样就只有 2 次上下文切换一次系统调用对应两次上下文切换和 3 次数据拷贝磁盘 → 内核缓冲区PageCache → socket缓冲区 → 网卡缓冲区。但是这还不是真正的零拷贝技术如果网卡支持 SG-DMAThe Scatter-Gather Direct Memory Access技术和普通的 DMA 有所不同我们可以进一步减少通过 CPU 把内核缓冲区PageCache里的数据拷贝到 socket 缓冲区的过程。 也就是只要 2 次数据拷贝 磁盘 → 内核缓冲区 P a g e C a c h e  → 网卡缓冲区 \color{red}{磁盘 → 内核缓冲区PageCache → 网卡缓冲区} 磁盘→内核缓冲区PageCache→网卡缓冲区 不再经过 s o c k e t 缓冲区 \color{red}{不再经过socket缓冲区} 不再经过socket缓冲区。  
这就是所谓的零拷贝Zero-copy技术因为没有在内存层面去拷贝数据也就是说全程没有通过 CPU 来搬运数据所有的数据都是通过 DMA控制器 来进行传输的。 
零拷贝技术的文件传输方式相比传统文件传输的方式减少了 2 次上下文切换和数据拷贝次数只需要 2 次上下文切换和数据拷贝次数就可以完成文件的传输。而且 2 次的数据拷贝过程都不需要通过 CPU2 次都是由 DMA控制器 来搬运。 
所以总体来看零拷贝技术可以把文件传输的性能提高至少一倍以上。 
PageCache 有什么作用 
上面经常提到的「内核缓冲区」实际上是磁盘高速缓存PageCache。 
PageCache 是为了提升对文件的读写效率Linux 内核会以页大小4KB为单位将文件划分为多数据块。当用户对文件中的某个数据块进行读写操作时内核首先会申请一个内存页称为 页缓存与磁盘文件中的数据块进行绑定。 
读写磁盘相比读写内存的速度慢太多了所以我们应该想办法把「读写磁盘」替换成「读写内存」。于是我们会通过 DMA 把磁盘里的数据搬运到内存里这样就可以用读内存替换读磁盘。 
PageCache 的优点主要是两个 
缓存最近被访问的数据加速访问预读功能局部性原理减少磁盘io 
PageCache 的这两个做法将大大提高读写 小文件 时磁盘的性能。 
但是在传输大文件GB 级别的文件的时候PageCache 会不起作用那就白白浪费 DMA 多做的一次数据拷贝造成性能的降低即使使用了 PageCache 的零拷贝也会损失性能。 
因为可能由于 PageCache 被大文件占据大文件本身难以命中 PageCache 缓存并且也会导致「热点」小文件被挤出缓存而无法利用到 PageCache这样在高并发的环境下会带来严重的性能问题。  ★ \color{red}{★} ★ 
大文件传输用什么方式实现 
传输文件的时候我们要根据文件的大小来使用不同的方式 
传输大文件的时候使用「异步 I/O  直接 I/O」 异步 I/O对于磁盘异步 I/O 只支持直接 I/O 内核向磁盘发起读请求但是可以不等待数据准备好就提前返回。 当内核将磁盘中的数据拷贝到进程缓冲区后进程将接收到内核的通知再去处理数据。直接I/O绕开 PageCache 的 I/O 传输小文件的时候则使用「零拷贝技术」 
9.2 I/O 多路复用select/poll/epoll 
监听的 Socket 和真正用来传输数据的 Socket 是两个 
一个叫作 监听 Socket一个叫作 已连接 Socket 
I/O 多路复用  ★ \color{red}{★} ★ 
多路复用一个 线程/进程 处理多个io事件流的请求。 select/poll/epoll内核提供给用户态的多路复用系统调用进程可以通过一个系统调用函数从内核中获取多个事件。 
select/poll/epoll是如何获取网络事件的呢 在获取事件时先把所有连接文件描述符集合传给内核再由内核返回产生了事件的连接最后在用户态中处理这些连接对应的请求即可。 
select、poll实现多路复用的方式及区别 
两者并没有本质区别它们内部都是使用「线性结构」来存储进程关注的 Socket 集合。 
方式 
在使用时首先要把关注的 Socket 集合通过 select/poll 系统调用从用户态拷贝到内核态。然后由内核检测事件当有网络事件产生时内核需要遍历进程关注的 Socket 集合找到有事件产生的 Socket并将其状态标记为 可读/可写。然后把整个 Socket 集合从内核态拷贝到用户态用户态还要再次遍历整个 Socket 集合找到被标记为 可读/可写 的 Socket然后对其处理。 
区别 
select使用 BitsMap位图 的方式存储 fd_set支持的文件描述符个数受限由linux内核中的 FD_SETSIZE 限制默认为 1024只能监听 0~1023 的文件描述符。poll则使用动态数组的方式存储 fd_set以链表形式来组织。突破了 select 中文件描述符的个数限制当然还是会受到系统文件描述符个数限制。 
缺点 很明显发现select和 poll的缺陷在于当客户端越多也就是 Socket 集合越大Socket 集合的2次遍历时间复杂度为 O(n)和2次拷贝会带来很大的开销。 
此方式随着并发数上来性能的损耗会呈指数级增长因此也很难应对 C10K问题即单机1万个并发连接。 
epoll实现多路复用的方式及区别 
epoll是解决 C10K 问题的利器通过两个方面解决了 select/poll的问题。 
epoll在内核里使用「红黑树」来关注进程所有待检测的 Socket。红黑树是个高效的数据结构增删改一般时间复杂度是 O(logn)。 通过对这棵黑红树的管理不需要像 select/poll在每次操作时都传入整个 Socket 集合而只需要传入一个待检测的 socket减少了内核和用户空间大量的数据拷贝和内存分配。epoll 使用事件驱动回调函数的机制内核里维护了一个「链表」来记录有事件产生的就绪事件。 只将有事件发生的 Socket 集合传递给用户态应用程序不需要像 select/poll 那样轮询整个集合包含有事件和无事件的 Socket 大大提高了检测的效率。 
而且epoll支持边缘触发产生事件时仅通知一次和水平触发产生事件时一直通知的方式而 select/poll只支持水平触发。一般而言边缘触发的方式会比水平触发的效率高。 
9.3 高性能网络模式Reactor 和 Proactor 
9.4 什么是一致性哈希 
适用场景分布式系统数据分片的系统。 在分布式系统中每个节点存储的数据是不同的。 
不同的负载均衡算法适用的业务场景也不同的。 
轮询这类的策略只能适用与每个节点的数据都相同的场景访问任意节点都能请求到数据。但是不适用分布式系统因为分布式系统意味着数据【水平切分】到了不同节点上。访问数据时一定要寻址存储该数据的节点。 
哈希算法虽然能建立数据和节点的映射关系但是每次在节点数量发生变化的时候最坏情况下所有数据都需要迁移这样太麻烦了所以哈希算法不适用节点数量变化的场景。 
为了减少迁移的数据量就出现了一致性哈希算法。 
一致性哈希是指将「存储节点」和「数据」都映射到一个首尾相连的哈希环上如果增加或者移除一个节点仅影响该节点在哈希环上顺时针相邻的后继节点其它数据也不会受到影响。 
但是一致性哈希算法不能够均匀的分布节点会出现大量请求都集中在一个节点的情况也就是数据倾斜。在这种情况下进行容灾与扩容时容易出现雪崩连锁反应。 
为了解决一致性哈希算法不能够均匀的分布节点的问题就需要引入虚拟节点对一个真实节点做多个副本。 不再将真实节点映射到哈希环上而是将虚拟节点映射到哈希环上并将虚拟节点映射到实际节点所以这里有「两层」映射关系缓存数据 ➜ 虚拟节点 ➜ 真实节点 
引入虚拟节点后可以会提高节点的均衡度还会提高系统的稳定性当节点数目变化时会有不同的节点共同分担系统的变化。 所以带虚拟节点的一致性哈希方法不仅适合硬件配置不同的节点的场景而且适合节点规模会发生变化的场景。 内部碎片和外部碎片本质上都是对内存空间的浪费区分二者的最主要特征就是内部碎片产生在分区内外部碎片产生在分区外内外是相对于分区/内存块而言的。 内部碎片产生的原因 在固定分区中固定分区的大小大于等于作业大小当分区大小大于作业大小时就会产生不能被其他作业利用的碎片称为内部碎片。 在固定分区存储管理中10KB大小的分区装入8KB大小的作业分区内产生了2KB大小的内部碎片。 
外部碎片产生的原因: 在可变分区存储管理中系统划分给作业的分区大小等于作业大小在分区内就不会产生多余的空间。但是在分区外就可能产生小的内存碎片因为太小不能被分配给作业小的碎片长期积累浪费了大量的内存空间我们称之为外部碎片。 
解决「外部内存碎片」的问题就是内存交换。 
不再需要一次性都把程序加载到物理内存中 而是只有在程序运行中需要用到对应虚拟内存页里面的指令和数据时再加载到物理内存里面去。 
对于已分配的页表项如果存在最近一定时间未访问的页表在物理内存紧张的情况下操作系统会将页面换出到硬盘也就是说不会占用物理内存。 
有了 TLB 后那么 CPU 在寻址时会先查 TLB如果没找到才会继续查常规的页表。 TLB 的命中率其实是很高的因为程序最常访问的页就那么几个。 
数据段包括已初始化的静态常量和全局变量  ★ \color{red}{★} ★ BSS 段包括未初始化的静态变量和全局变量 ★ \color{red}{★} ★ 文件映射段包括动态库、共享内存等 
CPU 只会访问虚拟内存地址  ★ \color{red}{★} ★ 
顺序读比随机读性能好的原因磁盘预读局部性原理  ★ \color{red}{★} ★ 
接收网络数据包网卡缓冲区 → socket缓冲区 → PageCache 内核缓冲区 → 用户缓冲区 
进程的上下文切换不仅包含了虚拟内存、栈、全局变量等用户空间的资源还包括了内核堆栈、寄存器等内核空间的资源。 为什么说 同一个进程下的线程上下文切换的开销要比进程小得多  ★ \color{red}{★} ★ 单进程中可以运行多个线程同进程里的线程可以共享进程的部分资源比如文件描述符列表、进程空间、代码段、全局数据段、堆、共享库等这些共享资源在上下文切换时不需要切换而只需要切换线程的私有数据独立栈、寄存器等不共享的数据因此同一个进程下的线程上下文切换的开销要比进程小得多。 此外还有 tlb 页表的切换开销。 Page Cache 正如其名是对磁盘上 page页的内存缓存同时可以用于读/写操作。 十、Linux命令略 
十一、学习心得略 十二、常见问题个人补充 
32位系统和64位系统有什么区别32位和64位针对的是CPU处理器 
处理数据速度32位处理器每次最多能处理32位数据而64位处理器每次最多处理64位数据。支持内存大小32位操作系统为2^324GB64位操作系统理论值直接达到了 2^6416TB。支持操作系统32位的cpu处理器只能安装32位的电脑操作系统64位的cpu处理器则可以安装32位和64位操作系统所以64位处理器是向下兼容的。支持的软件、系统体积大小也分别不同 
什么是内存对齐优点是什么 ★ \color{red}{★} ★ 
内存对齐是指cpu在读取内存地址时按照一定的偏移量去读取。优点以空间换时间提高读取效率。CPU每次寻址都是要占用时间的并且CPU 访问内存时并不是逐个字节访问而是以字长word size为单位访问所以数据结构应该尽可能地在自然边界上对齐。有些CPU可以访问任意地址上的任意数据而有些CPU只能在特定地址访问数据因此不同硬件平台具有差异性这样的代码就不具有移植性。如果在编译时将分配的内存进行对齐这就具有更好的平台移植性了。若访问未对齐的内存处理器需要做2次内存访问而对齐的内存访问仅需一次访问内存对齐后可以提升性能。 示例在C语言中若通过#pragma pack(4)指定cpu读取单位为4字节而 int a  0 保存在内存地址为 0x00000001 ~ 0x00000004 中。   若 内存不对齐那么cpu就会先去读取 0x00000000 ~ 0x00000003 的前4字节数据并去除 0x00000000 的空数据然后再去读取 0x00000004 ~ 0x00000007 的后4字节数据并去除 0x00000005 ~ 0x00000007 的空数据。 上述过程一共需要CPU访问 2次 内存并做数据的拼接处理后才能读取到整形变量a的完整数据影响性能。  而若 内存对齐则cpu只需访问一次内存即可读取到a的完整数据。  
 文章转载自: http://www.morning.lhhkp.cn.gov.cn.lhhkp.cn http://www.morning.jygsq.cn.gov.cn.jygsq.cn http://www.morning.sryyt.cn.gov.cn.sryyt.cn http://www.morning.jgcyn.cn.gov.cn.jgcyn.cn http://www.morning.nkcfh.cn.gov.cn.nkcfh.cn http://www.morning.zcqgf.cn.gov.cn.zcqgf.cn http://www.morning.c7627.cn.gov.cn.c7627.cn http://www.morning.mttqp.cn.gov.cn.mttqp.cn http://www.morning.srbsr.cn.gov.cn.srbsr.cn http://www.morning.qyfrd.cn.gov.cn.qyfrd.cn http://www.morning.fpkpz.cn.gov.cn.fpkpz.cn http://www.morning.gqwpl.cn.gov.cn.gqwpl.cn http://www.morning.stfdh.cn.gov.cn.stfdh.cn http://www.morning.dyght.cn.gov.cn.dyght.cn http://www.morning.wnbqy.cn.gov.cn.wnbqy.cn http://www.morning.rnhh.cn.gov.cn.rnhh.cn http://www.morning.bplqh.cn.gov.cn.bplqh.cn http://www.morning.znnsk.cn.gov.cn.znnsk.cn http://www.morning.c7510.cn.gov.cn.c7510.cn http://www.morning.skkmz.cn.gov.cn.skkmz.cn http://www.morning.dbdmr.cn.gov.cn.dbdmr.cn http://www.morning.dtnzk.cn.gov.cn.dtnzk.cn http://www.morning.xqjh.cn.gov.cn.xqjh.cn http://www.morning.dsgdt.cn.gov.cn.dsgdt.cn http://www.morning.yznsx.cn.gov.cn.yznsx.cn http://www.morning.tkcct.cn.gov.cn.tkcct.cn http://www.morning.dfmjm.cn.gov.cn.dfmjm.cn http://www.morning.mrlls.cn.gov.cn.mrlls.cn http://www.morning.qljxm.cn.gov.cn.qljxm.cn http://www.morning.xhhzn.cn.gov.cn.xhhzn.cn http://www.morning.qhfdl.cn.gov.cn.qhfdl.cn http://www.morning.fpczq.cn.gov.cn.fpczq.cn http://www.morning.khxwp.cn.gov.cn.khxwp.cn http://www.morning.jgcyn.cn.gov.cn.jgcyn.cn http://www.morning.nthyjf.com.gov.cn.nthyjf.com http://www.morning.tmfm.cn.gov.cn.tmfm.cn http://www.morning.lynb.cn.gov.cn.lynb.cn http://www.morning.dfwkn.cn.gov.cn.dfwkn.cn http://www.morning.tpssx.cn.gov.cn.tpssx.cn http://www.morning.lyhrg.cn.gov.cn.lyhrg.cn http://www.morning.ptlwt.cn.gov.cn.ptlwt.cn http://www.morning.bgkk.cn.gov.cn.bgkk.cn http://www.morning.hyhzt.cn.gov.cn.hyhzt.cn http://www.morning.txltb.cn.gov.cn.txltb.cn http://www.morning.xcdph.cn.gov.cn.xcdph.cn http://www.morning.qkrzn.cn.gov.cn.qkrzn.cn http://www.morning.ftdlg.cn.gov.cn.ftdlg.cn http://www.morning.kxxld.cn.gov.cn.kxxld.cn http://www.morning.schwr.cn.gov.cn.schwr.cn http://www.morning.qsy40.cn.gov.cn.qsy40.cn http://www.morning.wpqwk.cn.gov.cn.wpqwk.cn http://www.morning.nyqb.cn.gov.cn.nyqb.cn http://www.morning.mkzdp.cn.gov.cn.mkzdp.cn http://www.morning.bztzm.cn.gov.cn.bztzm.cn http://www.morning.lqchz.cn.gov.cn.lqchz.cn http://www.morning.zwxfj.cn.gov.cn.zwxfj.cn http://www.morning.srgsb.cn.gov.cn.srgsb.cn http://www.morning.xrqkm.cn.gov.cn.xrqkm.cn http://www.morning.xptkl.cn.gov.cn.xptkl.cn http://www.morning.bjjrtcsl.com.gov.cn.bjjrtcsl.com http://www.morning.fbdtd.cn.gov.cn.fbdtd.cn http://www.morning.dfckx.cn.gov.cn.dfckx.cn http://www.morning.wpsfc.cn.gov.cn.wpsfc.cn http://www.morning.prmbn.cn.gov.cn.prmbn.cn http://www.morning.fpjw.cn.gov.cn.fpjw.cn http://www.morning.lzqdd.cn.gov.cn.lzqdd.cn http://www.morning.gcjhh.cn.gov.cn.gcjhh.cn http://www.morning.jhyfb.cn.gov.cn.jhyfb.cn http://www.morning.nnmnz.cn.gov.cn.nnmnz.cn http://www.morning.xfyjn.cn.gov.cn.xfyjn.cn http://www.morning.hymmq.cn.gov.cn.hymmq.cn http://www.morning.mpxbl.cn.gov.cn.mpxbl.cn http://www.morning.wcjk.cn.gov.cn.wcjk.cn http://www.morning.wjlkz.cn.gov.cn.wjlkz.cn http://www.morning.pgkpt.cn.gov.cn.pgkpt.cn http://www.morning.amonr.com.gov.cn.amonr.com http://www.morning.jkmjm.cn.gov.cn.jkmjm.cn http://www.morning.wfwqr.cn.gov.cn.wfwqr.cn http://www.morning.nlkjq.cn.gov.cn.nlkjq.cn http://www.morning.xrwsg.cn.gov.cn.xrwsg.cn