国际标准网络模型
国际标准网络模型通常指的是 OSI(Open Systems Interconnection)参考模型,它是一个由国际标准化组织(ISO)定义的网络通信模型,用于指导网络协议的设计和实现。该模型将计算机网络通信划分为七个不同的层次,每个层次负责特定的功能,从物理连接到应用程序。
以下是 OSI 参考模型的七个层次及其功能:
1. 物理层(Physical Layer):
2. 数据链路层(Data Link Layer):
3. 网络层(Network Layer):
- 控制数据包在网络中的路由和转发。
- 提供寻址、路由、流量控制和错误处理等功能。
4. 传输层(Transport Layer):
- 提供端到端的数据传输服务,确保数据在源与目的地之间的可靠传输。
- 通常包括错误检测、流量控制和数据分段等功能。
5. 会话层(Session Layer):
6. 表示层(Presentation Layer):
7. 应用层(Application Layer):
每个层次的功能相对独立,通过定义清晰的接口,不同层次之间能够有效地协同工作,实现端到端的通信。OSI 参考模型为网络协议的设计和实现提供了一个标准化的框架,促进了不同厂商和组织之间的互操作性。
TCP/IP 模型是另一个网络通信模型,通常与 OSI 模型进行对比。在 TCP/IP 模型中,功能被划分为四个层次,分别是网络接口层、互联网层、传输层和应用层。以下是 TCP/IP 模型的四个层次及其对应的功能:
1. 网络接口层(Network Interface Layer):
- 对应 OSI 模型中的物理层和数据链路层。
- 负责将数据包封装成帧,在网络中传输。
- 与硬件设备和网络介质直接交互,处理物理连接和数据帧传输。
2. 网络层(Internet Layer):
- 对应 OSI 模型中的网络层。
- 负责数据包的路由和转发,实现不同网络之间的通信。
- 使用 IP 协议来定义数据包的寻址和路由。
3. 传输层(Transport Layer)
4. 应用层(Application Layer):
- 对应 OSI 模型中的会话层、表示层和应用层。
- 提供各种网络应用服务,如 HTTP、FTP、SMTP 等。
- 包含各种应用层协议,用于实现不同的网络应用服务。
由于 OSI 模型实在太复杂,提出的也只是概念理论上的分层,并没有提供具体的实现方案。所以事实上我们常见的网络模型只是TCP/IP模型,Linux也是采用的这种模型
Linux的网络协议栈
知道了 TCP/IP 网络模型,以及网络包的封装原理后,那么 Linux 网络协议栈的样子,它其实就类似于 TCP/IP 的四层结构:
不过Linux 的网络协议栈是作为 Linux 内核的一部分存在的,因此网络协议栈中的网络功能代码运行在内核空间。这种设计使得网络协议栈能够直接访问硬件设备,提供更高的效率和更快的响应速度。
Linux的传输过程和软中断
在Linux系统中,数据包的传输过程和软中断密切相关,因为软中断是一种机制,用于处理网络数据包和其他异步事件。
软中断
网卡是计算机里的一个硬件,专门负责接收和发送网络包,当网卡接收到一个网络包后,会通过 DMA 技术,将网络包写入到指定的内存地址,也就是写入到 Ring Buffer ,这个是一个环形缓冲区,接着就会告诉操作系统这个网络包已经到达。
在传输过程中怎么告诉操作系统这个网络包已经到达了呢?
最简单的一种方式就是触发中断,也就是每当网卡收到一个网络包,就触发一个中断告诉操作系统。但是,这存在一个问题,在高性能网络场景下,网络包的数量会非常多,那么就会触发非常多的中断,会导致 CPU 一直没完没了的处理中断,而导致其他任务可能无法继续前进,从而影响系统的整体效率。
为了解决性能开销, Linux内核引入了NAPI 机制(混合中断和轮询的方式来接收网络包)核心概念就是不采用中断的方式读取数据,首先采用中断唤醒数据接收的服务程序,然后 poll
的方法来轮询数据。
因此,当有网络包到达时,还是先通过会通过 DMA 技术,将网络包写入到指定的内存地址,接着网卡向 CPU 发起硬件中断,当 CPU 收到硬件中断请求后,根据中断表,调用已经注册的中断处理函数。
这个处理函数需要先暂时屏蔽中断,表示已经知道内存中有数据了,告诉网卡下次再收到数据包直接写内存就可以了,不要再通知 CPU 了,这样可以提高效率,避免 CPU 不停的被中断。接着,发起软中断,然后恢复刚才屏蔽的中断。
至此,硬件中断处理函数的工作就已经完成。其实硬中断做的事情很少,主要事情都是由软中断完成的。
先来解释一下什么是软中断:
软中断是Linux内核中一种机制,用于处理异步事件,如网络数据包的到达、定时器事件等。软中断允许内核在不打断正在执行的进程的情况下,及时响应这些异步事件。
内核中的 ksoftirqd 线程专门负责软中断的处理,当 ksoftirqd 内核线程收到软中断后,就会来轮询处理数据。ksoftirqd 线程会从 Ring Buffer 中获取一个数据帧,用 sk_buff 表示,从而可以作为一个网络包交给网络协议栈进行逐层处理。
数据包传输过程:
首先数据包生成,内核接收到数据包后,数据包会经过网络协议栈的各个层级进行处理,如链路层、网络层、传输层等。在每个层级,相应的协议会处理数据包,添加必要的头部信息以便路由和传输。
内核根据数据包的目标地址进行路由选择,确定数据包应该通过哪个网络接口发送或转发。如果目标地址是本地的,则数据包将直接传递给目标应用程序;否则,数据包将被发送到下一个路由器或主机。
数据包通过合适的网络接口发送到目标主机或路由器,经过网络传输的各种设备,最终到达目标。
目标主机的网络接口接收数据包,并将其传递给内核进行处理,最终将数据包分发给目标应用程序。
需要注意的是在发送过程中:
如果使用的是 TCP 传输协议发送数据,那么先拷贝一个新的 sk_buff 副本 ,这是因为 sk_buff 后续在调用网络层,最后到达网卡发送完成的时候,这个 sk_buff 会被释放掉。而 TCP 协议是支持丢失重传的,在收到对方的 ACK 之前,这个 sk_buff 不能被删除。所以内核的做法就是每次调用网卡发送的时候,实际上传递出去的是 sk_buff 的一个拷贝,等收到 ACK 再真正删除。
值得注意的是全部数据包只用一个结构体sk_buff 来描述,为了满足这个合理的要求, sk_buff 中 data
的指针可以完美满足他,通过调动指针的位置(这个data的位置在头部)
如当接收报文时,从网卡驱动开始,通过协议栈层层往上传送数据报,通过增加 skb->data 的值,来达到一种层层剥离的效果。
当要发送报文时,创建 sk_buff 结构体,数据缓存区的头部预留足够的空间,用来填充各层首部,在经过各下层协议时,通过减少 skb->data 的值来增加协议首部。
至此传输层的工作完成后,经过类似的网络层和网络接口层的操作后会触发软中断告诉网卡驱动程序,这里有新的网络包需要发送,驱动程序会从发送队列中读取 sk_buff,将这个 sk_buff 挂到 RingBuffer 中,接着将 sk_buff 数据映射到网卡可访问的内存 DMA 区域,最后触发真实的发送。(其实我也不太懂这部分)
当发送完成的时候,网卡设备会触发一个硬中断来释放内存,主要是释放 sk_buff 内存(这里的是备份)和清理 RingBuffer 内存。最后,当收到这个 TCP 报文的 ACK 应答时,传输层就会释放原始的 sk_buff(真实) 。
多少次拷贝?
用户空间到内核空间:当应用程序通过套接字发送数据时,数据首先要从用户空间拷贝到内核空间的套接字缓冲区
从传输层进入网络层的时候:每一个 sk_buff 都会被克隆一个新的副本出来。副本 sk_buff 会被送往网络层
当sk_buff 大于 MTU 时:可能会发生,申请额外的sk_buff,将原来的拷贝为多个。