├── 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 | ![这里写图片描述](https://img-blog.csdn.net/20170820104536564?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcWljaGVuZzc3Nw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 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 | ![CAS算法图解](https://user-gold-cdn.xitu.io/2019/9/3/16cf74dbfb0b7acf?imageView2/0/w/1280/h/960/ignore-error/1) 75 | 76 | ![ABA问题图解](https://user-gold-cdn.xitu.io/2019/9/3/16cf74dc2240c253?imageView2/0/w/1280/h/960/ignore-error/1) 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 | ![img](https://upload-images.jianshu.io/upload_images/1281379-adfde0d80334c1f8.png?imageMogr2/auto-orient/strip|imageView2/2/w/538/format/webp) 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 | ![img](https://upload-images.jianshu.io/upload_images/1281379-2db1deb0115ec4f2.png?imageMogr2/auto-orient/strip|imageView2/2/w/319/format/webp) 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 | ![img](https://upload-images.jianshu.io/upload_images/1281379-2575b81bbab6b67b.png?imageMogr2/auto-orient/strip|imageView2/2/w/437/format/webp) 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 | ![server和client之间的基本工作原理流程图](https://img-blog.csdn.net/20180415211654300?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2dpdGh1Yl8zOTY1NTAyOQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) 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 | ![高响应比优先调度算法2.png](https://wiki.jsswsq.com/images/3/38/%E9%AB%98%E5%93%8D%E5%BA%94%E6%AF%94%E4%BC%98%E5%85%88%E8%B0%83%E5%BA%A6%E7%AE%97%E6%B3%952.png) 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 | ![直接相联地址转换](https://img-blog.csdn.net/20171224112640770?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTF9OYW4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 373 | 374 | - 缺点:替换操作频繁,命中率比较低。 375 | 376 | ##### 全相联映射 377 | 378 | - 规则: 379 | 380 | - 主存的任意一块可以映射到Cache中的任意一块 381 | 382 | - 具体: 383 | 384 | - 主存与缓存分成相同大小的数据块。 385 | - 主存的某一数据块可以装入缓存的任意一块空间中。 386 | - 如果Cache的块数为C,主存的块数为M,则映象关系共有C×M种。 387 | 388 | ![全相联地址转换](https://img-blog.csdn.net/20171224112328778?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTF9OYW4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 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 | ![architecture](https://static.oschina.net/uploads/img/201803/07163541_gEDQ.png) 402 | 403 | ![这里写图片描述](https://img-blog.csdn.net/20180410092739206?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvRHlsYW5fRnJhbms=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 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 |
√: 可能出现
10 |
×: 不会出现
11 | 12 | | | 脏读 | 不可重复读 | 幻读 | 13 | | :------: | :--: | :--------: | :--: | 14 | | 读未提交 | √ | √ | √ | 15 | | 读提交 | × | √ | √ | 16 | | 重复读 | × | × | √ | 17 | | 序列化 | × | × | × | 18 | 19 | #### 隔离级别 20 | 21 | ##### 未提交读 Read uncommitted 22 | 23 | 不允许同时写 24 | 25 | - 允许脏读, 不允许更新丢失 26 | - 不可同时写, 但写时允许读 27 | - 通过"排他写锁"实现 28 | 29 | ##### 提交读 Read committed 30 | 31 | 写时不允许读写 32 | 33 | - 不允许脏读, 允许不可重复读 34 | - 未提交的写事务禁止其他事务访问对应数据, 读事务时允许其他事务访问 35 | - 通过"瞬间共享锁"和"排他写锁"实现 36 | 37 | ##### 重复读 Repeatable read 38 | 39 | 只能同时读 40 | 41 | - 不允许不可重复读和脏读, 可能出现幻读 42 | - 读取数据时禁止写事务, 写时禁止其他任何事务 43 | 44 | ##### 串行化 Serializable 45 | 46 | 任何情况的读写都不能同时进行 47 | 48 | #### 解决的问题 49 | 50 | ##### 脏读 51 | 52 | 事务A正在访问并修改数据,尚未将修改结果提交给数据库;事务B也访问并使用了原始数据 53 | 54 | ##### 不可重复读 55 | 56 | 一个事务多次读取同一记录,得到的结果不一致. 比如事务A两次读取同一文档, 在两次读取之间文档被其他事物修改, 导致两次读取不一致. 57 | 58 | ##### 幻读 59 | 60 | 事务A对表中数据做了修改, 涉及到表中全部数据行. 同时, 事务B修改表中数据, 这种修改是向表中插入一行新数据. 那么, 事务A用户就会发现表中还有没被修改过的数据行, 就像出现幻觉一样. 61 | 62 | 幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行 63 | 64 | > 幻读,并不是说两次读取获取的结果集不同,幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。 65 | > 66 | > 更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读。 67 | > 68 | > 幻读错误的理解:说幻读是 事务A 执行两次 select 操作得到不同的数据集,即 select 1 得到 10 条记录,select 2 得到 11 条记录。这其实并不是幻读,这是不可重复读的一种,只会在 R-U R-C 级别下出现,而在 mysql 默认的 RR 隔离级别是不会出现的。 69 | 70 | 71 | 72 | Q: 可重复读级别下,两个事务并发读取一个 i=1,并且i++,最终结果是什么 73 | 74 | 75 | 76 | ### 悲观锁、乐观锁 77 | 78 | 79 | 80 | ### 唯一索引、主键、二者区别 81 | 82 | ##### 唯一索引 83 | 84 | - 唯一索引不允许两行具有相同的索引值。 85 | - 如果现有数据中存在重复的键值,则大多数数据库都不允许将新创建的唯一索引与表一起保存。当新数据将使表中的键值重复时,数据库也拒绝接受此数据。例如,如果在employee 表中的职员姓氏(lname) 列上创建了唯一索引,则所有职员不能同姓。 86 | 87 | ##### 主键索引 88 | 89 | - 主键既是约数也是索引。 90 | - 主键索引是唯一索引的特殊类型。 91 | - 数据库表通常有一列或列组合,其值用来唯一标识表中的每一行。该列称为表的主键。 92 | - 在数据库关系图中为表定义一个主键将自动创建主键索引,主键索引是唯一索引的特殊类型。主键索引要求主键中的每个值是唯一的。当在查询中使用主键索引时,它还允许快速访问数据。 93 | 94 | ##### 二者区别 95 | 96 | - 对于主健/unique constraint , oracle/sql server/mysql等都会自动建立唯一索引; 97 | - 主键不一定只包含一个字段,所以如果你在主键的其中一个字段建唯一索引还是必要的; 98 | - 主健可作外健,唯一索引不可; 99 | - 主健不可为空,唯一索引可; 100 | - 主健也可是多个字段的组合; 101 | - 主键与唯一索引不同的是: 102 | - 有not null属性; 103 | - 每个表只能有一个。 104 | 105 | 106 | 107 | ### 数据库引擎 108 | 109 | 110 | 111 | ### 数据库引擎innoDB和MyISAM区别 112 | 113 | 1. InnoDB 支持**事务**,MyISAM 不支持事务。这是 MySQL 将默认存储引擎从 MyISAM 变成 InnoDB 的重要原因之一; 114 | 2. InnoDB 支持**外键**,而 MyISAM 不支持。对一个包含外键的 InnoDB 表转为 MYISAM 会失败; 115 | 3. InnoDB 是**聚集索引**,MyISAM 是非聚集索引。 116 | - 聚簇索引的文件存放在主键索引的叶子节点上,因此 InnoDB 必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。 117 | - MyISAM 是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。 118 | 4. InnoDB 不**保存表的具体行数**,执行 select count(*) from table 时需要全表扫描。而MyISAM 用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快; 119 | 5. InnoDB **最小锁粒度**是行锁,MyISAM 最小的锁粒度是表锁。一个更新语句会锁住整张表,导致其他查询和更新都会被阻塞,因此并发访问受限。这也是 MySQL 将默认存储引擎从 MyISAM 变成 InnoDB 的重要原因之一; 120 | 121 | >**如何选择:** 122 | > 123 | >1. 是否要支持事务,如果要请选择 InnoDB,如果不需要可以考虑 MyISAM; 124 | >2. 如果表中绝大多数都只是读查询,可以考虑 MyISAM,如果写也挺频繁,请使用InnoDB。 125 | >3. 系统奔溃后,MyISAM恢复起来更困难,能否接受,不能接受就选 InnoDB; 126 | >4. MySQL5.5版本开始Innodb已经成为Mysql的默认引擎(之前是MyISAM),说明其优势是有目共睹的。如果你不知道用什么存储引擎,那就用InnoDB,至少不会差。 127 | 128 | 129 | 130 | ### MVCC的实现 131 | ### 索引原理 132 | ### innodb主键自增为什么 有什么好处 133 | 134 | 135 | 136 | 137 | ### 事务 ACID 138 | 139 | - **A** : 事务的**原子性**(Atomicity) 140 | 141 | - 一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被[回滚](https://zh.wikipedia.org/wiki/回滚_(数据管理))(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即,事务不可分割、不可约简。 142 | 143 | - **C **: 事务的**一致性**(Consistency) 144 | 145 | - 在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设[约束](https://zh.wikipedia.org/wiki/数据完整性)、[触发器](https://zh.wikipedia.org/wiki/触发器_(数据库))、[级联回滚](https://zh.wikipedia.org/wiki/级联回滚)等。 146 | - 指事务的运行并不改变数据库中数据的一致性. 比如A转100元给B,A扣减100,B加上100,在事务开始前和事务完成之后都能保证他们的帐是对上的,那么这就是事务一致性。但是在事务过程中有可能会出现A扣减了100元,B没有加上100元的情况,这就是不一致。 147 | 148 | > 事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。如果事务成功地完成,那么系统中所有变化将正确地应用,系统处于有效状态。如果在事务中出现错误,那么系统中的所有变化将自动地回滚,系统返回到原始状态。 149 | 150 | - **I** : **独立性**(Isolation) 151 | 152 | - 数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。 153 | - 事务隔离分为不同级别,包括未提交读(Read uncommitted)、提交读(read committed)、可重复读(repeatable read)和串行化(Serializable)。 154 | 155 | - **D** : **持久性**(Durability) 156 | 157 | - 事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失. 158 | 159 | 160 | 161 | ### 聚簇索引和普通索引 162 | 163 | ##### 聚簇索引 164 | 165 | - 聚簇索引的叶子节点就是数据节点 166 | - innoDB按聚簇索引的形式存储数据 167 | - 优缺点: 168 | - 优点: 169 | 1. 当需要取出一定范围内的数据时,用聚簇索引比用非聚簇索引好。 170 | 2. 通过聚簇索引查找目标数据时理论上比非聚簇索引要快,因为非聚簇索引定位到对应主键时还要多一次目标记录寻址,即多一次I/O。 171 | 3. 使用覆盖索引扫描的查询可以直接使用页节点中的主键值。 172 | - 缺点: 173 | 1. **插入速度严重依赖于插入顺序**,按照主键的顺序插入是最快的方式,否则将会出现页分裂,严重影响性能。因此,对于InnoDB表,我们一般都会定义一个自增的ID列为主键。 174 | 2. **更新主键的代价很高,因为将会导致被更新的行移动**。因此,对于InnoDB表,我们一般定义主键为不可更新。 175 | 3. **二级索引访问需要两次索引查找,第一次找到主键值,第二次根据主键值找到行数据。**二级索引的叶节点存储的是主键值,而不是行指针(非聚簇索引存储的是指针或者说是地址),这是为了减少当出现行移动或数据页分裂时二级索引的维护工作,但会让二级索引占用更多的空间。 176 | 4. **采用聚簇索引插入新值比采用非聚簇索引插入新值的速度要慢很多**,因为插入要保证主键不能重复,判断主键不能重复,采用的方式在不同的索引下面会有很大的性能差距,聚簇索引遍历所有的叶子节点,非聚簇索引也判断所有的叶子节点,但是聚簇索引的叶子节点除了带有主键还有记录值,记录的大小往往比主键要大的多。这样就会导致聚簇索引在判定新记录携带的主键是否重复时进行昂贵的I/O代价。 177 | 178 | ##### 非聚簇索引 179 | 180 | - 非聚簇索引的叶子节点仍然是索引节点,只不过有指向对应数据块的指针 181 | - MyISAM按非聚簇索引的形式存储数据 182 | - 因此,MYISAM引擎的索引文件(.MYI)和数据文件(.MYD)是相互独立的 183 | 184 | ![img](https://user-gold-cdn.xitu.io/2019/5/16/16ac10253b8748df?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 185 | 186 | 187 | 188 | ### 联合索引 189 | 190 | 191 | 192 | ### 数据库最左前缀匹配原则 193 | 194 | - 在mysql建立联合索引时会遵循最左前缀匹配的原则,即最左优先,在检索数据时从联合索引的最左边开始匹配 195 | 196 | - 最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。 197 | - =和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式 198 | 199 | 200 | 201 | ### B+树和hash哪个适合做索引 202 | 203 | ##### B+树 204 | 205 | B-Tree 索引需要从根节点到枝节点,最后才能访问到页节点这样多次的IO访问,遍历树的节点,因此查询、插入、修改、删除的平均时间复杂度是 O(lg(n))。 206 | 207 | ##### hash 208 | 209 | Hash 索引结构的特殊性,其检索效率非常高,索引的检索可以一次定位。因此查询、插入、修改、删除的平均时间复杂度是 O(1)。 210 | 211 | ##### 为什么选用B+树 212 | 213 | 对于一条单行查询的 SQL,确实哈希索引更快,应为只查询一条记录,哈希检索可以一次定位。 214 | 215 | 但是关系型数据库更多的需求像下面: 216 | 217 | - 分组:group by 218 | - 排序: order by 219 | - 比较: <、> 220 | - ...... 221 | 222 | 在这样的查询需求下,哈希型的索引的时间复杂度会变成O(n),而树型索引的有序特性,依然能够保持 O(log(n))的高效率。因此数据库选择树结构实现。 223 | 224 | 225 | 226 | 227 | 228 | 总结:后端岗貌似很喜欢对数据库层次进行疯狂追问,什么范式,事务貌似都是简单到不会问的题了 229 | 230 | 面试官问我硬件题,我都怀疑我面得是后端吗? -------------------------------------------------------------------------------- /Shopee面经整理/答案/算法.md: -------------------------------------------------------------------------------- 1 | #### 如何判断一个二叉树是否是二叉搜索树 2 | - 中序遍历 3 | 4 | 5 | 6 | #### 一副牌有52张牌,如何设计算法完成洗牌 7 | 8 | 9 | 10 | #### 从y个元素中如何获得前x个最小的元素 11 | 12 | 13 | 14 | #### 有1000个瓶子,其中有一瓶是毒药,现在有10只老鼠,老鼠喝掉毒药之后一星期后发病死亡,问最少需要多少个星期能判断出哪一瓶是毒药 15 | - 二分法 16 | - 0 ~ 1000遍二进制,10只老鼠对应每一位 17 | - n只老鼠在t周时间里能确定(t+1)^n瓶毒药 18 | 19 | 20 | 21 | #### 大文件找出排名前1000的数据 22 | 23 | 24 | 25 | #### 最长不重复子串(两个指针) 26 | 27 | 28 | 29 | #### 1000个数据,查找出现次数最多的k个数字 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Shopee面经整理/答案/计网.md: -------------------------------------------------------------------------------- 1 | ## 1. ping网站所用的协议 2 | 3 | 通过**DNS协议**,将ping后接的域名转换为IP地址。(DNS使用的传输层协议是**UDP**) 4 | 5 | 通过**ARP解析服务**,由IP地址解析出MAC地址,以在数据链路层传输。 6 | 7 | ping是为了测试另一主机是否可达,发送一份**ICMP(Internet控制报文协议)**回显请求给目标主机,等待ICMP回显应答。 8 | 9 | - 跟踪路由的trancert命令也基于ICMP协议 10 | 11 | ![这里写图片描述](https://img-blog.csdn.net/20170822224933262?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvU2lsZW5jZU9P/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 12 | 13 | #### 详细过程 14 | 15 | ##### 查IP 16 | 17 | 1、主机查找本地系统Hosts文件的DNS缓存,如果存在该域名对应的IP,则获取IP,跳转到第8步;如果不存在,则继续。 18 | 19 | 2、主机向本网络路由器发起请求,查找路由DNS缓存,如果存在该域名对于的IP,则获取IP,跳转到第8步;如果不存在,则继续。 20 | 21 | 3、路由器向本地ISP(互联网提供商)的DNS服务器发起请求,查找DNS服务器的缓存,如果存在该域名对应的IP,则跳转到第7步;如果不存在,则继续。 22 | 23 | 4、本地DNS服务器向根域名服务器发起请求,根域名服务器告诉本地服务器,下一次应查询的顶级域名服务器dns.com的IP地址。 24 | 25 | 5、本地域名服务器向顶级域名服务器dns.com进行查询,顶级域名服务器dns.com告诉本地域名服务器,下一步应查询的权限服务器dns.abc.com的IP地址。 26 | 27 | 6、本地域名服务器向权限域名服务器dns.abc.com进行查询,权限域名服务器dns.abc.com告诉本地域名服务器,所查询的主机的IP地址。 28 | 29 | 7、本地域名服务器最后把查询结果——该域名对应的IP地址告诉给主机。 30 | 31 | 8、至此,主机知道了该域名的IP地址。 32 | 33 | ---------------------------------------------------------------------------------------- 34 | 35 | ##### 查MAC 36 | 37 | 9、主机通过子网掩码判断该IP地址是本网段还是跨网段,由于本网段比较简单,我们以跨网段进行讲解。 38 | 39 | 10、主机先查看本地ARP高速缓存,查看表中是否有本网络路由器(网关)的MAC地址,如果有,则获取MAC地址,跳转到第12步;如果没有,则继续。 40 | 41 | 11、主机使用ARP解析协议获取到本网段路由的MAC地址。 42 | 43 | 12、至此,主机知道本网络一个路由的MAC地址。 44 | 45 | ---------------------------------------------------------------------------------------- 46 | 47 | ##### ping 48 | 49 | 13、主机将ICMP报文封装成IP数据报,IP数据报的源地址为主机的IP地址,目的地址是域名对应的IP地址; 50 | 51 | 14、主机将IP数据报封装成MAC帧,MAC帧的源地址为主机的MAC地址,目的地址是路由器的MAC地址; 52 | 53 | 12、路由器接收到ICMP报文之后,发现MAC帧的目的地址是自己,IP地址是主机想要访问的IP地址,则将MAC帧的源地址改为自己的MAC地址,目的地址改为本网段另一个路由的MAC地址(也要通过ARP协议获取),转发下去... 54 | 55 | 13、直到最后一个路由根据ARP协议,找到了主机想要访问的IP地址对应的主机的MAC地址,然后将ICMP报文封装成MAC帧发送给该域名主机。 56 | 57 | 14、由于ARP协议具有相互学习性,域名主机接收到主机发送的ICMP回送请求报文之后,将向本网络路由发送ICMP回送回答报文,该路由又会转发下去... 58 | 59 | 15、当主机收到域名主机发送的ICMP回送回答报文之后,这样就表明该主机到域名主机是连通可达的。 60 | 61 | 62 | 63 | ## 2. 访问URL全过程 64 | 65 | ##### DNS解析 66 | 67 | ##### TCP连接 68 | 69 | ##### 发送http请求 70 | 71 | ##### 服务器处理请求并返回报文 72 | 73 | ##### 浏览器解析渲染页面 74 | 75 | ##### 断开连接:TCP四次挥手 76 | 77 | 78 | 79 | ## 3. TCP三次握手 80 | 81 | - 在 socket 编程中,客户端执行 `connect()` 时,将触发三次握手。 82 | 83 | ![three-way-handshake](https://raw.githubusercontent.com/HIT-Alibaba/interview/master/img/tcp-connection-made-three-way-handshake.png) 84 | 85 | ##### 第一次握手 86 | 87 | - 客户端 → 服务器,发送连接请求报文 88 | - SYN = 1, seq = x 89 | - 发送完毕后,客户端进入 `SYN_SEND` 状态。 90 | 91 | ##### 第二次握手 92 | 93 | - 服务器 → 客户端,发送确认报文 94 | - SYN = 1, ACK = 1, seq = y, ack = x + 1 95 | - 发送完毕后,服务器端进入 `SYN_RCVD` 状态。 96 | 97 | ##### 第三次握手 98 | 99 | - 客户端 → 服务器,发送确认报文 100 | - SYN = 0, ACK = 1, ack = y + 1 101 | - 发送完毕后,客户端进入 `ESTABLISHED` 状态,当服务器端接收到这个包时,也进入 `ESTABLISHED` 状态,TCP 握手结束。 102 | 103 | #### 为什么不能缩减为两次握手 104 | 105 | - 因为有连接请求报文失效的情况 106 | - 比如Client发送了一个请求报文,由于网络问题滞留在中间的某个网络节点,直到很久之后才到达Server,这是一个失效报文,但Server收到后仍然会回复确认报文。如果采用两次握手,Server确认后会一直等待Client发来数据,但Client并不会理会Server 107 | 108 | 109 | 110 | ## 4. TCP四次挥手 111 | 112 | - 客户端和服务器端均可主动发起挥手 113 | - socket编程中,任何一方执行`close()`操作即可触发挥手操作 114 | 115 | ![four-way-handshake](https://raw.githubusercontent.com/HIT-Alibaba/interview/master/img/tcp-connection-closed-four-way-handshake.png) 116 | 117 | ##### 第一次挥手 118 | 119 | - FIN = 1, seq = x 120 | - 假设客户端想要关闭连接,客户端发送一个 FIN 标志位置为1的包,表示自己已经没有数据可以发送了,但是仍然可以接受数据。 121 | - 发送完毕后,客户端进入 `FIN_WAIT_1` 状态。 122 | 123 | ##### 第二次挥手 124 | 125 | - ACK = 1, ack = x + 1 126 | - 服务器端确认客户端的 FIN 包,发送一个确认包,表明自己接受到了客户端关闭连接的请求,但还没有准备好关闭连接。 127 | - 发送完毕后,服务器端进入 `CLOSE_WAIT` 状态,客户端接收到这个确认包之后,进入 `FIN_WAIT_2` 状态,等待服务器端关闭连接。 128 | 129 | ##### 第三次挥手 130 | 131 | - FIN = 1, seq = y 132 | - 服务器端准备好关闭连接时,向客户端发送结束连接请求,FIN 置为1。 133 | - 发送完毕后,服务器端进入 `LAST_ACK` 状态,等待来自客户端的最后一个ACK。 134 | 135 | ##### 第四次挥手 136 | 137 | - ACK = 1, ack = y + 1 138 | - 客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 `TIME_WAIT`状态,等待可能出现的要求重传的 ACK 包。 139 | - 服务器端接收到这个确认包之后,关闭连接,进入 `CLOSED` 状态。 140 | - 客户端等待了某个固定时间(两个最大段生命周期,2MSL,2 Maximum Segment Lifetime)之后,没有收到服务器端的 ACK ,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入 `CLOSED` 状态。 141 | 142 | ### 为什么需要四次挥手 143 | 144 | 因为TCP是全双工模式。主机1发送FIN报文段,只表示主机1没有数据需要发送,相当于要求关闭单方数据通道;而主机2发送FIN报文段,才表示主机2也没有数据需要发送,可以关闭双方通道。 145 | 146 | 147 | 148 | ## 5. TCP的TIME_WAIT状态 149 | 150 | ![image-20200402152323309](C:\Users\84040\AppData\Roaming\Typora\typora-user-images\image-20200402152323309.png) 151 | 152 | 客户端“等待2MSL”期间即处于TIME_WAIT状态 153 | 154 | #### TIME_WAIT状态 155 | 156 | - TCP连接中,调用close()发起主动关闭的一方,在发送最后一个ACK之后会进入time_wait的状态,也就说该**发送方会保持2MSL时间之后才会回到初始状态**。MSL值得是数据包在网络中的最大生存时间。 157 | - 后果:在2MSL连接等待期间,本TCP连接定义的四元组(客户端IP地址和端口,服务端IP地址和端口号)不能被使用。 158 | 159 | #### TIME_WAIT状态的目的 160 | 161 | 1. 保证TCP全双工连接可靠释放 162 | 163 | 假设主动关闭连接的一方(假设是客户端)最后发送的ACK报文在网络中丢失,服务器会重发FIN报文。在重发的FIN报文到达客户端之前,客户端必须维护连接状态,才能正常进行重传等工作。 164 | 165 | 如果没有TIME_WAIT状态,那么当服务器重发的FIN报文到达时,客户端会用RST报文相应,这会让服务器认为出现了异常。 166 | 167 | 2. 使旧的数据包在网络中因过期而消失 168 | 169 | TCP连接由 (local_IP, local_port, remote_IP, remote port) 四元组唯一标识,现在假设TIME_WAIT状态不存在的情况: 170 | 171 | 客户端主动关闭连接后,未经TIME_WAIT状态,直接重新和服务器再次建立一个新TCP连接,这个新连接的四元组和旧连接相同。如果旧连接的报文阻塞在网络中,直到建立新连接后才到达服务器,由于新旧TCP连接四元组相同,服务器会认为这个旧连接的报文是当前需要的报文,而将其向上传递给应用层,这就会造成数据错乱等问题,破坏TCP的可靠性。 172 | 173 | > 释放的端口可能会重连刚断开的服务器端口,这样依然存活在网络里的老的TCP报文可能与新TCP连接报文冲突,造成数据冲突 174 | 175 | #### TIME_WAIT状态可能造成的问题 176 | 177 | 高并发短连接的情况下,会有大量socket处于TIME_WAIT状态。 178 | 179 | - 高并发:短时间内同时占用大量端口 180 | - 短连接:业务处理+数据传输时间远小于TIME_WAIT时间 181 | 182 | 持续高并发短连接,会导致服务器因资源不足拒绝服务。 183 | 184 | ##### 解决办法 185 | 186 | - SO_REUSEADDR能避免TIME_WAIT状态,让端口释放后立刻就可以被再次使用 187 | - 让短连接变为长连接 188 | 189 | 190 | 191 | ## 6. 端口号 192 | 193 | #### 公认端口(0 ~ 1023) 194 | 195 | - FTP : 21 196 | - TELNET : 23 197 | - SMTP : 25 198 | - DNS : 53 199 | - TFTP : 69 200 | - HTTP : 80 201 | - SNMP : 161 202 | 203 | #### 注册端口(1024 ~ 49151) 204 | 205 | #### 动态(临时)端口(49152~65535) 206 | 207 | 208 | 209 | ## 7. TCP和UDP 210 | 211 | ### TCP 212 | 213 | - TCP 提供一种**面向连接的、可靠的**字节流服务 214 | - 在一个 TCP 连接中,仅有两方进行彼此通信。广播和多播不能用于 TCP 215 | - **可靠传输**:校验和,确认和重传机制 216 | - **有序性/非重复**:给数据分节进行排序,累积确认 217 | - **流量控制**:滑动窗口 218 | - **流量控制指点对点通信量,作用于接收者,防止发送速度太快导致分组丢失** 219 | - 接收方维护接收窗口 220 | - 发送方维护拥塞窗口和发送窗口 221 | - 发送窗口 = min(接收窗口,拥塞窗口) 222 | - 接收方返回的 ACK 中会包含自己接收窗口的大小,并且利用大小来控制发送方的数据发送。 223 | - **拥塞控制**:动态改变窗口大小 224 | - **拥塞控制是一个全局性的过程,作用于网络,防止网络负载过大导致拥塞** 225 | - 慢开始算法(cwndssthresh):线性增长 227 | - 出现网络拥塞时(未收到确认): 228 | - ssthresh = (1/2)cwnd 229 | - cwnd = 1 230 | - 快重传: 231 | - 接收方在收到失序报文后立刻发出重复确认(而非等到自己发送数据时捎带确认) 232 | - 发送方收到三个重复确认就立即重传对方未收到的报文(而不必等待重传计时器到期) 233 | - 快恢复: 234 | - 连续收到三个重复确认:ssthresh = (1/2)ssthresh 235 | - 因为能收到重复确认,所以认为网络没有拥塞,此时令cwnd = ssthresh,执行拥塞避免算法 236 | 237 | 238 | 239 | ### UDP 240 | 241 | - UDP 缺乏可靠性。UDP 本身不提供确认,序列号,超时重传等机制。UDP 数据报可能在网络中被复制,被重新排序。即 UDP 不保证数据报会到达其最终目的地,也不保证各个数据报的先后顺序,也不保证每个数据报只到达一次 242 | - UDP 数据报是有长度的。每个 UDP 数据报都有长度,如果一个数据报正确地到达目的地,那么该数据报的长度将随数据一起传递给接收方。而 TCP 是一个字节流协议,没有任何(协议上的)记录边界。 243 | - UDP 是无连接的。UDP 客户和服务器之前不必存在长期的关系。UDP 发送数据报之前也不需要经过握手创建连接的过程。 244 | - UDP 支持多播和广播。 245 | 246 | 247 | 248 | ## 8. HTTP状态码 249 | 250 | - 200 - 请求成功 251 | - 301 - 资源(网页等)被永久转移到其它URL 252 | - 404 - 请求的资源(网页等)不存在 253 | - 500 - 内部服务器错误 254 | 255 | 256 | 257 | ## cookie和session的区别? 258 | 259 | 追问:session是如何识别用户的?(emm,我说了session id,面试官又追问id存在哪儿) 260 | session id就是依靠cookie,在cookie中的 261 | 262 | 263 | 264 | ## 说一下https有什么加密方式(对称、非对称,只答了非对称),其如何传输公钥保证公钥不被截获 265 | 266 | -------------------------------------------------------------------------------- /Shopee面经整理/答案/面经1.md: -------------------------------------------------------------------------------- 1 | **一面** 2 | 3 | 1. 数据结构: 4 | - 比较数组和链表 5 | - 什么是平衡二叉树 6 | - 编程:实现反转链表 7 | 2. 数据库: 8 | - MySQL复合索引 9 | - MySQL引擎MyISAM和InnoDB有什么区别 10 | 3. 操作系统: 11 | - Linux的Kill命令(-9信号的作用) 12 | - kill命令是通过向进程发送指定的信号来结束相应进程的 13 | - Linux的进程间的通信 14 | 4. 网络: 15 | - TCP的四次挥手 16 | - TCP四次挥手中的TIME_WAIT状态 17 | - TCP和UDP的优缺点比较 18 | 5. 算法: 19 | - 编程:快速排序 20 | 21 | 22 | 23 | **二面** 24 | 25 | 1. 问到了笔试题的第三题: 26 | 复习笔试题有必要。 27 | 2. 问了TCP3次握手的过程,为什么要3次? 28 | 3. 针对TCP3次握手怎么攻击?(这个没答出来) 29 | - SYN攻击和DDOS攻击原理 30 | 4. TCP的传输过程是怎么样的?怎么确保有序? 31 | 有点生疏,但还是答出来了7788 32 | 5. 悲观锁和乐观锁?在项目中有用到吗? 33 | 6. 在学习的过程中有做什么有趣的东西吗? 34 | 我把这个问题理解为介绍项目,然后就开始介绍项目。这里答得不是很好,这种天马行空的问题,可以尽情拓展,我答的比较少。 35 | 7. 为什么喜欢软件开发? 36 | 8. 为什么选择shopee? 37 | 38 | ### -------------------------------------------------------------------------------- /Shopee面经整理/答案/面经2.md: -------------------------------------------------------------------------------- 1 | ### mysql事务的隔离级别有了解吗,每个级别分别有解决了什么问题? 2 | 3 | ### 乐观锁&悲观锁,乐观锁与悲观锁的实现方式? 4 | 5 | ### 索引的实现方式有哪些?(B+树索引、位图索引、哈希索引) 6 | 7 | ### 讲一下B+树的实现原理,为什么要用B+树? 8 | 9 | ### Hashmap了解吗?红黑树讲一讲,为什么用红黑树? 10 | 11 | ### 缓存有用过哪一些?radis了解吗? 12 | 13 | ### 进程与线程的区别? 14 | 15 | ### 了解协程吗? 16 | 17 | ### 进程之间的通信方式? 18 | 19 | ### 线程之间的同步方式 20 | 21 | 22 | 23 | ### 进程调度算法(6个) 24 | 25 | 先来先服务、短进程有限、时间片轮转、多级反馈队列、优先级调度算法、高相应比优先调度算法 26 | 27 | #### 先来先服务 28 | 29 | - 每次调度都是**从就绪队列中选择一个最先进入该队列的进程**,为之分配CPU,使之投入运行。该进程一直运行到完成或发生某事件而阻塞后才放弃处理机。 30 | - 优缺点: 31 | - 优点:公平,实现简单 32 | - 缺点:平均等待时间长,不利于短作业 33 | 34 | #### 短进程优先 35 | 36 | - **从就绪队列中选出一个估计运行时间最短的进程**,将处理机分配给它,使它立即执行并一直执行到完成,或发生某事件而被阻塞放弃处理机时再重新调度。 37 | - 优缺点: 38 | - 优点:平均等待时间短 39 | - 缺点:长进程饥饿 40 | 41 | #### 时间片轮转 42 | 43 | - 系统将所有的就绪进程**按先来先服务**的原则排成一个队列,每次调度时,把CPU分配给**队首进程**,并令其执行一个时间片。时间片的大小从几ms到几百ms。 44 | - 当执行的时间片用完时,由一个计时器发出**时钟中断请求**,调度程序便据此信号来停止该进程的执行,并将它送往**就绪队列的末尾**;然后,再把处理机分配给就绪队列中新的队首进程,同时也让它执行一个时间片。 45 | - 这样就可以保证就绪队列中的所有进程在一给定的时间内均**能获得一时间片的处理机执行时间**。换言之,系统能在给定的时间内响应所有用户的请求。 46 | - 优缺点: 47 | - 优点:兼顾长短作业 48 | - 缺点:平均等待时间较长,上下文切换较费时。适用于分时系统。 49 | 50 | #### 多级反馈队列 51 | 52 | - 设置多个就绪队列,并为各个队列赋予不同的优先级。第一个队列的优先级最高,第二个队列次之,其余各队列的优先权逐个降低。该算法赋予各个队列中进程执行时间片的大小也各不相同,在优先权愈高的队列中,为每个进程所规定的执行时间片就愈小。例如,第二个队列的时间片要比第一个队列的时间片长一倍,第i+1个队列的时间片要比第i个队列的时间片长一倍。 53 | - 当一个新进程进入内存后,首先将它放入第一队列的末尾,按FCFS原则排队等待调度。当轮到该进程执行时,如它能在该时间片内完成,便可准备撤离系统;如果它在一个时间片结束时尚未完成,调度程序便将该进程转入第二队列的末尾,再同样地按FCFS原则等待调度执行;如果它在第二队列中运行一个时间片后仍未完成,再依次将它放入第三队列,……,如此下去,当一个长作业(进程)从第一队列依次降到第n队列后,在第n队列便采取按时间片轮转的方式运行。 54 | - 仅当第一队列空闲时,调度程序才调度第二队列中的进程运行;仅当第1~(i-1)队列均空时,才会调度第i队列中的进程运行。如果处理机正在第i队列中为某进程服务时,又有新进程进入优先权较高的队列(第1~(i-1)中的任何一个队列),则此时新进程将抢占正在运行进程的处理机,即第i队列中某个正在运行的进程的时间片用完后,由调度程序选择优先权较高的队列中的那一个进程,把处理机分配给它。 55 | - 优缺点: 56 | - 优点:是兼顾长短作业,有较好的响应时间。 57 | - 缺点:不断有新进程到来时,则长进程可能饥饿。 58 | 59 | #### 优先级调度算法 60 | 61 | ##### 非抢占式优先级调度算法 62 | 63 | - 在这种方式下,系统一旦把处理机**分配给就绪队列中优先权最高的进程后,该进程便一直执行下去,直至完成**;或因发生某事件使该进程放弃处理机时,系统方可再将处理机重新分配给另一优先权最高的进程。 64 | - 这种调度算法主要用于批处理系统中;也可用于某些对实时性要求不高的实时系统中。 65 | 66 | ##### 抢占式优先级调度算法 67 | 68 | - 在这种方式下,系统同样是把处理机**分配给优先权最高的进程**,使之执行。但在其执行期间,只要出现了另一个其优先级更高的进程,进程调度程序就立即停止当前进程的执行,**重新将处理机分配给新到的优先权最高的进程**。 69 | - 因此,在采用这种调度算法时,是每当系统中出现一个新的就绪进程i时,就将其优先级Pi与正在执行的进程j的优先级Pj进行比较。如果Pi≤Pj,原进程Pj便继续执行;但如果是Pi>Pj,则立即停止Pj的执行,做进程切换,使i进程投入执行。 70 | - 抢占式优先级调度算法能更好地满足紧迫作业的要求,故而常用于要求比较严格的实时系统中,以及对性能要求较高的批处理和分时系统中。 71 | 72 | #### 高相应比优先调度算法 73 | 74 | - 为每个进程设置动态优先权,并使作业的优先权随着等待时间的增加而提高。保证长作业在等待一定的时间后,必然有机会分配到处理机。 75 | 76 | - 优先权公式: 77 | 78 | ![高响应比优先调度算法2.png](https://wiki.jsswsq.com/images/3/38/%E9%AB%98%E5%93%8D%E5%BA%94%E6%AF%94%E4%BC%98%E5%85%88%E8%B0%83%E5%BA%A6%E7%AE%97%E6%B3%952.png) 79 | 80 | - 算法过程 81 | 82 | - 设置一个就绪队列; 83 | - 若就绪队列中作业数多于1个且当前有作业使用CPU,则按照优先权公式,对照计算就绪队列中每个作业的优先权,否则直接执行作业; 84 | - 按照优先权数值排序,优先权数值越高,则作业越先被执行; 85 | - 等待当前作业使用完CPU,按照步骤3计算好的队列顺序执行作业; 86 | - 重复步骤3、4直到所有作业执行完毕。 87 | 88 | 89 | 90 | ### 什么叫死锁?死锁的必要条件?如何处理死锁问题? 91 | 92 | ### 计算机网络七层结构?每一层主要做什么?http、tcp分别属于哪一层? 93 | 94 | ### http的状态码有哪些? 95 | 96 | ### get与vpost的区别? 97 | 98 | ### http与https的区别?https的加密方式? 99 | 100 | ### tcp三次握手?四次挥手? 101 | 102 | ### 消息队列有用过吗? 103 | 104 | ### 有做过什么项目?做了什么? 105 | 106 | ### 有什么想要问我的吗? -------------------------------------------------------------------------------- /Shopee面经整理/算法or智力题.md: -------------------------------------------------------------------------------- 1 | 1. 如何判断一个二叉树是否是二叉搜索树 2 | 2. 一副牌有52张牌,如何设计算法完成洗牌 3 | 3. 从y个元素中如何获得前x个最小的元素 4 | 4. 有1000个瓶子,其中有一瓶是毒药,现在有10只老鼠,老鼠喝掉毒药之后一星期后发病死亡,问最少需要多少个星期能判断出哪一瓶是毒药 5 | 5. 大文件找出排名前1000的数据 6 | 6. 最长不重复子串(两个指针) 7 | 7. 1000个数据,查找出现次数最多的k个数字 8 | 9 | -------------------------------------------------------------------------------- /Shopee面经整理/计网.md: -------------------------------------------------------------------------------- 1 | 1. ping网站所用的协议 2 | 3 | 2. TCP三次握手 4 | 5 | 3. TCP的TIME_WAITE状态 6 | 7 | 4. TCP和UDP 8 | 9 | 5. TCP相比于UDP在什么方面保证了其可靠性 10 | 11 | 6. TCP如何保证传输的有序性,可靠性? 12 | 13 | 7. TCP四次挥手 14 | 15 | 8. cookie和session的区别?追问:session是如何识别用户的?(emm,我说了session id,面试官又追问id存在哪儿) 16 | 17 | session id就是依靠cookie,在cookie中的 18 | 19 | 9. 说一下https有什么加密方式(对称、非对称,只答了非对称),其如何传输公钥保证公钥不被截获 20 | 21 | -------------------------------------------------------------------------------- /Shopee面经整理/面经1.md: -------------------------------------------------------------------------------- 1 | 1. 数据结构: 2 | - 比较数组和链表 3 | - 什么是平衡二叉树 4 | - 编程:实现反转链表 5 | 6 | 2. 数据库: 7 | - MySQL复合索引 8 | - MySQL引擎MyISAM和InnoDB有什么区别 9 | 10 | 3. 操作系统: 11 | - Linux的Kill命令(-9信号的作用) 12 | - Linux的进程间的通信 13 | 14 | 4. 网络: 15 | - TCP的四次挥手 16 | - TCP四次挥手中的TIME_WAIT状态 17 | - TCP和UDP的优缺点比较 18 | 19 | 5. 算法: 20 | - 编程:快速排序 -------------------------------------------------------------------------------- /Shopee面经整理/面经2.md: -------------------------------------------------------------------------------- 1 | 1. mysql事务的隔离级别有了解吗,每个级别分别有解决了什问题? 2 | 2. 乐观锁&悲观锁,乐观锁与悲观锁的实现方式? 3 | 3. 索引的实现方式有哪些?(B+树索引、位图索引、哈希索引) 4 | 4. 讲一下B+树的实现原理,为什么要用B+树? 5 | 5. Hashmap了解吗?红黑树讲一讲,为什么用红黑树? 6 | 6. 缓存有用过哪一些?radis了解吗? 7 | 7. 进程与线程的区别? 8 | 8. 了解协程吗? 9 | 9. 进程之间的通信方式? 10 | 10. 线程之间的同步方式?(锁,信号量) 11 | 11. 进程调度算法? 12 | 12. 什么叫死锁?死锁的必要条件?如何处理死锁问题? 13 | 13. 计算机网络七层结构?每一层主要做什么?http、tcp分别属于哪一层? 14 | 14. http的状态码有哪些? 15 | 15. get与post的区别? 16 | 16. http与https的区别?https的加密方式? 17 | 17. tcp三次握手?四次挥手? 18 | 18. 消息队列有用过吗? 19 | 19. 有做过什么项目?做了什么? 20 | 20. 有什么想要问我的吗? --------------------------------------------------------------------------------