网络系统
1. 网络协议栈
- 即应用层数据在各层的封装格式
传输层,给应⽤数据前⾯增加了TCP头
⽹络层,给TCP数据包前⾯增加了IP头
⽹络接⼝层,给IP数据包前后分别增加了帧头和帧尾
- 每⼀层都增加了各⾃的协议头,⽹络包的⼤⼩就增⼤
- 网络协议栈结构
应⽤程序需要通过系统调⽤,来跟 Socket 层进⾏数据交互
Socket 层的下⾯就是传输层(TCP/UDP)、⽹络层(IP)和⽹络接⼝层(ARP)
最下⾯的⼀层,则是⽹卡驱动程序和硬件⽹卡设备
2. Linux接收网络包流程
- 当⽹卡接收到⼀个⽹络包后,会通过 DMA 技术,将⽹络包放⼊到 Ring Buffer,这个是⼀个环形缓冲区
- Linux 内核引⼊了 NAPI 机制来告知操作系统网络包到达,且不频繁触发中断来影响系统效率
网络包到达,网卡发起硬件中断,于是会执⾏⽹卡硬件中断处理函数
中断处理函数处理完需要「暂时屏蔽中断」
唤醒「软中断」来轮询处理数据
直到没有新数据时才恢复中断,这样⼀次中断处理多个⽹络包
- 软中断怎么处理网络包
从 Ring Buffer 中拷⻉数据到内核缓冲区
以一个网络包交给网络协议栈逐层解析
网络接口层:检查报⽂的合法性 ,再去掉帧头帧尾,交给网络层
⽹络层:取出 IP 包,判断⽹络包下⼀步的⾛向
交给上层处理。确认给本机后,就会看是 TCP 还是 UDP,接着去掉 IP 头,然后交给传输层
转发出去
传输层取出TCP头或UDP头,根据四元组「源IP、源端⼝、⽬的IP、⽬的端⼝」作为标识,找出对应Socket,并把数据拷⻉到 Socket 的接收缓冲区
应⽤层程序调⽤ Socket 接⼝,从内核的 Socket 接收缓冲区读取新到来的数据到应⽤层
3. Linux发包流程
- 发送⽹络包的流程正好和接收流程相反。
- 应用层->socket缓冲区->传输层->网络层->网络接口层->软中断->网卡
4. 零拷贝
- 什么是DMA
在进⾏ I/O 设备和内存的数据传输的时候,数据搬运的⼯作全部交给DMA 控制器,⽽ CPU 不再参与任何与数据搬运相关的事情,这样 CPU 就可以去处理别的事务
- 什么是零拷贝
没有在内存层⾯去拷⻉数据,也就是说全程没有通过CPU 来搬运数据,所有的数据都是通过 DMA 来进⾏传输的
- 零拷贝实现
传统IO
mmap+write
sendfile
sendfile+DMA(Linux2.4 内核版本)
- 零拷贝项目
Kafka
Nginx
- PageCache
在数据传输过程中,第一步都是先把数据拷贝到内核缓冲区,这个「内核缓冲区」实际上是磁盘⾼速缓存(PageCache)
PageCache 来缓存最近被访问的数据
PageCache 使⽤了「预读功能」
但是大文件传输(不能使用零拷贝)
PageCache被大文件占满,其他「热点」的⼩⽂件可能就⽆法充分使⽤到 PageCache ,降低磁盘读写性能
PageCache会很快被占满,白白浪费MDA多做的一次拷贝
- 大文件传输:「异步 I/O + 直接 I/O」
read会阻塞很久:可以用异步IO来解决
绕过PageCache :可以只用直接IO,就是不使用PageCache
使用直接IO无法享受内核的优化
内核调度算法会尽可能缓存多次I/O请求在PageCache 中,最后「合并」成⼀个更⼤的 I/O请求再发给磁盘,减少磁盘寻址操作
无法享受预读功能
5. I/O多路复用
- 基本的Socket模型
服务端⾸先调⽤ socket() 函数,指定网络协议、传输协议,创建scoket
调⽤bind() 函数,给这个 Socket 绑定⼀个 IP 地址和端⼝
1~2就是让发送到指定网卡和端口的数据,来到我们的应用程序
调⽤ listen() 函数进⾏监听
服务器为每个socket维护了两个队列,服务器调用accept() 函数,来从内核的全连接队列获取客户端的连接,成功返回已连接的socket;失败就阻塞
TCP 半连接队列
TCP 全连接队列
监听的 Socket 和真正⽤来传数据的 Socket 是两个:
⼀个叫作监听 Socket
⼀个叫作已连接 Socket
- C10K问题
C 是 Client 单词⾸字⺟缩写, C10K 就是单机同时处理 1 万个请求的问题
- Scoket如何服务多用户
理论上:对于 IPv4,客户端的 IP 数最多为 2 的 32 次⽅,客户端的端⼝数最多为 2 的 16 次⽅,也就是服务端单机最⼤ TCP 连接数约为 2 的 48 次⽅。
但是:每个socket会占用文件描述符;每个TCP连接都有数据结构,就会占用内存
最主要的是内存以及网络I/O模型
- 多进程
为每个客户端分配⼀个进程来处理请求。
缺点:
每个进程占用系统资源,代价很重
子进程不会自动回收,会变成僵尸进程
需要手动释放
进程上下文切换,影响性能
- 多线程
涉及到频繁创建和销毁线程
- 线程池
消息来了放到队列,线程去取
队列是全局的,每个线程都会操作,为了避免多线程竞争,线程在操作这个队列前要加锁
C10K问题
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
涵盖各大厂考官最爱问知识点,22年最新整理!