Linux 网络收包流程
哈喽大家好,我是咸鱼
我们在跟别人网上聊天的时候,有没有想过你发送的信息是怎么传到对方的电脑上的
又或者我们在上网冲浪的时候,有没有想过 HTML 页面是怎么显示在我们的电脑屏幕上的
无论是我们跟别人聊天还是上网冲浪,其实都依靠于计算机网络这项技术
计算机网络是指将多台计算机通过通信设备和传输介质连接在一起,使得它们之间能够相互通信、资源共享和协同工作
而计算机之间是通过数据包来实现信息传输和信息交换的,数据包是计算机网络中传输数据的基本单位
今天咸鱼将以 Linux 为例来给大家介绍一下 Linux 是如何实现网络接收数据包
网络协议栈&网络架构
在正文开始之前,我们先来了解一下 Linux 中的网络协议模型和网络子系统
- 网络协议模型(网络协议栈)
在 Linux 中,Linux 网络协议栈分成了五层

其中:
应用层提供 socket 接口来供用户进程访问内核空间的网络协议栈
传输层、网络层协议由 Linux 内核网络协议栈实现
链路层协议靠网卡驱动来实现
物理层协议由硬件网卡实现

网络子系统(网络架构)
网络子系统是 Linux 内核中的一部分,由多个模块和驱动程序组成,它负责管理和控制系统的网络功能以实现网络通信
通过 Linux 网络子系统(网络架构)来实现上述网络协议模型

其中
- System call interface:为应用程序获取内核的网络系统提供了接口,例如 socket
- Protocol agnostic interface:为和各种传输层协议的网络交互提供的一层公共接口
- Network protocals:对各种传输层协议的实现,如 TCP、UDP、IP 等
- Device agnostic interface:为各种底层网络设备抽象出的公共接口,与各种网络设备驱动连接在一起
- Device drivers:与各种网络设备交互的驱动
收包过程
当 Linux 接收一个数据包的时候,这个包是怎么经过 Linux 的内核从而被应用程序拿到的呢?

- 到达网卡(NIC,Network Interface Card)
首先数据包到达网卡之后,网卡会校验接收到的数据包中的目的 MAC 地址是不是自己的 MAC 地址,如果不是的话通常就会丢弃掉
这种只接受发送给自己的数据包(其余的扔掉)的工作模式称为非混杂模式(Non-Promiscuous Mode)
混杂模式(Promiscuous Mode)则是网卡会接收通过网络传输的所有数据包,而不仅仅是发送给它自己的数据包
非混杂模式是网卡默认的工作模式,可以尽可能的保护网络安全和减少网络负载
网卡在校验完 MAC 地址之后还会校验数据帧(Data Frame)中校验字段 FCS 来一次确保接收到的数据包是正确的
- 网卡硬件缓冲区 ——> 系统内存(ring buffer)
当网卡接收到数据包时,它将数据包的内容存储在硬件缓冲区中,然后通过 DMA 将接收到的数据从硬件缓冲区传输到系统内存中的指定位置,这个位置通常是一个环形缓冲区( ring buffer)
DMA(直接内存访问,Direct Memory Access)
DMA是一种数据传输技术,允许外设(如网卡、硬盘控制器、显卡等)直接访问计算机内存,而无需经过 CPU
通过 DMA 可以大大提高数据传输的效率,减轻 CPU 的负担
- 触发硬中断
当网卡将数据包 DMA 到用于接收的环形缓冲区(rx_ring)之后,就会触发一个硬中断来告诉 CPU 数据包收到了
什么时候会触发一个硬中断,可以通过下面的参数来进行配置:
- rx-usecs:当过这么长时间过后,一个中断就会被产生
- rx-frames:当累计接收到这么多个数据帧后,一个中断就会被产生
上面的参数配置可以通过下面的命令来查看
# 以 CentOS 7 为例
ethtool -c <网卡名称>
当 ring buffer 满了之后,新来的数据包将给丢弃
ifconfig 查看网卡的时候,可以里面有个 overruns,表示因为环形队列满而被丢弃的包
CPU 收到硬中断之后就会停止手中的活,保存上下文,然后去调用网卡驱动注册的硬中断处理函数
为数据包分配 skb_buff ,并将接收到的数据拷贝到 skb_buff 缓冲区中
当一个数据包经过了网卡引起中断之后,每一个包都会在内存中分配一块区域,称为
sk_buff(套接字缓存,socket buffer )
sk_buff是 Linux 网络的一个核心数据结构
- 触发软中断
网卡的硬中断处理函数处理完之后驱动先 disable 硬中断,然后 enable 软中断
ps:待 ring buffer 中的所有数据包被处理完成后,enable 网卡的硬中断,这样下次网卡再收到数据的时候就会通知 CPU
内核负责软中断进程 ksoftirqd 发现有软中断请求到来,进行下面的一些操作
# 查看软中断进程
[root@localhost ~]# ps -ef | grep ksoftirqd
调用 net_rx_action 函数
它会通过 poll 函数去 rx_ring 中拿数据帧,获取的时候顺便把 rx_ring 上的数据给删除
static void net_rx_action(struct softirq_action *h)
{
struct softnet_data *sd = &__get_cpu_var(softnet_data);
unsigned long time_limit = jiffies + 2;
int budget = netdev_budget;
void *have;
local_irq_disable();
while (!list_empty(&sd->poll_list)) {
......
n = list_first_entry(&sd->poll_list, struct napi_struct, poll_list);
work = 0;
if (test_bit(NAPI_STATE_SCHED, &n->state)) {
work = n->poll(n, weight);
trace_napi_poll(n);
}
budget -= work;
}
}
除此之外,poll 函数会把 ring buffer 中的数据包转换成内核网络模块能够识别的 skb 格式(即 socket kernel buffer )
socket kernel buffer (skb) 是 Linux 内核网络栈处理网络包(packets)所使用的 buffer,它的类型是 sk_buffer
3、最后进入 netif _receive_skb 处理流程,它是数据链路层接收数据帧的最后一关
根据注册在全局数组 ptype_all 和 ptype_base 里的网络层数据帧类型去调用第三层协议的接收函数处理
例如对于 ip 包来讲,就会进入到
ip_rcv;如果是 arp 包的话,会进入到arp_rcv
- 到达网络层(以 IP 协议为例)
IP 层的入口函数在 ip_rcv 函数,调用 ip_rcv 函数进入三层协议栈
首先会对数据包进行各种检查(检查 IP Header),然后调用 netfilter 中的钩子函数: NF_INET_PRE_ROUTING
netfilter: 是 Linux 内核中进行数据包过滤,连接跟踪(Connect Track),网络地址转换(NAT)等功能的主要实现框架
该框架在网络协议栈处理数据包的关键流程中定义了一系列钩子点(Hook 点),并在这些钩子点中注册一系列函数对数据包进行处理
这些注册在钩子点的函数即为设置在网络协议栈内的数据包通行策略,也就意味着,这些函数可以决定内核是接受还是丢弃某个数据包
NF_INET_PRE_ROUTING 会根据预设的规则对数据包进行判断并根据判断结果做相关的处理(修改或者丢弃数据包)
处理完成后,数据包交由 ip_rcv_finish 处理,该函数根据路由判决结果,决定数据包是交由本机上层应用处理,还是需要进行转发
如果是交由本机处理,则会交由 ip_local_deliver 本地上交流程;如果需要转发,则交由 ip_forward 函数走转发流程
- 到达传输层(以 TCP 为例)
传输层 TCP 处理入口在 tcp_v4_rcv 函数,首先检查数据包的 TCP 头部等信息,确保数据包的完整性和正确性
然后去查找该数据包对应的已经打开的 socket ,如果找不到匹配的 socket,表示该数据包不属于任何一个已建立的连接,因此该数据包会被丢弃
如果找到了匹配的 socket,TCP 会进一步检查该 socket 和连接的状态,如果状态正常,TCP 会将数据包从内核传输到用户空间,放入 socket 的接收缓冲区(socket receive buffer)
- 应用层获取数据
当数据包到达操作系统内核的传输层时,应用程序可以从套接字的接收缓冲区(socket receive buffer)中读取数据包
一般有两种方式读取数据,一种是 recvfrom 函数阻塞在那里等着数据来,这种情况下当 socket 收到通知后,recvfrom 就会被唤醒,然后读取接收队列的数据
另一种是通过 epoll 或者 select 监听相应的 socket,当收到通知后,再调用 recvfrom 函数去读取接收队列的数据
总结
网络模块可以说是 Linux 内核中最复杂的模块了
看起来一个简简单单的收包过程就涉及到许多内核组件之间的交互,如网卡驱动、协议栈,内核ksoftirqd 线程等
咸鱼原本打算把收包和发包的流程都写上的,但是光是写收包流程就就要了我半条命了,等下次有机会把发包的流程也写一下
总结一下 Linux 网络收包流程:
- 数据到达网卡之后,网卡通过 DMA 将数据放到内存分配好的一块
ring buffer中,然后触发硬中断 - CPU 收到硬中断之后简单的处理了一下(分配
skb_buffer),然后触发软中断 - 软中断进程
ksoftirqd执行一系列操作(例如把数据帧从ring ruffer上取下来)然后将数据送到三层协议栈中 - 在三层协议栈中数据被进一步处理发送到四层协议栈
- 在四层协议栈中,数据会从内核拷贝到用户空间,供应用程序读取
- 最后被处在应用层的应用程序去读取
Linux 网络收包流程的更多相关文章
- linux网络收包过程
记录一下linux数据包从网卡进入协议栈的过程,不涉及驱动,不涉及其他层的协议处理. 内核是如何知道网卡收到数据的,这就涉及到网卡和内核的交互方式: 轮询(poll):内核周期性的检查网卡,查看是否收 ...
- kernel笔记——网络收发包流程
本文将介绍网络连接建立的过程.收发包流程,以及其中应用层.tcp层.ip层.设备层和驱动层各层发挥的作用. 应用层 对于使用socket进行网络连接的服务器端程序,我们会先调用socket函数创建一个 ...
- linux网络协议栈--路由流程分析
转:http://blog.csdn.net/hsly_support/article/details/8797976 来吧,路由 路由是网络的核心,是linux网络协议栈的核心,我们找个入口进去看看 ...
- Linux网络数据包的揭秘以及常见的调优方式总结
https://mp.weixin.qq.com/s/boRWlx1R7TX0NLuI2sZBfQ 作为业务 SRE,我们所运维的业务,常常以 Linux+TCP/UDP daemon 的形式对外提供 ...
- Linux网络 - 数据包的接收过程【转】
转自:https://segmentfault.com/a/1190000008836467 本文将介绍在Linux系统中,数据包是如何一步一步从网卡传到进程手中的. 如果英文没有问题,强烈建议阅读后 ...
- [转]Linux网络 - 数据包的接收过程
转, 原文: https://segmentfault.com/a/1190000008836467 ------------------------------------------------- ...
- [转]Linux网络 - 数据包的发送过程
转, 原文:https://segmentfault.com/a/1190000008926093 -------------------------------------------------- ...
- Linux网络 - 数据包的接收过程(转)
https://segmentfault.com/a/1190000008836467
- Linux内核中网络数据包的接收-第一部分 概念和框架
与网络数据包的发送不同,网络收包是异步的的.由于你不确定谁会在什么时候突然发一个网络包给你.因此这个网络收包逻辑事实上包括两件事:1.数据包到来后的通知2.收到通知并从数据包中获取数据这两件事发生在协 ...
- LINUX下的远端主机登入 校园网络注册 网络数据包转发和捕获
第一部分:LINUX 下的远端主机登入和校园网注册 校园网内目的主机远程管理登入程序 本程序为校园网内远程登入,管理功能,该程序分服务器端和客户端两部分:服务器端为remote_server_udp. ...
随机推荐
- c#|创建一个简单的窗体项目
- MDI窗体,打开子窗口的时候关闭其他子窗口及去除MainMenuStrip上自动产生的图标
去除MDI子窗体最大化后在MainMenuStrip上自动产生的图标和最大化.最小化以及关闭按钮在MainMenuStrip的ItemAdded事件中添加代码如下: 1 private void me ...
- C++学习笔记二:变量与数据类型(整型)
1.int(整型数据): 1.1 进制的表示:十进制,八进制,16进制,二进制 int number1 = 15; // Decimal int number2 = 017; // Octal int ...
- Cocos内存管理解析 CCRef/retain/release/autorelease
Cocos内存管理源码(autorelease解析) 背景 这段时间在做项目的时候,需求需要往spine动作的挂点上绑定按钮节点,由于按钮在编辑器中是加在已有节点上的,所以在往spine上添加挂点时, ...
- Chrome扩展的核心:manifest 文件(下)
大家好,我是 dom 哥.这是我关于 Chrome 扩展开发的系列文章,感兴趣的可以 点个小星星. 在上篇和中篇中已经完成了对 manifest 文件中以下字段的解释: "manifest_ ...
- JVM整理笔记
1.JVM位置 JVM是作用在操作系统之上的,它与硬件没有直接的交互 2.JVM体系结构 3.类装载器ClassLoader 类装载器:负责加载class文件,class文件在文件开头有特定的文件标示 ...
- 在arm架构的银河麒麟系统部署Nginx
以下是在arm架构的银河麒麟系统上部署Nginx的详细步骤: 1. 创建文件夹 首先,在合适的位置创建必要的文件夹.在本例中,我们将创建/opt/nginx和/usr/src/nginx两个文件夹. ...
- 数字孪生和GIS的融合能够为智慧水务带来什么帮助?
数字孪生和地理信息系统(GIS)的融合在智慧水务领域有着重要的应用前景.让我们一起探讨数字孪生和GIS如何合作,为智慧水务系统带来了哪些帮助. GIS系统提供了准确的地理数据,包括水资源.管道网络.水 ...
- C#Socket编程详解(一)TCP与UDP简介
一.TCP与UDP(转载) 1.TCP 1.1 定义 TCP(TransmissionControl Protocol)传输控制协议. 是一种可靠的.面向连接的协议(eg:打电话).传输效率低全双工通 ...
- 使用Bot Framework建立你的第一个聊天机器人
今天微软给我推送了一个邮件,大概是微软近期开发了很多人工智能相关的API,无意中看到了Bot Framework,就点进去看了看似乎还蛮有意思的.于是准备搭建一个环境试试. 第一步需要下载Bot Ap ...