八股复习:计网、计操
计网
一、HTTP
1. 浏览器键入网址全过程
浏览器首先解析 URL,判断是域名还是 IP;然后进行 DNS 域名解析,获取服务器 IP 地址;接着操作系统发起 TCP 三次握手建立连接;如果是 HTTPS 还会进行 TLS 握手协商加密套件;连接建立后浏览器构造 HTTP 请求报文发送给服务器;服务器接收请求、处理业务逻辑、查询数据库后返回 HTTP 响应报文;浏览器接收响应,解析 HTML、CSS、JS 并渲染页面;最后完成 TCP 四次挥手断开连接。整个过程涉及应用层、传输层、网络层、数据链路层协同工作。
记忆:DNS → TCP → HTTP请求 → 响应渲染 → 断开
2. DNS解析的流程
DNS 解析采用递归查询 + 迭代查询结合的方式。浏览器先查自身缓存,没有再查操作系统缓存(hosts 文件),再查本地 DNS 服务器;本地 DNS 服务器如果没有缓存,会向根 DNS 服务器查询,根服务器返回顶级域名服务器(如 .com)地址;本地 DNS 再向顶级域名服务器查询,得到权威域名服务器地址;最后向权威服务器查询,得到目标 IP 地址并返回给浏览器,同时各级都会缓存结果以加速下次访问。
记忆:浏览器→本地→根→顶级→权威,递归+迭代
3. HTTP是无状态的吗?为什么
HTTP 是无状态协议。因为协议本身不会保存任何客户端的信息,每一次请求都是独立的,服务器无法识别多个请求是否来自同一个用户。这种设计让服务器更轻量、更容易横向扩展,但无法直接支持登录、购物车等需要身份识别的功能,因此需要通过 Cookie、Session、Token 等机制在应用层维护状态。
记忆:无记忆、轻量、易扩展,靠 Cookie/Session 存状态
4. HTTP1.1 / 2.0 / 3.0 区别
HTTP1.1 是文本协议,串行请求、队头阻塞,一个连接同一时间只能处理一个请求,支持长连接、缓存、分块传输。HTTP2.0 是二进制分帧、多路复用,一个连接可并行多个请求,解决队头阻塞,还支持头部压缩(HPACK)、服务器推送。HTTP3.0 基于 QUIC 协议(UDP),彻底解决队头阻塞,连接建立更快(0-RTT/1-RTT),支持连接迁移,安全性更高,弱网环境表现更好。
记忆:1.1队头堵;2.0多路复用;3.0 QUIC/UDP
5. HTTP与HTTPS
HTTP 是明文传输,运行在 TCP 之上,端口 80,不加密,容易被窃听、篡改、劫持。HTTPS 是 HTTP + SSL/TLS,端口 443,通过非对称加密交换会话密钥,再用对称加密传输数据,结合数字证书认证身份,保证传输的机密性、完整性、身份可信。HTTPS 握手更耗时,但更安全,是现代 Web 标准。
记忆:HTTP明文80;HTTPS加密443,证书+对称+非对称
6. OSI七层模型 & TCP/IP四层模型
OSI 七层模型(理论):应用层、表示层、会话层、传输层、网络层、数据链路层、物理层。
TCP/IP 四层模型(实际使用):应用层、传输层、网络层、网络接口层。
对应关系:应用层(合并了OSI应用/表示/会话)、传输层对应、网络层对应、网络接口层(合并了数据链路/物理)。
记忆:OSI七:应表会传网链物;TCP/IP四:应传网接
7. GET与POST区别
GET 用于获取资源,参数放在 URL 中,长度受限、明文可见、可被缓存、可被收藏、幂等(多次请求结果一致)。POST 用于提交数据,参数放在请求体中,长度无限制、相对安全、不可缓存、不可收藏、不幂等。GET 会被浏览器主动预加载,POST 不会;GET 只能传输 ASCII,POST 支持多种格式。本质区别是语义不同,而非安全差异。
记忆:GET查、URL、可缓存;POST传、请求体、更安全
二、TCP与UDP
1. 三次握手、四次挥手、为什么三次
三次握手:
- 客户端发送 SYN 报文,请求建立连接
- 服务端回复 SYN+ACK,确认并同意
- 客户端发送 ACK,连接建立
必须三次:为了确认双方收发能力都正常,防止因网络阻塞导致过期的连接请求到达服务器,造成服务器资源浪费。
四次挥手:
- 客户端发 FIN,请求关闭
- 服务端回 ACK(此时服务端可能还在发数据)
- 服务端发 FIN,确认关闭
- 客户端回 ACK,等待 2MSL 后彻底关闭
需要四次是因为TCP 是全双工,双方都要独立关闭。
记忆:握手SYN、SYN+ACK、ACK;挥手FIN、ACK、FIN、ACK
2. TCP和UDP区别

TCP 是面向连接、可靠、有序、字节流协议,提供超时重传、流量控制、拥塞控制,开销大,速度慢,适合文件传输、网页、支付等要求可靠的场景。
UDP 是无连接、不可靠、无序、数据报协议,不保证到达、不保证顺序,开销小、速度极快,支持一对一、一对多、多对多,适合直播、游戏、DNS、视频通话。
记忆:TCP可靠慢;UDP快不可靠
3. TCP可靠传输;UDP如何可靠
TCP 依靠序列号、确认应答 ACK、超时重传、滑动窗口、流量控制、拥塞控制、校验和保证可靠传输,丢失、乱序、重复都能自动处理。
UDP 本身不可靠,若要实现可靠,必须在应用层自行实现:添加序列号、确认应答、超时重传、滑动窗口、去重、校验等机制(类似 QUIC)。
记忆:TCP:序号+ACK+重传+窗口;UDP:应用层自己实现可靠
4. TCP分包与粘包
TCP 是流式协议,没有消息边界,多个小包可能被合并(粘包),一个大包可能被拆分(分包)。
原因:Nagle 算法、滑动窗口、MTU 限制。
解决方案:
- 固定消息长度
- 特殊分隔符
- 消息头 + 消息长度(最常用)
记忆:流无边界;解决:头长度/分隔符
5. TCP拥塞控制
TCP 拥塞控制包含四个核心算法:
- 慢启动:拥塞窗口指数增长
- 拥塞避免:窗口线性增长
- 快重传:收到 3 个重复 ACK 立即重传,不等待超时
- 快恢复:丢包后窗口降为一半,进入线性增长,不从头开始
目的是防止发送过快导致网络拥塞崩溃。
记忆:慢启→拥塞避→快重传→快恢复
TCP涉及到的函数
对照着tcp状态迁移图学习:


- 客户端先发起请求, 向服务端发送tcp包,进入SYN_SENT状态:[SYN=1,seqnum=1234],一般seqnum为随机值
- 服务端解析这个tcp包,发现有[SYN, seqnum],确认是客户端发送三次握手请求后,向客户端返回[ACK=1, acknum=1235(seqnum+1), SYN=1, seqnum=5647(随机值)], 进入SYN_RCVD状态
- 客户端发送数据 [ACK=1, acknum=5648], 进入ESTABLISHED, 服务端接受到ACK,进入到ESTABLISHED
三次握手主要是确定双方发送数据从什么时候开始,这里的seqnum、acknum的作用是保证不重复、不乱序。
第一个过程表示客户端向服务端发送从seqnum(1234)开始,下一次发从acknum(1235)开始;
第二个过程表示服务端向客户端发送从seqnum(5647)开始,下一次发从acknum(5648)开始;
(1)M: 三次握手的过程,发生在哪些函数里面?

半连接队列 syn队列
全连接队列 accept队列
- a. tcp连接的生命周期,从什么时候开始?
从client第一次连接开始,server端listen()到syn包,协议栈会为其分配对应的tcp连接。
- b. 第三次握手数据包,如何从半连接队列查找匹配的节点?
通过五元组 [源ip,源port,目的ip, 目的port, 协议类型],从而查找到匹配的节点
- c. 如何防护SYN泛宏,DDos攻击(攻击者模拟客户端多次向服务端发送第一次请求,真正需要服务的顾客无法正常访问,可能导致服务器崩溃)
(2)M:listen的第二个参数是什么?
从上世纪70年代,listen已经历了多个版本的迭代,listen(fd, backlog), 第二个参数backlog共有三个版本
1)syn队列长度
在旧版本中(70年代)listen(fd, backlog), 通过backlog参数来设置半连接syn队列的长度,从而限制连接无限增长。
优点:避免SYN泛宏
缺点:鸡肋
2)syn + accept队列总长度,未分配fd的tcb的数量
优于 1)
3)accept队列长度
加快建立连接的速率,更符合网络的需求
accept()
1)分配fd
2)fd --> tcb
关于IO有没有数据?
当一个I/O事件(例如,新的数据到达可读,或者套接字可写)发生时,epoll会根据其配置的触发模式来通知应用程序
(3) 如果双方同时connect,发起三次握手呢?
在客户端调用 connect 函数之前,其实我们还可以通过 bind 函数将客户端绑定一个端口,那么如果此时我们再在另一个客户端通过 connect 函数连接该客户端,这时就是 p2p 连接.
fd = socket();
localaddr, remoteaddr;
bind(8000); //optional
connect();
p2p: 这是一种Tcp的点对点连接,没有所谓的客户端与服务端的概念,这种方式是一种去中心化的连接方式,中间不通过网络、Server,直接进行通信,传播速度更快和方便。

水平触发与边沿触发
水平触发(Level Triggered, LT):检测有无数据,可触发多次,可分多次读完
这是epoll的默认工作模式。在这种模式下,只要文件描述符上存在可用的I/O条件(例如,读缓冲区中仍有数据可读,或者写缓冲区仍有空间可写),epoll就会持续地报告该事件。即使应用程序没有一次性处理完所有数据,只要条件仍然满足,epoll会反复触发该事件,直到所有数据都被处理完毕,或者写缓冲区被填满。
边沿触发(Edge Triggered, ET):检测数据从无到有(过程),只触发一次,且一次性读完.(相当于一个while 1,直到读完退出)
在这种模式下,epoll只会在文件描述符上的I/O条件发生变化时(即从“不可用”变为“可用”的瞬间)通知一次事件。一旦事件被通知,即使文件描述符上仍然存在可用的I/O条件,epoll也不会再次触发该事件,直到下一次I/O条件发生新的“边沿”变化。应用程序必须在一次事件通知中尽可能多地处理所有可用的数据或完成所有可写的操作,否则剩余的数据或未完成的操作将不会再次触发事件通知,可能导致数据滞留或饿死。
所以 accept适合用 水平触发(LT),有数据就触发,有多个连接,每次处理一个节点。
那 M: 如果用 边沿触发(ET), accept该如何写, 从而可以处理多个连接?
因为ET只触发一次,为了能处理多个连接,可以使用一个while循环
while(1){
if(-1 == accept()) break;
}
Poisx API

只讨论传输层 tcp协议是描述的两个机器kernel协议栈互相通信
代码实现层面则是posix API和kernel协议栈的交互逻辑
tcp通信涉及到的拥塞控制和滑动窗口
拥塞控制是一组算法和机制,用于检测和响应网络中的拥塞情况,防止过多的数据注入网络。
主要算法:
慢启动(Slow Start):在TCP连接建立之初,窗口大小指数增长,直到达到一个阈值(ssthresh),然后转入拥塞避免阶段。
拥塞避免(Congestion Avoidance):在达到慢启动阈值后,窗口大小线性增长。

快速重传(Fast Retransmit):在检测到丢包时,立即重传丢失的数据包,而不是等待重传定时器超时。
快速恢复(Fast Recovery):在快速重传后,适当调整窗口大小,以快速恢复传输。
在传输阶段,涉及到几个概念:
1.慢启动:慢启动是 TCP 拥塞控制的初始阶段,用于在连接建立后逐步增加发送速率,避免一开始就向网络发送大量数据导致拥塞。尽管名为 “慢启动”,但实际速率增长是指数级的,只是起点较低,因此称为 “慢”。
2.拥塞控制:拥塞控制是网络协议通过调整发送方的数据包发送速率,使网络负载处于合理范围,避免拥塞发生或缓解已发生拥塞的机制
3.滑动窗口:滑动窗口是 TCP 实现流量控制和拥塞控制的基础机制,它通过在发送方和接收方维护一个 “窗口” 范围,动态限制发送方未确认数据的最大量,从而实现高效的双向数据传输。
4.延迟确认:当接收方收到数据后,不立即发送 ACK(确认数据),而是等待一小段时间(通常 200ms 以内),若这段时间内有其他数据需要发送给对方(如应用层响应数据),则将 ACK 与数据报文合并发送;若没有,则超时后单独发送 ACK。
5.超时重传:在超过一定时间内发送方没有回复,默认这个包重传。
close()
1.将fd回收
2.发送一个fin包
断开连接:四次挥手
主动方和被动方

第一次握手:客户端发送FIN报文,表示 “我已完成数据发送,请求关闭我的发送通道”,发送后,客户端进入 FIN_WAIT_1 状态,等待服务器的确认。
第二次握手:服务器回复ACK报文,告知客户端 “我已收到你的关闭请求”,发送后,服务器进入 CLOSE_WAIT 状态,此时客户端到服务器的发送通道已关闭,但服务器仍可向客户端发送数据。
客户端收到 ACK 后,客户端进入 FIN_WAIT_2 状态,等待服务器的 FIN 报文。
第三次握手:服务器向客户端发送FIN报文,表示 “我也完成数据发送,请求关闭我的发送通道”,发送后,服务器进入 LAST_ACK 状态,等待客户端的最终确认。
第四次握手:客户端回复ACK报文,发送后,客户端进入 TIME_WAIT 状态,确保服务器收到 ACK),最终进入 CLOSED 状态。服务器收到 ACK 后,立即进入 CLOSED 状态,释放所有资源。

可能遇到的问题:
1)fin_wait_1 ack没有收到,先收到fin
ESTABLISHED -> FIN_WAIT_1 -> TIME_WAIT
2) 双方同时调用close
双方都是发送FIN,进入FIN_WAIT_1, 接收ACK, 进入CLOSING, 接收ACK, 进入TIME_WAIT
(如果服务器出现大量TIME_WAIT,可能是出现了双方同时调用close的情况)
计操
一、进程与线程
1. 进程与线程的区别
进程是资源分配的最小单位,拥有独立的虚拟地址空间、文件描述符、信号处理,切换开销大,进程间相互隔离。
线程是 CPU 调度的最小单位,共享所属进程的地址空间、堆、全局变量、文件描述符,只私有栈、寄存器、线程局部存储,切换开销极小。一个进程可包含多个线程,线程崩溃可能导致整个进程崩溃。
记忆:进程是资源,线程是调度;进程隔离,线程共享
2. 进程间通信方式(IPC)
常见 IPC 方式:
- 匿名管道/管道:半双工,父子进程使用
- 命名管道(FIFO):不相关进程也可用
- 消息队列:按消息存取,独立于进程
- 共享内存:最快 IPC,直接映射物理内存
- 信号量:用于同步与互斥
- Socket:跨主机通信
记忆:管、有名管、消息队、共享内存、信号量、Socket
3. 进程调度算法
常见调度算法:
- FCFS 先来先服务:简单,不利于短作业
- SJF 短作业优先:平均等待时间最短,可能饥饿
- 优先级调度:按优先级执行
- 时间片轮转:公平,适合交互式系统
- 多级反馈队列:综合型,最通用(Linux 标准)
记忆:FCFS、SJF、时间片、优先级、多级队列
4. 协程是什么
协程是用户态轻量级线程,完全由程序自己调度,不进入内核态,切换不需要上下文切换、不需要系统调用,开销远小于线程。协程是非抢占式,必须主动让出 CPU,适合 IO 密集型高并发场景,能大幅提升吞吐量。
记忆:用户态、轻量、自己调度、无内核开销
二、锁
1. 锁与死锁
锁用于多线程/多进程同步互斥,保证共享资源安全访问,避免竞争冒险。
死锁是指多个执行流互相持有对方需要的资源,且都不主动释放,导致永久阻塞,无法继续执行。
记忆:锁保安全;死锁:互相等、永久卡死
2. 死锁的四个必要条件
- 互斥:资源同时只能被一个占有
- 请求与保持:持有资源并请求新资源
- 不可剥夺:资源只能主动释放
- 循环等待:形成资源等待环路
四个条件缺一不可,破坏任意一个即可避免死锁。
记忆:互斥、请求保持、不可剥夺、循环等待
3. 互斥锁 vs 自旋锁
互斥锁:抢不到锁会休眠,进入内核态,不占用 CPU,适合锁持有时间长的场景。
自旋锁:抢不到锁循环忙等,不休眠,一直占用 CPU,适合锁持有时间极短、内核/中断场景。
记忆:互斥锁休眠;自旋锁循环等
4. 乐观锁 vs 悲观锁
悲观锁:默认会发生冲突,操作前先加锁(如 synchronized、互斥锁),阻塞其他线程,简单安全但并发低。
乐观锁:默认无冲突,不加锁,执行完成后用版本号、时间戳、CAS 检查是否冲突,冲突则重试,并发高但实现复杂。
记忆:悲观先加锁;乐观后校验(CAS/版本号)
三、内存管理
1. 虚拟内存 & 物理内存
物理内存是真实的硬件内存条,是实际可用的地址空间。
虚拟内存是操作系统给进程提供的逻辑地址空间,让每个进程以为独占连续内存,通过页表映射到物理内存,可将暂时不用的内存置换到磁盘(Swap),扩大可用空间、实现隔离与保护。
记忆:物理真内存;虚拟逻辑、页表映射
2. 程序内存布局
程序运行时内存分为:
- 代码段:存放执行指令
- 数据段:已初始化全局变量、静态变量
- BSS 段:未初始化全局/静态变量(默认 0)
- 堆:动态分配,向上增长
- 栈:局部变量、函数调用,向下增长
- 命令行参数、环境变量
记忆:代码→数据→BSS→堆→栈
3. 堆和栈的区别
栈由系统自动分配/释放,连续空间,速度快,大小固定,线程私有。
堆由程序员手动 malloc/new 分配,free/delete 释放,不连续,速度慢,大小灵活,容易产生内存碎片,进程内共享。
记忆:栈自动、快、连续;堆手动、慢、碎片
4. 内存不足会发生什么
物理内存不足时,操作系统启动Swap 交换分区,将不活跃的内存页换出到磁盘;若继续不足,触发 OOM killer 机制,根据优先级杀掉占用高、优先级低的进程,保证系统不崩溃。
记忆:先Swap换页;再OOM杀进程
5. 分段 & 分页
分页:把内存切成固定大小的页,离散分配,无外部碎片,地址映射简单。
分段:按程序逻辑模块分段(代码段、数据段、栈段),有完整意义,会产生外部碎片。
现代操作系统多用段页式结合。
记忆:分页固定、无碎片;分段逻辑、有碎片
6. 共享内存原理
共享内存是将同一块物理内存映射到多个进程的虚拟地址空间,进程直接读写,不需要内核拷贝,是最快 IPC。但需要信号量等机制实现同步,避免竞争。
记忆:同一块物理内存;直接读写、最快IPC
7. 用户态 & 内核态
用户态:应用程序运行,权限低,不能直接访问硬件、修改页表、关中断等。
内核态:操作系统运行,最高权限,可管理所有资源、调度进程、访问硬件。
通过系统调用、中断、异常从用户态切换到内核态,切换有开销。
记忆:用户态受限;内核态最高权;系统调用切换
需要我继续给你整理 MySQL / Redis / C++ / 算法手撕模板 吗?我可以按这个深度一次性给你补齐,直接背完上战场。当前文件内容过长,豆包只阅读了前 64%。

浙公网安备 33010602011771号