├── README.md └── Shopee面经整理 ├── Linux.md ├── Shopee二面面经整理 ├── 基础知识.md ├── 智力题.md └── 算法.md ├── 其他.md ├── 操作系统.md ├── 数据库.md ├── 答案 ├── Linux.md ├── 其他.md ├── 操作系统.md ├── 数据库.md ├── 算法.md ├── 计网.md ├── 面经1.md └── 面经2.md ├── 算法or智力题.md ├── 计网.md ├── 面经1.md └── 面经2.md /README.md: -------------------------------------------------------------------------------- 1 | # Shopee Interview (BE) 2 | 3 | 2020年春招Shopee后端开发岗位(深圳)笔/面试经验个人纪录。 4 | 5 | 笔面试使用C++为编程语言,下述笔面经中均不涉及Java和Python等语言的基础知识。 6 | 7 | ## 笔试 8 | 9 | 笔试时间03.28,牛客网考试。三道编程题,均为Leetcode中等难度原题,考试过程中可以切本地IDE。 10 | 11 | 三道题分别为: 12 | - [无重复字符的最长子串](https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/) 13 | - [全排列](https://leetcode-cn.com/problems/permutations/) 14 | - [可被三整除的最大和](https://leetcode-cn.com/problems/greatest-sum-divisible-by-three/) 15 | 16 | 17 | ## 面试 18 | 19 | 包括一面(04.03)、二面(04.10)和hr面(04.15) 20 | 21 | 22 | ### 一面 23 | 24 | [本人一面面经](https://www.nowcoder.com/discuss/399675) 25 | 26 | 准备面试时,我将牛客网上的一面面经进行了粗略分类整理,详见:[Shopee一面面经整理](Shopee面经整理/) 27 | 28 | 并利用网络资源给出了其中部分问题的答案,详见:[Shopee一面面经答案](Shopee面经整理/答案/) 29 | 30 | 31 | ### 二面 32 | 33 | [本人二面面经(基本无参考价值)](https://www.nowcoder.com/discuss/404912) 34 | 35 | 牛客网二面面经整理详见:[Shopee二面面经整理](Shopee面经整理/Shopee二面面经整理/) 36 | 37 | ### hr面 38 | 39 | 常规问题,包括: 40 | 41 | - 基本信息核对 42 | 43 | - 在校成绩 44 | 45 | - 手上有无offer,待遇如何 46 | 47 | - 期望薪资 48 | 49 | - 对工作城市、工作氛围的期望 50 | 51 | - 反问 52 | 53 | ### 最终结果 54 | 55 | 让我等了1个月之久,最后发了拒信,理由是项目经历不足(准备考研,没实习)。 56 | 57 | 自我感觉:一面面试官对我印象不错;二面中介绍毕设(NLP,比较复杂的一个模型)时语言混乱、逻辑不清,导致面试官对我印象欠佳。此外,二面面试官提到加班问题,我表示想去Shopee当然是因为不加班,面试官:也不是都不加班。后来我了解到他隶属金融部门,算是Shopee里加班最严重的部门之一(?),拒掉我这种抗拒加班的人也情有可原。 58 | 59 | 但无论如何,吊人一个月还是给人印象很差,以后不会再面这家了。 60 | -------------------------------------------------------------------------------- /Shopee面经整理/Linux.md: -------------------------------------------------------------------------------- 1 | 1. Linux上我怎么查看某端口被什么进程占用、如何修改文件的权限 2 | 2. -------------------------------------------------------------------------------- /Shopee面经整理/Shopee二面面经整理/基础知识.md: -------------------------------------------------------------------------------- 1 | 1. 等价类 2 | 3 | 2. 链表的作用 4 | 5 | 3. cd 6 | 7 | 4. 进程状态 8 | 9 | - 运行态,就绪态,阻塞态 10 | 11 | - 新建态,就绪态,运行态,阻塞态,终止态 12 | 13 |  14 | 15 | - 挂起(换到外存) 16 | 17 | - 挂起就绪 18 | 19 | 是指进程被对换到辅存时的就绪状态,是不能被直接调度的状态,只有当主存中没有活跃就绪态进程,或者是挂起就绪态进程具有更高的优先级,系统将把挂起就绪态进程调回主存并转换为活跃就绪。 20 | 21 | - 挂起阻塞 22 | 23 | 5. 单例模式 24 | 25 | 6. C++ static作用 26 | 27 | 7. 虚函数 28 | 29 | 8. http状态码 30 | 31 | 9. ACID特性 32 | 33 | 10. 从启动到写完一个C++代码,到执行,计算机做了什么 34 | 35 | 11. 事务的概念,有没有用过,怎么用的,为什么用 36 | 37 | 12. 介绍死锁,银行家算法 38 | 39 | 13. 介绍分治算法 40 | 41 | 14. 快排和堆排的原理和复杂度 42 | 43 | 15. linux操作系统awk(这个我说我用得少),那再随便说五个命令,都是干嘛的 44 | 45 | 16. 如何查看某个进程开启的socket(查看/proc/pid号/fd文件),如何查看tcp链接(没太交流明白,下来发现好像就是netstat?) 46 | 47 | 17. redis怎么用的 48 | 49 | 18. 纯将项目中的各种需求和实现和实际上线过程中的问题,如何改进,如果新的balabala需求如何设计和改进(20min) 50 | 51 | 19. 对分布式有哪些了解 52 | 53 | 20. TCP3次握手的过程,为什么要3次?针对TCP3次握手怎么攻击? 54 | 55 | 21. TCP的传输过程是怎么样的?怎么确保有序? 56 | 57 | 22. 悲观锁和乐观锁? 58 | 59 | - 悲观锁 60 | 61 | - 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(**共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程**)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。 62 | - 乐观锁 63 | 64 | - 总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。**乐观锁适用于多读的应用类型,这样可以提高吞吐量**,像数据库提供的类似于**write_condition机制**,其实都是提供的乐观锁。 65 | - **版本号机制**:一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。 66 | 67 | - **CAS算法**: Compare And Swap(比较与交换),是一种无锁算法,基于硬件原语实现,能够在不使用锁的情况下实现多线程之间的变量同步。 68 | 69 | - 可能出现的问题: 70 | 1. ABA问题,一个线程将内存值从A改为B,另一个线程又从B改回到A。(解决方法:在变量前面添加版本号,每次变量更新的时候都将版本号加1) 71 | 2. 循环时间长开销大:CAS算法需要不断地自旋来读取最新的内存值,长时间读取不到就会造成不必要的CPU开销。 72 | 3. 只能保证一个共享变量的原子操作 73 | 74 |  75 | 76 |  77 | 78 | 79 | 80 | 23. OSI七层,数据链路层传输单位 81 | 82 | 24. 数据链路层和数据传输层的校验方法的区别 83 | 84 | 25. 长连接和短连接及适用情况 85 | 86 | 26. python read/readline/readlines的区别 87 | 88 | 27. python怎么读数据库 89 | 90 | 28. 长连接和短连接及适用情况 91 | 92 | 29. https加密 93 | 94 | - http缺点123 95 | - SSL解决:通信加密(解决窃听),数字签名(解决数据篡改),数字证书(解决身份伪装) 96 | - https的通信过程 97 | 98 | 30. 4次挥手大量Time_wait的解决方法 99 | 100 | 31. 发现当前有很多处于last_ack状态的连接,是什么问题 101 | 102 | 32. 说说LVS调度算法,优缺点 103 | 104 | 33. 数据库如何在读时确保数据时最新的(说是在committed之前添点什么东西,我一直在回答隔离性,结果不对) 105 | 106 | - 个人认为第11题如果mysql是默认RR隔离级别,那考察了MVCC里的快照度和当前读,如果一定要读最新的话就select for update但是会block住,读已提交级别的快照会被别的提交动态更新,所以是最新的..串行化那肯定就不用说了,所以还是跟隔离级别有一些关系。 107 | 108 | 34. volatile适用场景 109 | 110 | - > volatile是轻量级的synchronized,但是volatile不会引起线程的上下文切换和调度。 111 | 112 | - volatile在多核处理器进行开发时保证了共享变量的可见性,即当一个线程修改这个变量时,其他线程能立马得到最新修改的值。 113 | 114 | - **volatile**变量可用于提供线程安全,但是只能应用于非常有限的一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。因此,单独使用 volatile 还不足以实现计数器、互斥锁或任何具有与多个变量相关的不变式(Invariants)的类(例如 “start <=end”)。 115 | 116 | 35. 常规的SQL多表连接查询、事务 117 | 118 | 36. 说一下MySQL你的理解 我从锁 索引 事物 三个方向讲了大概10-15分钟左右 119 | 120 | - **索引** 121 | - **锁** 122 | - **事务** 123 | - MySQL 事务主要用于处理操作量大,复杂度高的数据。 124 | - 事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。 125 | - 事务用来管理 insert,update,delete 语句 126 | - 事务必须满足ACID特性 127 | - 隔离级别 128 | - begin开启,rollback回滚,commit提交 129 | - 130 | 131 | 37. 你对网络方向有什么了解 132 | 133 | 38. 协程特点,对比线程 134 | 135 | - **进程**拥有自己独立的堆和栈,既不共享堆,亦不共享栈,进程由操作系统调度。 136 | 137 | **线程**拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度(标准线程是这样的)。 138 | 139 | **协程**和线程一样共享堆,不共享栈,协程由程序员在协程的代码里显示调度。 140 | 141 | 一个应用程序一般对应一个进程,一个进程一般有一个主线程,还有若干个辅助线程,线程之间是平行运行的,在线程里面可以开启协程,让程序在特定的时间内运行。 142 | 143 | - **协程是一种用户态的轻量级线程,**协程的调度完全由**用户**控制(线程是内核调度)。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。 144 | 145 | - 协程多与线程进行比较 146 | 147 | - 一个线程可以多个协程,一个进程也可以单独拥有多个协程,这样python中则能使用多核CPU。 148 | - 线程进程都是同步机制,而**协程则是异步** 149 | - 协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态 150 | 151 | - **协程和线程的区别**是:协程**避免了无意义的调度**,由此可以提高性能,但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力。 152 | 153 | 39. HTTPS加密具体细节SSL 154 | 155 | 40. 同步与异步 156 | 157 | - 同步需要等待(阻塞),异步无需等待(不阻塞) 158 | 159 | - **同步**:可以理解为在执行完一个函数或方法之后,一直等待系统返回值或消息,这时程序是出于阻塞的,只有接收到返回的值或消息后才往下执行其他的命令。 160 | - **同步**就是整个处理过程顺序执行,当各个过程都执行完毕,并返回结果。是一种线性执行的方式,执行的流程不能跨越。一般用于流程性比较强的程序,比如用户登录,需要对用户验证完成后才能登录系统。 161 | - **异步**:执行完函数或方法后,不必阻塞性地等待返回值或消息,只需要向系统委托一个异步过程,那么当系统接收到返回值或消息时,系统会自动触发委托的异步过程,从而完成一个完整的流程。 162 | - **异步**则是只是发送了调用的指令,调用者无需等待被调用的方法完全执行完毕;而是继续执行下面的流程。是一种并行处理的方式,不必等待一个程序执行完,可以执行其它的任务,比如页面数据加载过程,不需要等所有数据获取后再显示页面。 -------------------------------------------------------------------------------- /Shopee面经整理/Shopee二面面经整理/智力题.md: -------------------------------------------------------------------------------- 1 | 1. 100层楼扔玻璃球的问题 -------------------------------------------------------------------------------- /Shopee面经整理/Shopee二面面经整理/算法.md: -------------------------------------------------------------------------------- 1 | ### 常见数据结构/算法实现 2 | 3 | 1. 二叉搜索树(insert, delete等基本操作) 4 | 2. 二分查找 5 | 3. 三路排序 6 | 4. 拓扑排序 7 | 8 | 9 | 10 | ### 字符串 11 | 12 | 1. 26个字母组合 一共输出2的26次个字符串 13 | 2. 【字典序】4321 找比它小的字典序数字,那么是 4312,时间复杂度不能是 O(n2) 14 | 3. 完美洗牌 15 | 4. 接雨水,力扣42题 16 | - **左右指针** 17 | 5. 给个m,求1到m的最大奇约数的和。 18 | 6. 反转字符串 19 | 7. 字符串中大小写字母分成前后两部分,字母顺序不变 20 | - 写一个函数,将输入的字符串中大写字母移动到字符串的末尾,同时需要保留出现的相对顺序, 例如输入AaBbCc 返回 abcABC, 要求不使用额外内存 21 | 22 | 23 | 24 | ### 数组/链表 25 | 26 | 1. 一个数组a,给个s,返回一组在数组里差是s的两个数。 27 | 28 | - 我:排序+二分 29 | - 他:返回和怎么求,O(n)复杂度 30 | - 我:不会 31 | - **力扣原题,hashmap遍历,每次判断,cur - s或者cur + s是否在map里面** 32 | 33 | 2. 求两个数组的重复元素 34 | 35 | 3. 调整数组使得数组元素最多出现两次,不改变元素相对位置 36 | 37 | 4. (剑指offer)数组中只出现一次的数字 38 | 39 | 5. 数每个元素的比它小的元素的数量 40 | 41 | 输入[8,1,2,2,3] 42 | 43 | 输出[4,0,1,1,3] 44 | 45 | 解释:比8小的有1,2,2,3所以是4 46 | 47 | 比1小的有0个所以是0 48 | 49 | 答案:map 50 | 51 | 52 | 53 | ### 其他 54 | 55 | 1. 给定一组非负整数,重新排列它们的顺序使之组成一个最大的整数(如给定[3,30,34,5,9],输出9534330) 56 | 57 | 58 | 59 | ### 送分题 60 | 61 | 1. 三位数取出每一位 62 | 2. 实现斐波那契数列 -------------------------------------------------------------------------------- /Shopee面经整理/其他.md: -------------------------------------------------------------------------------- 1 | 1. 数组和链表的区别 2 | 2. 快排。快排的最好最差复杂度情况 3 | 3. 哈希表原理,常见碰撞算法(解决冲突的方法) 4 | 4. hashmap设计及存在相同key的时候的解决方法 5 | 5. 平衡二叉树 6 | 6. 如何判断一个二叉树是否是二叉搜索树 7 | 7. C++中delete a和delete[] a的区别 8 | 8. 有环链表的判断 和 环长度判断 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Shopee面经整理/操作系统.md: -------------------------------------------------------------------------------- 1 | 1. 进程和线程的区别 2 | 2. 进程通信 3 | 3. socket客户端和服务端通信过程 4 | 4. 事务隔离级别 5 | 5. 可重复读级别下,两个事务并发读取一个 i=1,并且i++,最终结果是什么 6 | 6. 内存置换算法有什么,说一下时钟置换算法 7 | 7. 说一下LRU过程 8 | 8. cpu内存你了解吗?(emm,我把jvm具体说了下,什么栈什么堆) 9 | 9. cpu缓存你知道吗?(我把cache的原理啥的说了一遍,面试官追问cache的缓存如何和外存的缓存保持一致性?我心想:我面得是后端吗?) 10 | - cpu缓存那道应该是先要判断cache,再更新主内存。MESI协议 11 | - cache缓存和外存保持一致是CAS总线锁定吗;总线锁被淘汰了,是MESI缓存一致性协议 12 | 10. 多线程volite关键字什么作用,追问:可见性你怎么理解的? -------------------------------------------------------------------------------- /Shopee面经整理/数据库.md: -------------------------------------------------------------------------------- 1 | 1. 唯一索引、主键、二者区别 2 | 2. 数据库引擎说 3 | 3. 数据库引擎innodb和myisam区别 4 | 4. MVCC的实现 5 | 5. 索引原理 6 | 6. 数据库隔离级别 脏读、幻读、不可重复读 7 | 7. innodb主键自增为什么 有什么好处 8 | 8. 事务 ACID 9 | 9. 聚族索引和普通索引 10 | 10. 联合索引 11 | 11. 数据库最左前缀匹配原则 12 | 12. B+树和hash哪个适合做索引 13 | 14 | 15 | 16 | 总结:后端岗貌似很喜欢对数据库层次进行疯狂追问,什么范式,事务貌似都是简单到不会问的题了 17 | 18 | 面试官问我硬件题,我都怀疑我面得是后端吗? -------------------------------------------------------------------------------- /Shopee面经整理/答案/Linux.md: -------------------------------------------------------------------------------- 1 | 1. Linux上怎么查看某端口被什么进程占用 2 | 3 | - ```shell 4 | lsof -i:端口号 5 | ``` 6 | 7 | - ls open files 8 | 9 | - ```shell 10 | netstat -tunpl | grep 端口号 11 | ``` 12 | 13 | 2. 如何修改文件的权限 14 | 15 | 3. -------------------------------------------------------------------------------- /Shopee面经整理/答案/其他.md: -------------------------------------------------------------------------------- 1 | ### 1 数组和链表的区别 2 | 3 | #### 1.1 数组 4 | 5 | - 内存连续, 定义时就需要预留空间 6 | - 插入删除为O(n), 效率低 7 | - 随机读取效率高, 直接根据索引读内存即可 8 | - 不利于扩展, 初始定义的空间不够时需要重新定义数组 9 | 10 | #### 1.2 链表 11 | 12 | - 内存不连续 13 | - 插入删除为O(1), 效率高 14 | - 随机读取/查找效率低, 不具有随机访问性, 必须按顺序找 15 | - 不指定大小, 扩展方便 16 | 17 | 18 | 19 | ### 2 快排 20 | 21 | #### 最好最差情况 22 | 23 | 24 | 25 | 1. 哈希表原理,常见碰撞算法(解决冲突的方法) 26 | - 碰撞算法: 27 | - 再散列法 28 | - 线性探测再散列 29 | - 二次探测再散列 30 | - 随即探测再散列 31 | - 再哈希法:使用第二个、第三个、……哈希函数再次计算地址,直到没有冲突 32 | - 拉链法 33 | - 设置公共溢出区 34 | 2. hashmap设计及存在相同key的时候的解决方法 35 | 3. 平衡二叉树 36 | 37 | 38 | 39 | ### 3 如何判断一个二叉树是否是二叉搜索树 40 | 41 | #### 3.1 二叉搜索树的定义 42 | 43 | - 节点的左子树中任意节点值小于根节点 44 | - 节点的右子树中任意节点值大于根节点 45 | - 左右子树都必须是二叉查找树,不允许存在重复节点。 46 | 47 | #### 3.2 判断二叉搜索树的算法 48 | 49 | ##### 利用中序遍历 50 | 51 | 对树进行中序遍历, 得到的数组若为升序, 则树为二叉搜索树 52 | 53 | (可以不用数组, 只保存前驱节点, 若前驱节点大于当前节点, 则返回假) 54 | 55 | 56 | 57 | ### 4 C++中delete a和delete[] a的区别 58 | 59 | - delete 回收单个对象内存空间 60 | - delete[] 回收一组对象内存空间, 分为两种情况 61 | - 为基本数据类型回收空间 62 | - 为自定义类型回收空间 63 | - 析构函数调用区别: 64 | - delete a释放a指针指向的全部内存空间, 但只调用a[0]对象的析构函数 65 | - delete[] a释放a指针指向的内存空间, 并逐一调用数组中每个对象的析构函数 66 | - 因此, 对于int/char/struct等简单数据类型, 由于对象没有析构函数, 二者没有差别 67 | - 但对于有析构函数的对象, 例如自定义类, delete a只能释放对象本身占用的存储空间, 而不调用逐一析构函数, 如果该类占用了操作系统资源, 如套接字文件线程等, 不析构会导致资源一直被占用, 无法释放 68 | 69 | 70 | 71 | ### 5 有环链表的判断 和 环长度判断 72 | 73 | 快慢指针 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /Shopee面经整理/答案/操作系统.md: -------------------------------------------------------------------------------- 1 | ## 1. 进程和线程的区别 2 | 3 | *进程是分配资源的基本单位,线程是CPU调度的基本单位,同一进程下的各个线程共享资源。* 4 | 5 | - 进程有独立的地址空间,一个进程如果在保护模式下崩溃,不会对其它进程产生影响。 6 | - 线程是一个进程中的不同执行路径。线程有自己的堆栈和局部变量(线程有独立的程序计数器、一组寄存器和栈),但线程之间没有单独的地址空间,一个线程崩溃,统一进程下所有线程都崩溃。(所以多进程程序比多线程程序更健壮) 7 | - 一个程序至少有一个进程,一个进程至少有一个线程 8 | - 线程的划分尺度小于进程,所以多线程程序并发性高 9 | - 同一进程下的多个线程共享内存(地址空间),从而极大地提高了程序运行效率 10 | - 每个线程都有独立的程序运行入口、顺序执行序列和程序出口,但线程不能独立执行,必须依赖进程 11 | - 从逻辑角度来看,多线程的意义在于在一个应用程序中,有多个执行部分时可以同时执行。但OS并不把多个线程看成独立应用。 12 | - 优缺点:线程开销小,进程方便资源管理和保护;进程可以跨机器迁移。 13 | - **进程切换和线程切换**: 14 | - 进程切换:① 切换页目录以使用新地址空间;② 切换内核栈和硬件上下文; 15 | - 线程切换不用切地址空间,也就是不用做① 16 | - 上下文切换通过OS内核完成,性能损耗主要来源于① 寄存器内容切出切入;② 切换后CPU原本的缓存作废,TLB(页表缓冲)等都被刷新,导致一段时间的内存访问十分低效(线程切换没有这个问题) 17 | - **内核线程、用户线程、轻量级线程** 18 | - **用户态、内核态** 19 | 20 | 21 | 22 | ## 2. 进程通信 (七种) 23 | 24 | https://www.jianshu.com/p/c1015f5ffa74 25 | 26 | 管道(匿名管道、有名管道)、共享内存、信号量、消息队列、信号、套接字 27 | 28 | #### 管道(匿名管道) 29 | 30 | - 半双工,数据只能沿一个方向流动;双方通信需要两个管道; 31 | - 只能用于父子/兄弟进程; 32 | - 数据读写类似队列,先进先出,写数据加在管道缓冲区的末尾,读数据从缓冲区头部读取; 33 | - 缓冲区大小有限(管道存在于内存中,管道创建时,内存为缓冲区分配一个页面大小); 34 | - 管道传送无格式字节流,读写双方必须事先约定数据格式,比如多少个字节算一个消息; 35 | 36 | #### 命名管道(FIFO) 37 | - 将路径名与管道关联,名字存在于文件系统中,管道中的内容存放在内存中; 38 | - 因为有名字,所以没有亲缘关系的进程也能通信,只要能访问名字路径即可; 39 | 40 | > **匿名管道和命名管道总结:** 41 | > (1)管道是特殊类型的文件,在满足先入先出的原则条件下可以进行读写,但不能进行定位读写。 42 | > (2)匿名管道是单向的,只能在有亲缘关系的进程间通信;命名管道以磁盘文件的方式存在,可以实现本机任意两个进程通信。 43 | > (3)**无名管道阻塞问题:**无名管道无需显示打开,创建时直接返回文件描述符,在读写时需要确定对方的存在,否则将退出。如果当前进程向无名管道的一端写数据,必须确定另一端有某一进程。如果写入无名管道的数据超过其最大值,写操作将阻塞,如果管道中没有数据,读操作将阻塞,如果管道发现另一端断开,将自动退出。 44 | > (4)**命名管道阻塞问题:**有名管道在打开时需要确实对方的存在,否则将阻塞。即以读方式打开某管道,在此之前必须一个进程以写方式打开管道,否则阻塞。此外,可以以读写(O_RDWR)模式打开有名管道,即当前进程读,当前进程写,不会阻塞。 45 | 46 | 使用管道需要注意: 47 | 48 | [使用管道需要注意的几个点](http://blog.lujun9972.win/blog/2018/04/28/使用管道要注意的几个点/index.html) 49 | 50 | [使用管道需要注意的四种特殊情况](https://blog.51cto.com/10706198/1763155) 51 | 52 | #### 共享内存 53 | 54 | - 多个进程直接读写同一块内存空间 55 | 56 | - 最快。**原因**:进程间通信不需要通过内核,只需要对共享内存区域操作即可 57 | 58 | - 内核留出一块内存区,需要共享内存的进程将其映射到自己的私有地址空间(同一内存区映射到共享它的不同进程的地址空间),该进程就可以直接读写这一块内存,无需数据拷贝 59 | 60 | - 多进程共享同一块内存,需要同步机制达到进程间的同步与互斥(如信号量)(也就是说和其他进程间通讯方式不同,共享内存需要用户自己实现同步) 61 | 62 |  63 | 64 | #### 信号量 65 | 66 | - 是一个计数器 67 | - 创建、等待、挂出信号量: 68 | - 创建:调用者指定信号量初值,对二值信号量来说为0或1 69 | - 等待(P操作):测试信号量的值,小于0就阻塞 70 | - 挂出(V操作):信号量值加1 71 | - P操作、V操作、信号量值加减应当是原子操作;因此,信号量通常在内核中实现 72 | 73 | #### 消息队列 74 | 75 | - 存放在**内核**中的消息链表,**有特定格式**,每个消息队列由**消息队列标识符**表示 76 | - 先进先出。但允许随机定位查询查询,消息不一定要以先进先出的次序读取,也可以按消息类型读取 77 | - 消息队列存放在内核中,只有在内核重启(即OS重启)或显式删除一个消息队列时,消息队列才会真正删除 78 | - 无名管道:只存在于内存 79 | - 命名管道:存在于实际的磁盘介质或者文件系统 80 | - 与管道不同的是,消息队列在某个进程往一个队列写入消息之前,并不需要另外某个进程在该队列上等待消息的到达 81 | - 允许一个或多个进程写入与读取消息 82 | - 消息队列克服了信号承载信息量少,管道只能承载无格式字 节流以及缓冲区大小受限等缺点 83 | 84 | #### 信号 85 | 86 | - 信号是软件层次上对中断机制的一种模拟,是一种**异步**通信方式,信号可以在用户进程和内核之间直接交互,内核可以利用信号来通知用户空间的进程发生了哪些系统事件,信号事件主要有两个来源: 87 | - 硬件来源:用户按键输入`Ctrl+C`退出、硬件异常如无效的存储访问等; 88 | - 软件终止:终止进程信号、其他进程调用kill函数、软件异常产生信号; 89 | - 信号可以在任何时候发给某一进程,而无需知道该进程的状态 90 | - 如果该进程当前并未处于执行状态,则该信号由内核保存起来,直到该进程恢复执行并传递给它为止 91 | - **信号生命周期和处理流程** 92 | - 信号被某个进程产生,并设置此信号传递的对象(一般为对应进程的pid),然后传递给操作系统; 93 | - 操作系统根据接收进程的设置(是否阻塞)而选择性的发送给接收者 94 | - 如果接收者阻塞该信号(且该信号是可以阻塞的),操作系统将暂时保留该信号,而不传递,直到该进程解除了对此信号的阻塞(如果对应进程已经退出,则丢弃此信号) 95 | - 如果对应进程没有阻塞,操作系统将传递此信号; 96 | - 目的进程接收到此信号后,将根据当前进程对此信号设置的预处理方式,暂时终止当前代码的执行,保护上下文(主要包括临时寄存器数据,当前程序位置以及当前CPU的状态)、转而执行中断服务程序,执行完成后在回复到中断的位置。当然,对于抢占式内核,在中断返回时还将引发新的调度。 97 | 98 | #### 套接字 99 | 100 | - 套接字是支持TCP/IP的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。 101 | 102 | - 可以实现跨机器进程通讯 103 | 104 |  105 | 106 | - 套接字特性由三个属性决定:域、端口号、协议类型 107 | 108 | - 域:指定套接字通信中使用的网络介质。 109 | 110 | - **AF_INET**指Internet网络 111 | - **AF_UNIX**指UNIX文件系统,它就是文件输入/输出,地址是文件名 112 | 113 | - 端口号: 114 | 115 | - 每一个基于TCP/IP网络通讯的程序(进程)都被赋予了唯一的端口和端口号,端口是一个信息缓冲区,用于保留Socket中的输入/输出信息,端口号是一个16位无符号整数,范围是0-65535,以区别主机上的每一个程序(端口号就像房屋中的房间号),低于256的端口号保留给标准应用程序,比如pop3的端口号就是110,每一个套接字都组合进了IP地址、端口,这样形成的整体就可以区别每一个套接字。 116 | 117 | - 协议类型: 118 | 119 | - 流套接字:可靠、有序、面向连接,使用TCP 120 | - 数据报套接字:不可靠、无序、无连接,使用UDP 121 | - 原始套接字:允许直接访问底层协议IP或ICMP,常用于网络监听,能够对网络传输机制进行控制 122 | 123 | > 1. 流套接字只能读取TCP协议的数据 124 | > 2. 数据报套接字只能读取UDP协议的数据 125 | > 3. 原始套接字可以读写内核没有处理的IP数据包 126 | > 127 | > 因此,如果要访问其他协议发送数据必须使用原始套接字。 128 | 129 | - 套接字通信的建立 130 | 131 | 132 | 133 | ## 3. socket客户端和服务端通信过程 134 | 135 |  136 | 137 | #### 服务器端 138 | (1)首先服务器应用程序用**系统调用socket**来创建一个套接字,它是系统分配给该服务器进程的类似文件描述符的资源,它不能与其他的进程共享。 139 | (2)然后,服务器进程会给套接字起个名字,我们使用**系统调用bind**来给套接字命名。然后服务器进程就开始等待客户连接到这个套接字。 140 | (3)接下来,**系统调用listen**来创建一个队列并将其用于存放来自客户的进入连接。 141 | (4)最后,服务器通过**系统调用accept**来接受客户的连接。它会创建一个与原有的命名套接不同的新套接字,这个套接字只用于与这个特定客户端进行通信,而命名套接字(即原先的套接字)则被保留下来继续处理来自其他客户的连接(建立客户端和服务端的用于通信的流,进行通信)。 142 | 143 | #### 客户端 144 | (1)客户应用程序首先**调用socket**来创建一个未命名的套接字,然后将服务器的命名套接字作为一个地址来**调用connect**与服务器建立连接。 145 | (2)一旦连接建立,我们就可以像使用底层的文件描述符那样用套接字来实现双向数据的通信(通过流进行数据传输)。 146 | 147 | #### 简略步骤 148 | 149 | ##### 客户端步骤 150 | 1. 创建套接字 151 | 2. 向服务器发送连接请求(connect) 152 | 3. 通信(send/recv) 153 | 4. 关闭套接字 154 | 155 | ##### 服务器端步骤 156 | 157 | 1. 创建用于监听的套接字(socket) 158 | 2. 将套接字绑定到本地地址和端口上(bind) 159 | 3. 将套接字设为监听模式(listen) 160 | 4. 等待客户请求(accept),此处要不断的调用accept 161 | 5. 通信(send/receive),完成后返回4 162 | 6. 关闭套接字(close socket) 163 | 164 |  165 | 166 | #### http通信和socket通信的差异 167 | 168 | - http连接使用“**请求-响应方式**”,即在请求时建立连接通道,当客户端向服务器发送请求后,服务端才能向客户端返回数据 169 | - socket通信则是在双方建立连接后,可以直接进行数据传输,在连接时可以实现信息的**主动推送**,无需每次都由客户端向服务器发送请求。 170 | 171 | 172 | 173 | ## 4. 线程通信 174 | 175 | - 线程通信的目的主要是线程同步,所以线程没有像进程通信中的用于数据交换的通信机制。 176 | 177 | #### 线程通信方式 (三种) 178 | 179 | ##### 锁机制 180 | 181 | - 包括: 互斥锁、读写锁、条件变量 182 | - 互斥锁提供了以排他方式防止数据结构被并发修改的方法。 183 | - 读写锁允许多个线程同时读共享数据,而对写操作是互斥的。 184 | - 条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。 185 | 186 | ##### 信号量机制 187 | 188 | - 包括无名线程信号量和命名线程信号量 189 | 190 | ##### 信号机制 191 | 192 | - 类似进程间的信号处理 193 | 194 | 195 | 196 | ## 5. 进程调度算法(6个) 197 | 198 | 先来先服务、短进程有限、时间片轮转、多级反馈队列、优先级调度算法、高相应比优先调度算法 199 | 200 | #### 先来先服务 201 | 202 | - 每次调度都是**从就绪队列中选择一个最先进入该队列的进程**,为之分配CPU,使之投入运行。该进程一直运行到完成或发生某事件而阻塞后才放弃处理机。 203 | - 优缺点: 204 | - 优点:公平,实现简单 205 | - 缺点:平均等待时间长,不利于短作业 206 | 207 | #### 短进程优先 208 | 209 | - **从就绪队列中选出一个估计运行时间最短的进程**,将处理机分配给它,使它立即执行并一直执行到完成,或发生某事件而被阻塞放弃处理机时再重新调度。 210 | - 优缺点: 211 | - 优点:平均等待时间短 212 | - 缺点:长进程饥饿 213 | 214 | #### 时间片轮转 215 | 216 | - 系统将所有的就绪进程**按先来先服务**的原则排成一个队列,每次调度时,把CPU分配给**队首进程**,并令其执行一个时间片。时间片的大小从几ms到几百ms。 217 | - 当执行的时间片用完时,由一个计时器发出**时钟中断请求**,调度程序便据此信号来停止该进程的执行,并将它送往**就绪队列的末尾**;然后,再把处理机分配给就绪队列中新的队首进程,同时也让它执行一个时间片。 218 | - 这样就可以保证就绪队列中的所有进程在一给定的时间内均**能获得一时间片的处理机执行时间**。换言之,系统能在给定的时间内响应所有用户的请求。 219 | - 优缺点: 220 | - 优点:兼顾长短作业 221 | - 缺点:平均等待时间较长,上下文切换较费时。适用于分时系统。 222 | 223 | #### 多级反馈队列 224 | 225 | - 设置多个就绪队列,并为各个队列赋予不同的优先级。第一个队列的优先级最高,第二个队列次之,其余各队列的优先权逐个降低。该算法赋予各个队列中进程执行时间片的大小也各不相同,在优先权愈高的队列中,为每个进程所规定的执行时间片就愈小。例如,第二个队列的时间片要比第一个队列的时间片长一倍,第i+1个队列的时间片要比第i个队列的时间片长一倍。 226 | - 当一个新进程进入内存后,首先将它放入第一队列的末尾,按FCFS原则排队等待调度。当轮到该进程执行时,如它能在该时间片内完成,便可准备撤离系统;如果它在一个时间片结束时尚未完成,调度程序便将该进程转入第二队列的末尾,再同样地按FCFS原则等待调度执行;如果它在第二队列中运行一个时间片后仍未完成,再依次将它放入第三队列,……,如此下去,当一个长作业(进程)从第一队列依次降到第n队列后,在第n队列便采取按时间片轮转的方式运行。 227 | - 仅当第一队列空闲时,调度程序才调度第二队列中的进程运行;仅当第1~(i-1)队列均空时,才会调度第i队列中的进程运行。如果处理机正在第i队列中为某进程服务时,又有新进程进入优先权较高的队列(第1~(i-1)中的任何一个队列),则此时新进程将抢占正在运行进程的处理机,即第i队列中某个正在运行的进程的时间片用完后,由调度程序选择优先权较高的队列中的那一个进程,把处理机分配给它。 228 | - 优缺点: 229 | - 优点:是兼顾长短作业,有较好的响应时间。 230 | - 缺点:不断有新进程到来时,则长进程可能饥饿。 231 | 232 | #### 优先级调度算法 233 | 234 | ##### 非抢占式优先级调度算法 235 | 236 | - 在这种方式下,系统一旦把处理机**分配给就绪队列中优先权最高的进程后,该进程便一直执行下去,直至完成**;或因发生某事件使该进程放弃处理机时,系统方可再将处理机重新分配给另一优先权最高的进程。 237 | - 这种调度算法主要用于批处理系统中;也可用于某些对实时性要求不高的实时系统中。 238 | 239 | ##### 抢占式优先级调度算法 240 | 241 | - 在这种方式下,系统同样是把处理机**分配给优先权最高的进程**,使之执行。但在其执行期间,只要出现了另一个其优先级更高的进程,进程调度程序就立即停止当前进程的执行,**重新将处理机分配给新到的优先权最高的进程**。 242 | - 因此,在采用这种调度算法时,是每当系统中出现一个新的就绪进程i时,就将其优先级Pi与正在执行的进程j的优先级Pj进行比较。如果Pi≤Pj,原进程Pj便继续执行;但如果是Pi>Pj,则立即停止Pj的执行,做进程切换,使i进程投入执行。 243 | - 抢占式优先级调度算法能更好地满足紧迫作业的要求,故而常用于要求比较严格的实时系统中,以及对性能要求较高的批处理和分时系统中。 244 | 245 | #### 高相应比优先调度算法 246 | 247 | - 为每个进程设置动态优先权,并使作业的优先权随着等待时间的增加而提高。保证长作业在等待一定的时间后,必然有机会分配到处理机。 248 | 249 | - 优先权公式: 250 | 251 |  252 | 253 | - 算法过程 254 | 255 | - 设置一个就绪队列; 256 | - 若就绪队列中作业数多于1个且当前有作业使用CPU,则按照优先权公式,对照计算就绪队列中每个作业的优先权,否则直接执行作业; 257 | - 按照优先权数值排序,优先权数值越高,则作业越先被执行; 258 | - 等待当前作业使用完CPU,按照步骤3计算好的队列顺序执行作业; 259 | - 重复步骤3、4直到所有作业执行完毕。 260 | 261 | 262 | 263 | ## 6. 内存置换算法(6个) 264 | 265 | - 局部置换算法 (物理页面数固定) 266 | 267 | 为每个进程分配一组固定数量的物理块,进程运行时不再改变。若发生缺页,从分配的N个页面中选出一页换出,调入下一页。 268 | 269 | - OPT (最佳替换算法) 270 | - LRU (最近最久未使用) 271 | - FIFO (先进先出) 272 | - Clock (时钟置换算法) 273 | - Enhanced Clock (改进的时钟算法) (二次机会法) 274 | - LFU (最不常用算法) 275 | 276 | - 全局置换算法 (逻辑页面数固定) 277 | 278 | 进程运行时,根据情况增加或减少物理块。发生缺页时,从空闲物理块取出一块分配给该进程,仅当所有物理块都用完时,OS才从内存中选择一页调出。 279 | 280 | - 工作集置换算法 281 | - 缺页率置换算法 282 | 283 | #### OPT 284 | 285 | - 选择**未来预计访问时间最远的页面**进行置换 286 | 287 | #### FIFO 288 | 289 | - 选择**在内存中驻留时间最长的页面**进行置换 290 | - **链表**实现。 291 | - 系统维护一个链表,记录了所有位于内存当中的逻辑页面。从链表的排列顺序来看,链首页面的驻留时间最长,链尾页面的驻留时间最短。当发生一个缺页中断时,把链表首页面置换掉,并把新的页面添加到链表的末尾。 292 | - 性能较差,调出的页面有可能是经常要访问的页面,并且有Belady现象。FIFO算法很少单独使用。 293 | 294 | #### LRU 295 | 296 | - 选择**最久未使用的页面**进行置换 297 | - 它是对最优页面置换算法的一个近似,其依据是程序的**局部性原理**,即在最近一小段时间内,如果某些页面被频繁的访问,那么在将来的近一小段时间,它们还可能被频繁访问。反过来说,如果在过去某些页面长时间未被访问,那么在将来它们还可能会长时间的得不到访问。 298 | - LRU算法需要**记录各个页面使用时间的先后顺序**,开销比较大。两种可能实现的方法: 299 | - 系统维护一个**页面链表**,最近刚刚使用过的页面作为首结点,最久未使用的页面作为尾结点。每一次访问内存时,找到相应的页面,把它从链表中摘下来,再移动到链表之首。每次缺页中断发生时,淘汰链表末尾的页面。 300 | - 设置一个**活动页面栈**,当访问某页时,将此页号压入栈顶,然后考察栈内是否有与此页面相同的页号,若有则抽出。当需要淘汰一个页面时,总是选择栈底的页面,它就是最久未使用的。 301 | 302 | #### CLOCK 303 | 304 | - 开销和性能都介于FIFO和LRU之间,是LRU的近似。对FIFO的一种改进。 305 | - 思路: 306 | - 需要用到页表项当中的访问位,当一个页面被装入内存时,把该位初始化为0。然后如果这个页面被访问(读/写),则把该位置为1。 307 | - 把各个页面组织成环形链表(类似时钟表面),把指针指向最老的页面(最先进来)。 308 | - 当发生一个缺页中断时,考察指针所指向的最老页面。若它的访问位为0,立即淘汰;若访问位为1,则把该位置为0,然后指针向下移动一格。如此下去,直到找到被淘汰的页面,然后把指针向下移动一格。 309 | - 实现:维持一个环形页面链表保存在内存中。 310 | 311 | #### Enhanced Clock 312 | 313 | - 在Clock基础上区分读和写,写为11、读为10 314 | - 指针修改顺序: 11 -> 10 -> 00,遇到00就置换 315 | - 修改Clock算法,使它允许脏页总是在一次时钟头扫描中保留下来,同时使用**脏位和使用位**来指导置换。 316 | 317 | #### LFU 318 | 319 | - 选择**累计访问次数最少的页面**进行置换 320 | 321 | - 实现方法:对每个页面设置一个访问计数器。每当一个页面被访问时,该页面的访问计数器加一。在发生缺页中断时,淘汰计数值最小的那个页面。 322 | 323 | - LRU考察的是**多久未访问**,时间越短越好; 324 | 325 | LFU考察的是访问的**次数或频度**,访问次数越多越好。 326 | 327 | - 问题:一个页面在进程开始时使用的最多,但以后就不使用了,实现费时费力。 328 | 329 | 解决:定期把次数寄存器右移一位。 330 | 331 | #### Belady现象 332 | 333 | - **Belady现象**:在采用FIFO等算法时,有时会出现分配的物理页面数增加,缺页率反而提高的异常现象。 334 | - Belady现象的原因:FIFO算法的置换特征与进程访问内存的动态特征是矛盾的。与置换算法的目标是不一致的(即替换较少使用的页面),因此被它置换出去的页面并不一定是进程不会访问的。 335 | 336 | #### LRU和FIFO比较 337 | 338 | - LRU算法和FIFO本质上都是先进先出的思路。 339 | - 只不过LRU是针对页面的最近访问时间来进行排序,所以需要在每一次页面访问的时候动态的调整各个页面之间的先后顺序(有一个页面的最近访问时间变了); 340 | - 而FIFO是针对页面进入内存的时间来进行排序。这个时间是固定不变的,所以各页面之间的先后顺序是固定的。如果一个页面在进入内存之后没有被访问,那么它的最近访问时间就是它进入内存的时间。 341 | - 换句话说,如果内存当中的所有页面都未曾访问过,那么LRU就退化为FIFO算法。 342 | 343 | [局部/ 全局页面置换算法](https://blog.csdn.net/weixin_45926367/article/details/104946605) 344 | 345 | 346 | 347 | ## 7. CPU缓存 348 | 349 | #### CPU缓存(cache) 350 | 351 | - 老CPU有两级缓存L1和L2,新CPU有三级缓存L1、L2和L3 352 | - L1 cache分为Instruction Cache(指令缓存)和Data Cache(数据缓存) 353 | - 对于多核CPU,L1和L2在每个CPU核中,L3由多CPU核共享 354 | - 越接近CPU的cache速度越快,L1快于L2快于L3 355 | 356 | #### cache命中 357 | 358 | cache比内存小很多,将内存数据加载到cache中需要决策放在哪一块,这就是cache的映射问题,我们的目标是使用合理的映射手段提高cache命中率。映射的单位为cache line(块)。 359 | 360 | ##### 直接映射 361 | 362 | - 规则: 363 | 364 | - 主存中的一块映射到cache中一个固定的块内 365 | 366 | - 具体: 367 | 368 | - 主存与cache分成相同大小的数据块。 369 | - 主存容量应是cache容量的整数倍,将主存空间按cache容量分区,区大小 = cache大小。 370 | - 主存中某区的一块存入缓存时只能存入缓存中块号相同的位置。 371 | 372 |  373 | 374 | - 缺点:替换操作频繁,命中率比较低。 375 | 376 | ##### 全相联映射 377 | 378 | - 规则: 379 | 380 | - 主存的任意一块可以映射到Cache中的任意一块 381 | 382 | - 具体: 383 | 384 | - 主存与缓存分成相同大小的数据块。 385 | - 主存的某一数据块可以装入缓存的任意一块空间中。 386 | - 如果Cache的块数为C,主存的块数为M,则映象关系共有C×M种。 387 | 388 |  389 | 390 | - 缺点:访问相关存储器时,每次都要与全部内容比较,速度低,成本高,因而应用少。 391 | 392 | ##### 组相联映射 393 | 394 | - 主存和cache都分组,主存一个组内块数和cache分组数相同,组间直接映射,组内全相联映射 395 | - 即:主存放到cache的哪个组是固定的,存到该组的哪一块是随机的 396 | - 实际: 397 | - 常采用的组相联结构Cache,每组内有2、4、8、16块,称为2路、4路、8路、16路组相联Cache 398 | 399 | #### CPU访存完整过程 400 | 401 |  402 | 403 |  404 | 405 | 406 | 407 | ## 8. 缓存一致性 408 | 409 | - https://coolshell.cn/articles/20793.html#coolshell-3 410 | 411 | ### 8.1 Cache写方式 412 | 413 | Cache写操作方式有两种: 414 | 415 | - **Write through(写通)**: 416 | - 每当CPU更新cache内容,就立即更新到内存 417 | - 优点:高一致性 418 | - 缺点:每次CPU写数据,都会导致总线事务,会经常引起总线竞争,效率低下 419 | - **Write back(写回)**: 420 | - CPU修改cache数据后不会立即更新内存,而是等到合适时机再更新 421 | 422 | 为了保持缓存一致性(多核CPU的核内cache数据一致),CPU又提供了两个操作: 423 | 424 | - **Write invalidate(写失效)**: 425 | - 当一个CPU修改了cache数据,如果其他CPU有该数据,则通知其为无效 426 | - **Write Update(写更新)**: 427 | - 当一个CPU修改了数据,如果其他CPU有该数据,则通知其更新数据 428 | 429 | 写更新会导致大量的更新操作,因此在MESI协议中,采取的是写失效(即MESI中的I:ivalid,如果采用的是写更新,那么就不是MESI协议了,而是MESU协议)。 430 | 431 | ### 8.2 MESI协议 432 | 433 | - 单核Cache中每个Cache line有2个标志:dirty和valid标志,它们很好的描述了Cache和Memory(内存)之间的数据关系(数据是否有效,数据是否被修改) 434 | 435 | - 在多核处理器中,多个核会共享一些数据,**MESI协议**就包含了描述共享的状态。 436 | 437 | #### MESI协议状态 438 | 439 | 在MESI协议中,每个Cache line有4个状态,可用2个bit表示,它们分别是: 440 | 441 | - **M(Modified)**:当前CPU cache拥有最新数据(最新的cache line),其他CPU拥有失效数据(cache line的状态是invalid),虽然当前CPU中的数据和主存是不一致的,但是以当前CPU的数据为准; 442 | - **E(Exclusive)**:只有当前CPU中有数据,其他CPU中没有改数据,当前CPU的数据和主存中的数据是一致的; 443 | - **S(Shared)**:当前CPU和其他CPU中都有共同数据,并且和主存中的数据一致; 444 | - **I(Invalid)**:当前CPU中的数据失效,数据应该从主存中获取,其他CPU中可能有数据也可能无数据,当前CPU中的数据和主存被认为是不一致的; 445 | 446 | #### MESI cache操作 447 | 448 | MESI协议中,每个cache的控制器不仅知道自己的操作(local read和local write),每个核心的缓存控制器通过监听也知道其他CPU中cache的操作(remote read和remote write),进而再确定自己cache中共享数据的状态是否需要调整。 449 | 450 | Cache操作有四种: 451 | 452 | - local read(LR):读本CPU cache中的数据; 453 | - local write(LW):将数据写到本CPU cache; 454 | - remote read(RR):其他CPU cache发生read; 455 | - remote write(RW):其他CPU cache发生write; 456 | 457 | #### MESI状态转换 458 | 459 | 460 | 461 | 462 | 463 | ## 多线程volite关键字什么作用,追问:可见性你怎么理解的? -------------------------------------------------------------------------------- /Shopee面经整理/答案/数据库.md: -------------------------------------------------------------------------------- 1 | ### 事务隔离级别 2 | 3 | - 4个隔离级别 4 | - Read uncommitted 读未提交 5 | - Read committed 读提交 6 | - Repeatable read 重复读 (MySQL默认隔离级别) 7 | - Serializable 序列化 8 | 9 |