Linux 系统是如何收发网络包的?(计算机网络)

news2024/11/17 13:46:45

一、Linux 网络协议栈

如下是TCP/IP四层网络模型,实际上Linux 网络协议栈与它相似

4612b18f676c49849bdbe893240c897a.png

 下图是Linux 网络协议栈

ca32350c57b6402e8d01911cad461b16.png

 二、Linux 接收网络包的流程

1.网卡是计算机里的一个硬件,专门负责接收和发送网络包,当网卡接收到一个网络包后,会通过 DMA (Direct Memory Access,直接内存访问)技术,将网络包写入到指定的内存地址,也就是写入到 Ring Buffer (环形缓冲区),接着就会告诉操作系统这个网络包已经到达。

2.告诉操作系统这个网络包已经到达最简单的一种方式就是触发中断,也就是每当网卡收到一个网络包,就触发一个中断告诉操作系统,但一旦网络包多起来,不停的打断会影响系统的整体效率。

为了解决频繁中断带来的性能开销,Linux 内核在 2.6 版本中引入了 NAPI 机制,它是混合「中断和轮询」的方式来接收网络包,它的核心概念就是不采用中断的方式读取数据,而是首先采用中断唤醒数据接收的服务程序,然后 poll 的方法来轮询数据。

因此,当有网络包到达时,会通过 DMA 技术,将网络包写入到指定的内存地址,接着网卡向 CPU 发起硬件中断,当 CPU 收到硬件中断请求后,根据中断表,调用已经注册的中断处理函数。

硬件中断处理函数会做如下的事情:

  • 需要先「暂时屏蔽中断」,表示已经知道内存中有数据了,告诉网卡下次再收到数据包直接写内存就可以了,不要再通知 CPU 了,这样可以提高效率,避免 CPU 不停的被中断。
  • 接着,发起「软中断」,然后恢复刚才屏蔽的中断。

至此,硬件中断处理函数的工作就已经完成。

(硬件中断处理函数做的事情很少,主要耗时的工作都交给软中断处理函数了。)

3.内核中的 ksoftirqd 线程专门负责软中断的处理,当 ksoftirqd 内核线程收到软中断后,就会来轮询处理数据。

ksoftirqd 线程会从 Ring Buffer 中获取一个数据帧,用 sk_buff (socket_buffer,一种数据结构)表示,从而可以作为一个网络包交给网络协议栈进行逐层处理。

4.首先,会进入到网络接口层,在这一层会检查报文的合法性,如果不合法则丢弃,合法则会找出该网络包的上层协议的类型,比如是 IPv4,还是 IPv6,接着再去掉帧头和帧尾,然后交给网络层。

5.到了网络层,则取出 IP 包,判断网络包下一步的走向,比如是交给上层处理还是转发出去。当确认这个网络包要发送给本机后,就会从 IP 头里看看上一层协议的类型是 TCP 还是 UDP,接着去掉 IP 头,然后交给传输层。

6.传输层取出 TCP 头或 UDP 头,根据四元组「源 IP、目的 IP、源端口、目的端口」 作为标识,找出对应的 Socket,并把数据放到 Socket 的接收缓冲区。

7.最后,应用层程序调用 Socket 接口,将内核的 Socket 接收缓冲区的数据「拷贝」到应用层的缓冲区,然后唤醒用户进程。

下图左边部分看到网络包接收的流程,右边部分刚好反过来,它是网络包发送的流程。

3ed95fe50f3542f18528387a6f915601.png

 

三、Linux 发送网络包的流程

发包是收包的逆过程。

1.首先,应用程序会调用 Socket 发送数据包的接口,由于这个是系统调用,所以会从用户态陷入到内核态中的 Socket 层,内核会申请一个内核态的 sk_buff 内存,将用户待发送的数据拷贝到 sk_buff 内存,并将其加入到发送缓冲区

2.接下来,网络协议栈从 Socket 发送缓冲区中取出 sk_buff,并按照 TCP/IP 协议栈从上到下逐层处理。

如果使用的是 TCP 传输协议发送数据,那么先拷贝一个新的 sk_buff 副本 ,这是因为 sk_buff 后续在调用网络层,最后到达网卡发送完成的时候,这个 sk_buff 会被释放掉。而 TCP 协议是支持丢失重传的,在收到对方的 ACK(acknowledgement,代表接收方已接收并理解发送的信息) 之前,这个 sk_buff 不能被删除。所以实际上网卡发送的是 sk_buff 的一个拷贝。

接着,对 sk_buff 填充 TCP 头。

sk_buff 可以表示各个层的数据包,在应用层数据包叫 data,在 TCP 层我们称为 segment,在 IP 层我们叫 packet,在数据链路层称为 frame。

3.然后交给网络层,在网络层里会做这些工作:选取路由(确认下一跳的 IP)、填充 IP 头、netfilter 过滤、对超过 MTU 大小的数据包进行分片。处理完这些工作后会交给网络接口层处理。

4.网络接口层会通过 ARP 协议获得下一跳的 MAC 地址,然后对 sk_buff 填充帧头和帧尾,接着将 sk_buff 放到网卡的发送队列中。

5.这一些工作准备好后,会触发「软中断」告诉网卡驱动程序,这里有新的网络包需要发送,驱动程序会从发送队列中读取 sk_buff,将这个 sk_buff 挂到 RingBuffer 中,接着将 sk_buff 数据映射到网卡可访问的内存 DMA 区域,最后触发真实的发送。

6.当数据发送完成以后,还需要清理内存。发送完成的时候,网卡设备会触发一个硬中断来释放内存,主要是释放 sk_buff 内存和清理 RingBuffer 内存。

最后,当收到这个 TCP 报文的 ACK 应答时,传输层就会释放原始的 sk_buff 。

 

发送网络数据的时候,涉及几次内存拷贝操作?

第一次:调用发送数据的系统调用的时候,内核会申请一个内核态的 sk_buff 内存,将用户待发送的数据拷贝到 sk_buff 内存,并将其加入到发送缓冲区。

第二次:在使用 TCP 传输协议的情况下,从传输层进入网络层的时候,每一个 sk_buff 都会被克隆一个新的副本出来。副本 sk_buff 会被送往网络层,等它发送完的时候就会释放掉,然后原始的 sk_buff 还保留在传输层,等收到这个数据包的 ACK 时,才会释放原始的 sk_buff 。

第三次:当 IP 层发现 sk_buff 大于 MTU 时才需要进行。会再申请额外的 sk_buff,并将原来的 sk_buff 拷贝为多个小的 sk_buff。

 

总结

1.Linux收包:网卡收到网络包之后会通过 DMA 技术将网络包写入RingBuffer环形缓冲区,接着网卡向 CPU 发起硬件中断,当 CPU 收到硬件中断请求后,根据中断表,调用已经注册的中断处理函数。硬件中断函数先暂时屏蔽中断(下一次就直接写入内存而不通知CPU),再发起软中断,软中断调用ksoftirqd 线程从 Ring Buffer 中获取一个数据帧,用sk_buffer(socket_buffer,一种数据类型)表示,然后由网络协议栈处理,先进入网络接口层,去掉帧头和帧尾,再进入网络层,去掉IP头部,接着进入传输层去掉TCP 头或 UDP 头,根据四元组「源 IP、目的 IP、源端口、目的端口」 作为标识,找出对应的 Socket,并把数据放到 Socket 的接收缓冲区。最后,应用层程序调用 Socket 接口,将缓冲区的数据「拷贝」到应用层的缓冲区,然后唤醒用户进程。

 

2.Linux发包:应用程序会调用 Socket 发送数据包的接口,内核会申请一个 sk_buff 内存,将用户待发送的数据拷贝到 sk_buff 内存,并将其加入到发送缓冲区,然后网络协议栈从 Socket 发送缓冲区中取出 sk_buff,先进入传输层,如果使用的是 TCP 协议发送数据,会先拷贝一个sk_buff 副本 ,然后为副本TCP头部,本来的sk_buff就留在传输层,接着sk_buff副本在网络层添加上IP头部,然后在网络接口层添加上MAC头部以及帧头和帧尾,最后将 sk_buff 放到网卡的发送队列中。随后会触发「软中断」告诉网卡驱动程序,驱动程序会从发送队列中读取 sk_buff,将这个 sk_buff 挂到 RingBuffer 中,接着将 sk_buff 数据映射到网卡可访问的内存 DMA 区域,最后触发真实的发送。当数据发送完成以后,网卡会触发一个硬中断来释放内存,主要是释放 sk_buff 内存和清理 RingBuffer 内存。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1276658.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Leetcode 剑指 Offer II 055. 二叉搜索树迭代器

题目难度: 中等 原题链接 今天继续更新 Leetcode 的剑指 Offer(专项突击版)系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~ 题目描述 实现一个二叉搜索树迭代器类 BSTIterator ,表示一个按…

华为云obs在java中的使用

1、申请obs服务。 申请完成后,会获得以下几个配置信息: AK"****************************"; SK"******************************************************"; ENDPOINT"obs.*************************"; BUCKET_NAME&q…

麻吉POS集成:如何无代码开发实现电商平台和CRM系统的高效连接

麻吉POS集成的前沿技术:无代码开发 在竞争激烈的电商市场中,商家们急需一种高效且易于操作的技术手段来实现系统间的快速连接与集成。麻吉POS以其前沿的无代码开发技术,让这一需求成为可能。无代码开发是一种允许用户通过图形用户界面进行编…

STDP突触设计(一)

最近看了很多的STDP的工作原理,比如 SNN系列|学习算法篇(6)脉冲时序依赖可塑性STDP_脉冲时间依赖可塑性-CSDN博客 Spike-Timing Dependent Plasticity - Scholarpedia 这两篇的介绍的代码非常的详细,我就不再赘述了 我设计的STDP的是基于…

了解 ignore_above 参数对 Elasticsearch 中磁盘使用的影响

在 Elasticsearch 中,ignore_above 参数允许你忽略(而不是索引)长于指定长度的字符串。 这对于限制字段的大小以避免性能问题很有用。 在本文中,我们将探讨 “ignore_above” 参数如何影响 Elasticsearch 中字段的大小&#xff0c…

力扣225-用队列实现栈

文章目录 力扣225-用队列实现栈示例代码实现总结收获 力扣225-用队列实现栈 示例 代码实现 class MyStack {Queue<Integer>queue1;Queue<Integer>queue2;public MyStack() {queue1new LinkedList<Integer>();queue2new LinkedList<Integer>();}public…

【C/PTA —— 13.指针2(课内实践)】

C/PTA —— 13.指针2&#xff08;课内实践&#xff09; 一.函数题6-1使用函数实现字符串部分复制6-2 拆分实数的整数部分和小数部分6-3 存在感 二.编程题7-1 单词反转 一.函数题 6-1使用函数实现字符串部分复制 void strmcpy(char* t, int m, char* s) {int len 0;char* ret …

基于瑞芯微rk3588+寒武纪 | 38TOPS INT8算力的AI边缘计算盒子,智能安防、智慧工地、智慧城管、智慧油站

边缘计算盒子 瑞芯微rk3588寒武纪 | 38TOPS INT8算力 ● 采用 Big-Little 大小核架构&#xff0c;搭载四核 A76四核 A55&#xff0c;CPU主频高达 2.4GHz &#xff0c;提供1MB L2 Cache 和 3MB L3 &#xff0c;Cache提供更强的 CPU 运算能力。 ● 高性能四核 Mali-G610 GPU&a…

Linux常用命令——awk命令

在线Linux命令查询工具 awk 文本和数据进行处理的编程语言 补充说明 awk是一种编程语言&#xff0c;用于在linux/unix下对文本和数据进行处理。数据可以来自标准输入(stdin)、一个或多个文件&#xff0c;或其它命令的输出。它支持用户自定义函数和动态正则表达式等先进功能…

leetcode刷题详解—— 环形子数组的最大和

1. 题目链接&#xff1a;918. 环形子数组的最大和 2. 题目描述&#xff1a; 给定一个长度为 n 的环形整数数组 nums &#xff0c;返回 nums 的非空 子数组 的最大可能和 。 环形数组 意味着数组的末端将会与开头相连呈环状。形式上&#xff0c; nums[i] 的下一个元素是 nums[(…

蓝桥杯第198题 人物相关性分析 C++ 模拟 字符串 双指针

题目 思路和解题方法 程序首先定义了一个函数check&#xff0c;用于判断一个字符是否为字母。接下来&#xff0c;程序读取输入的整数k和一行字符串str。定义了两个空的向量a和b&#xff0c;用于存储满足条件的子串的起始位置。使用for循环遍历字符串str的每个字符&#xff0c;检…

数据结构:带头双向循环链表的实现

引言 单链表存在缺陷&#xff1a;需要从头开始找前一个节点 解决方法&#xff1a;双向链表 链表的结构&#xff08;8种&#xff09;&#xff1a; 1. 单向&#xff0c;双向 2. 带头、不带头 带头即为带哨兵位的头节点&#xff0c;第一个节点不存储有效数据。带头节点&#…

计算机网络——数据链路层-封装成帧(帧定界、透明传输-字节填充,比特填充、MTU)

目录 介绍 帧定界 PPP帧 以太网帧 透明传输 字节填充&#xff08;字符填充&#xff09; 比特填充 比特填充习题 MTU 介绍 所谓封装成帧&#xff0c;就是指数据链路层给上层交付下来的协议数据单元添加帧头和帧尾&#xff0c;使之成为帧。 例如下图所示&#xff1a; …

SQL注入漏洞的检测及防御方法

SQL注入&#xff08;SQL Injection&#xff09;是一种广泛存在于Web应用程序中的严重安全漏洞&#xff0c;它允许攻击者在不得到授权的情况下访问、修改或删除数据库中的数据。这是一种常见的攻击方式&#xff0c;因此数据库开发者、Web开发者和安全专业人员需要了解它&#xf…

主动学习入门Week1

主动学习&#xff08;Active Learning&#xff09; 介绍实例详解模型分类基本查询策略经典方法应用方向引用 介绍 主动学习是一种通过主动选择最有价值的样本进行标注的机器学习或人工智能方法。其目的是使用尽可能少的、高质量的样本标注使模型达到尽可能好的性能。也就是说&…

Docker容器间网络共享

Docker容器间网络共享 1、新建网络2、容器绑定网卡3、验证 Docker环境中为了一套应用部署多个环境、并且不修改配置文件的情况下&#xff0c;做到一键部署。要求不同容器直接的网络交互&#xff0c;使用容器名称。 网络相关常用命令 #查看网络内部信息docker network inspect b…

scrapy的建模及管道的使用

一、数据建模 通常在做项目的过程中&#xff0c;在items.py中进行数据建模 为什么建模 定义item即提前规划好哪些字段需要抓&#xff0c;防止手误&#xff0c;因为定义好之后&#xff0c;在运行过程中&#xff0c;系统会自动检查&#xff0c;配合注释一起可以清晰的知道要抓…

熬夜会秃头——beta冲刺Day4

这个作业属于哪个课程2301-计算机学院-软件工程社区-CSDN社区云这个作业要求在哪里团队作业—beta冲刺事后诸葛亮-CSDN社区这个作业的目标记录beta冲刺Day4团队名称熬夜会秃头团队置顶集合随笔链接熬夜会秃头——Beta冲刺置顶随笔-CSDN社区 一、团队成员会议总结 1、成员工作进…

scrapy框架

scrapy文档 文档链接 安装指南 — Scrapy 2.5.0 文档 (osgeo.cn)https://www.osgeo.cn/scrapy/intro/install.html 创建scrapy框架 0.介绍&#xff1a; scrapy是异步非阻塞框架 异步&#xff1a;一个主线程有20个任务&#xff0c;可以来回切换 非阻塞&#xff1a;运行的程序不需…

vue项目引入外部vue项目页面

vue项目使用iframe引入外部项目页面 vue项目引入外部项目页面有好几种方法&#xff0c;我这种使用的是iframe 示例 <template><div><iframe:src"userPageUrl"width"100%"height"800"frameborder"0"allowfullscreen&…