-
2021-12-07 19:31:56
网卡是如何接收数据的
预备知识
硬件中断:一般是由硬件产生的,当外设侦测到变化时,告知中断控制器,中断控制器通过CPU或内存的中断脚通知CPU,然后完成程序计数器及堆栈寄存器的现场保存,进行上下文切换,并根据中断向量调用对应的中断处理程序进行中断处理。
软中断:一般是软件程序直接以一个CPU中断指令的形式指示CPU进行程序计数器及堆栈寄存器的现场保存工作,并进行上下文切换,然后调用相应的软中断处理程序进行中断处理(即系统调用)。
硬件中断直接以硬件的方式引发,响应速度快;软中断以软件指令的方式触发,适合于对响应速度要求不是特别严格的场景。
Q:为什么要使用软中断?
A:由于硬件中断处理程序执行的过程中不能被中断,所以如果它执行时间过长,会导致CPU没法响应其它硬件的中断,于是内核引入软中断,这样可以将硬中断处理函数中耗时的部分移到软中断处理函数里面来慢慢处理。网卡接收数据的步骤
- 网卡接收到一个数据包,根据目的MAC地址判断是不是发给自己的,如果不是则将丢弃该数据包;
- 网卡通过DMA技术将数据包拷贝内存中的RingBuffer(一个环形缓冲区)中;
- 网卡触发硬件中断,通知CPU接收到了数据包;
- CPU根据中断处理表,调用已注册的中断处理函数,该函数又会去调用网卡的驱动程序中相应的函数;
- 驱动程序先屏蔽网卡的中断,表示驱动程序已经知道内存中有数据了,告诉网卡下次再收到数据包直接写内存就可以了,不要再通知CPU了,这样可以提高效率,避免CPU不停的被中断;
- 启动软中断,这步结束后,硬件中断处理函数就结束返回了;
- 内核中的ksoftirqd进程专门负责软中断的处理,当它收到软中断后,就会调用相应软中断所对应的处理函数,对于上面第6步中是网卡驱动模块抛出的软中断,ksoftirqd会调用网络模块的net_rx_action函数,该函数调用网卡驱动里的poll函数来一个一个的处理数据包;
- 将数据包沿协议栈向上传递,逐层处理;
- 待内存中的所有数据包被处理完成后(即poll函数执行完成),启用网卡的硬件中断,这样下次网卡再收到数据的时候就会通知CPU;
更多相关内容 -
Linux网卡接收数据包过程图详细总结
2022-03-27 17:02:00简介 本文对代码的详细实现过程不做过多的讲解,重点让读者熟悉...1、接收数据前的准备工作 1)网络子系统的初始化; 2)协议栈的注册; 3)网卡驱动的初始化; 4)启动网卡; 2、接收和传输网络数据: 一、接简介
本文对代码的详细实现过程不做过多的讲解,重点让读者熟悉数据的接收过程,如需进一步熟悉源码,可根据下面的链接做进一步学习:
1、网卡驱动源码分析
2、网卡结构和基础知识详解收包过程总览
从TCP/IP网络分层模型中可以清楚当数据帧从网卡(物理层)接收到客户端(应用层)收到数据包的整个过程。
通过网卡进行网络数据接收一般要经历下面两个过程:
1、接收数据前的准备工作1)网络子系统的初始化; 2)协议栈的注册; 3)网卡驱动的初始化; 4)启动网卡;
2、接收和传输网络数据:
一、接收数据前的准备工作
1、网络子系统的初始化:
linux系统在加载内核到内存中后会进行大量的初始化工作,其中初始化各个内核子系统都是通过调用subsys_initcall函数来实现,具体初始化网络子系统主要有以下三个方面:
1、申请一个softnet_data数据结构,在这个数据结构里的poll_list是等待驱动程序将其poll函数注册进来;
2、将每一种软中断都注册一个处理函数;
3、将软中断和对应的中断处理函数都放在softirq_vec数组中。
具体流程如下图:
2、协议栈的注册:
内核实现了网络层的ip协议,也实现了传输层的tcp协议和udp协议。 这些协议对应的实现函数分别是ip_rcv(),tcp_v4_rcv()和upd_rcv()。和我们平时写代码的方式不一样的是,内核是通过注册的方式来实现的,这些函数主要是将从物理层ring_buffer写到skb_buffer中的数据做进一步的解析处理并传至上一层;
3、网卡驱动的初始化:
每一个驱动程序(不仅仅只是网卡驱动)会使用 module_init 向内核注册一个初始化函数,当驱动被加载时,内核会调用这个函数去完成驱动的注册、网卡设备的初始化和网卡驱动和设备的匹配等过程。
4、启动网卡:
当完成前面的注册和初始化过程后,网卡就可以通过命令ifconfig eth0 up进行打开,网卡的打开过程就是通过调用驱动注册的open函数去实现的,igb_open函数执行过程中,还会启用msix机制和NAPI机制,其中msix机制可以具体理解为中断管理机制,通过该函数调用最终的中断处理函数,而NAPI机制是与最开始的软中断调用的poll函数相关的,具体流程如下图:
二、接收和传输网络数据
整体过程梳理:
首先网卡接收网络数据帧并通过DMA保存在ring_buffer中,此时网卡通过硬中断通知cpu接收到数据,cpu会生成一个软中断,然后通过创建一个ksoftirqd内核线程处理软中断,然后通过NAPI机制调用驱动注册的poll函数,poll函数会将ring_buffer中的数据保存在skb_buffer中然后调用netif_receive_skb()将数据发送到内核协议栈进行处理,也就会调用最开始协议栈注册的函数。
具体流程如下图:
下面具体解析一个硬中断和ksoftirqd内核线程处理软中断:
硬中断:
首先当数据帧从网线到达网卡上的时候,第一站是网卡的接收队列。网卡在分配给自己的RingBuffer中寻找可用的内存位置,找到后DMA引擎会把数据DMA到网卡之前关联的内存里,这个时候CPU都是无感的。当DMA操作完成以后,网卡会像CPU发起一个硬中断,通知CPU有数据到达,具体流程如下图:
ksoftirqd内核线程处理软中断:
网络子系统初始化部分, 我们看到我们为NET_RX_SOFTIRQ注册了处理函数net_rx_action,函数就是在这里被调用执行的。
注意:
硬中断中设置软中断标记,和ksoftirq的判断是否有软中断到达,都是基于smp_processor_id()的。这意味着只要硬中断在哪个CPU上被响应,那么软中断也是在这个CPU上处理的。所以说,如果你发现你的Linux软中断CPU消耗都集中在一个核上的话,做法是要把调整硬中断的CPU亲和性,来将硬中断打散到不通的CPU核上去。
最后讲述协议栈的处理过程:
在ksoftirqd内核线程处理软中断最终调用的netif_receive_skb函数会根据包的协议,假如是upd包,会将包依次送到ip_rcv(),upd_rcv()协议处理函数中进行处理,最终将数据包传至传输层,函数处理流程如下:
总结
接收数据前准备工作:
1、创建ksoftirqd线程,为它设置好它自己的线程函数,后面就指望着它来处理软中断;
2、协议栈注册,linux要实现许多协议,比如arp,icmp,ip,udp,tcp,每一个协议都会将自己的处理函数注册一下,方便包来了迅速找到对应的处理函数;
3、网卡驱动初始化,每个驱动都有一个初始化函数,内核会让驱动也初始化一下。在这个初始化过程中,把自己的DMA准备好,把NAPI的poll函数地址告诉内核;
4、启动网卡,分配RX,TX队列,注册中断对应的处理函数;
以上是内核准备收包之前的重要工作,当上面都ready之后,就可以打开硬中断,等待数据包的到来了。数据接收:
1、网卡将数据帧DMA到内存的RingBuffer中,然后向CPU发起中断通知;
2、CPU响应中断请求,调用网卡启动时注册的中断处理函数;
3、中断处理函数发起软中断请求;
4、内核线程ksoftirqd线程发现有软中断请求到来,先关闭硬中断;
5、ksoftirqd线程开始调用驱动的poll函数收包;
5、poll函数将受到的包送到协议栈注册的ip_rcv函数中;
6、ip_rcv函数再讲包送到upd_rcv函数中(对于tcp包就送到tcp_rcv)。读者有兴趣可以通过下面的链接对网卡其他的知识进行学习:
1、网卡驱动源码分析
2、网卡结构和基础知识详解 -
网卡接收和发送数据包的过程
2012-06-15 13:02:47介绍网卡的接受和发送过程,对网卡驱动编写提供参考 -
计算机网络——数据从网卡到应用的过程
2020-09-29 09:55:16数据从网卡到应用的过程 数据从网卡到应用的过程1、过程概述2、网卡3、网卡驱动 数据从网卡到应用的过程 1、过程概述 假设一个HTTP请求的数据到达网卡,那数据是如何被层层处理并到达应用呢? 2、网卡 网卡(Network...数据从网卡到应用的过程
数据从网卡到应用的过程
1、过程概述
假设一个HTTP请求的数据到达网卡,那数据是如何被层层处理并到达应用呢?
2、网卡
网卡(Network Adapter),也称网络适配器,是一个 硬件设备,有全球唯一的 MAC(Media Access Control)地址,MAC地址在网卡生产时就被烧制在ROM中,网卡初始化时恢复到计算机中。
- 1、网卡收到的数据是 光信号或电信号,然后将其还原成 数字信息(1和0组成)。
下图是还原的数字信息结构。- 根据 FCS(帧校验序列,Frame Check Sequence)校验数据,判断数据在传输过程是否因噪音等影响导致信号失真,从而导致数据错误,需要丢弃这种无效的数据包。
- 然后 检查 数据包中MAC头部中的接收方的MAC地址,若不是发给自己,则丢弃数据包;若数据包是发给自己,则将数字信息保存到网卡内部缓冲区。
以上过程网卡自行搞定,不需要CPU参与,CPU也不知道数据包的到达。
3、网卡驱动
硬件需要驱动程序来控制,就像电脑需要操作系统一样,而
网卡驱动就是CPU控制和使用网卡的程序
。网卡处理完数字信号后,接下来的数据接收需要CPU参与,此时
网卡通过中断将数据包达到的事件通知给CPU
。接着,CPU暂停手头工作,开始用网卡驱动来干活。- 从网卡缓冲区读取接收到的数据
- 根据MAC头部的以太类型字段判断协议种类并调用处理该协议的软件(即协议栈)
通常我们接触的以太类型是 IP协议,因此会调用TCP/IP协议栈来处理。
- 1、网卡驱动程序提取这个帧的全部内容,去掉以太网的帧头,然后向上传递给IP层
- 2、IP层接收到包已经,继续去掉IP头的内容,然后交给TCP层
- 3、TCP层根据TCP协议定义的格式,继续解包,read()从socket buffer读取数据,然后传递给应用层
- 4、应用层根据TCP层传来的数据,按照对应的应用层来分析包
4、物理网卡收到数据包的处理流程
接收数据包是一个复杂的过程,涉及很多底层的技术细节,但大致需要以下几个步骤:
-
1、网卡收到数据包。
-
2、将数据包从网卡硬件缓存转移到服务器内存中。
-
3、通知内核处理。
-
4、经过TCP/IP协议逐层处理。
-
5、应用程序通过read()从socket buffer读取数据。
物理网卡收到数据包的处理流程如上图所示,详细步骤如下: -
1、网卡收到数据包,先将高低电平转换到网卡fifo存储,网卡申请ring buffer的描述,根据描述找到具体的物理地址,从fifo队列物理网卡会使用DMA将数据包写到了该物理地址,,其实就是skb_buffer中.
-
2、这个时候数据包已经被转移到skb_buffer中,因为是DMA写入,内核并没有监控数据包写入情况,这时候NIC触发一个硬中断,每一个硬件中断会对应一个中断号,且指定一个vCPU来处理,如上图vcpu2收到了该硬件中断.
-
3、硬件中断的中断处理程序,调用驱动程序完成,a.启动软中断
-
4、硬中断触发的驱动程序会禁用网卡硬中断,其实这时候意思是告诉NIC,再来数据不用触发硬中断了,把数据DMA拷入系统内存即可
5、硬中断触发的驱动程序会启动软中断,启用软中断目的是将数据包后续处理流程交给软中断慢慢处理,这个时候退出硬件中断了,但是注意和网络有关的硬中断,要等到后续开启硬中断后,才有机会再次被触发
-
6、NAPI触发软中断,触发napi系统
-
7、消耗ringbuffer指向的skb_buffer
-
8、NAPI循环处理ringbuffer数据,处理完成
-
9、启动网络硬件中断,有数据来时候就可以继续触发硬件中断,继续通知CPU来消耗数据包.
其实上述过程过程简单描述为:网卡收到数据包,DMA到内核内存,中断通知内核数据有了,内核按轮次处理消耗数据包,一轮处理完成后,开启硬中断。其核心就是网卡和内核其实是生产和消费模型,网卡生产,内核负责消费,生产者需要通知消费者消费;如果生产过快会产生丢包,如果消费过慢也会产生问题。也就说在高流量压力情况下,只有生产消费优化后,消费能力够快,此生产消费关系才可以正常维持,所以如果物理接口有丢包计数时候,未必是网卡存在问题,也可能是内核消费的太慢。
如何将网卡收到的数据写入到内核内存?
NIC在接收到数据包之后,首先需要将数据同步到内核中,这中间的桥梁是rx ring buffer。它是由NIC和驱动程序共享的一片区域,事实上,rx ring buffer存储的并不是实际的packet数据,而是一个描述符,这个描述符指向了它真正的存储地址,具体流程如下:
-
1、驱动在内存中分配一片缓冲区用来接收数据包,叫做sk_buffer;
-
2、将上述缓冲区的地址和大小(即接收描述符),加入到rx ring buffer。描述符中的缓冲区地址是DMA使用的物理地址; 3、驱动通知网卡有一个新的描述符;
-
4、网卡从rx ring buffer中取出描述符,从而获知缓冲区的地址和大小;
-
5、网卡收到新的数据包;
-
6、网卡将新数据包通过DMA直接写到sk_buffer中。
当驱动处理速度跟不上网卡收包速度时,驱动来不及分配缓冲区,NIC接收到的数据包无法及时写到sk_buffer,就会产生堆积,当NIC内部缓冲区写满后,就会丢弃部分数据,引起丢包。这部分丢包为rx_fifo_errors,在 /proc/net/dev中体现为fifo字段增长,在ifconfig中体现为overruns指标增长。
参考
1、https://www.jianshu.com/p/e6162bc984c8
2、https://bbs.gameres.com/thread_842984_1_1.html -
网卡适配器收发数据帧流程
2021-08-20 11:04:40网络设配器的发包过程 sk_buff(Socket Buffer) DMA 与 Buffer descriptor 参考文档 前文列表 《Linux 内核网络协议栈》 网卡的工作原理 Linux 操作系统的功能可以概括为进程管理、内存管理、文件系统管理...目录
文章目录
- 目录
- 前文列表
- 网卡的工作原理
- 网卡与网卡适配器
- 网卡的组成
- CPU 与网卡的协同
- 网络设配器的收包流程
- 网络设配器的发包过程
- sk_buff(Socket Buffer)
- DMA 与 Buffer descriptor
- 参考文档
前文列表
网卡的工作原理
Linux 操作系统的功能可以概括为进程管理、内存管理、文件系统管理、设备管理和计算机网络等几部分。所有的操作系统执行最终都可以映射到对物理设备的操作。除去对 CPU、内存等处理机设备的操作之外,操作系统对其他外部设备的操作都通过专门的驱动程序完成。操作系统的每种外设在内核中都必须有对应的设备驱动程序对其进行处理。所以分析网卡的工作原理即是分析网卡的驱动程序。
网络是独立的一个模块。为了屏蔽物理网络设备的多样性,Linux 内核协议栈实现中,对底层物理设备进行了抽象并定义了一个统一的概念,称之为 Socket 接口。所有对网络硬件的访问都是通过接口完成的,接口提供了一个抽象而统一的操作集合来处理基本数据报文的发送和接收。一个网络接口就被看作是一个发送和接收数据包的实体。
对于每个网络接口,都用一个 net_device 的数据结构来表示。net_device 中有很多提供系统访问和协议层调用的设备方法,包括提供设备初始化和往系统注册用的 init 函数,打开和关闭网络设备的 open 和 stop 函数,处理数据包发送的函数 hard_start_xmit,以及中断处理函数。
所有被发送和接收的数据报文都用 sk_buff 结构表示。要发送数据时,内核网络协议栈将根据系统路由表选择相应的网络接口进行数据传输;当接收数据包时,通过驱动程序注册的中断服务程序进行数据的接口处理。
网卡与网卡适配器
我们知道计算机的输入输出系统由外部硬件设备(e.g. 网卡)及其与主机之间的控制部件(Controller)所构成,其中控制部件常被称为设备控制器、设备适配器、设备驱动或 I/O 接口,主要负责控制并实现主机与外设之间的数据传输。
首先明确一下术语,在本文中,网卡指物理网络设备卡、网卡适配器指网卡设备控制器,即安装在操作系统上的网络设备驱动。
网络设备驱动在 Linux 内核中是以内核模块的形式存在的。所以对于网卡驱动的初始化,同样需要提供一个内核模块初始化函数来完成的,初始化网络设备的硬件寄存器、配置 DMA 以及初始化相关内核变量等。
设备初始化函数在内核模块被加载时调用,包括:
- 初始化 PHY 模块,包括设置双工/半双工运行模式、设备运行速率和自协商模式等。
- 初始化 MAC 模块,包括设置设备接口模式等。
- 初始化 DMA 模块,包括建立 BD(Buffer descriptor)表、设置 BD 属性以及给 BD 分配缓存等。
网卡的组成
网卡工作在物理层和数据链路层,主要由 PHY/MAC 芯片、Tx/Rx FIFO、DMA 等组成,其中网线通过变压器接 PHY 芯片、PHY 芯片通过 MII 接 MAC 芯片、MAC 芯片接 PCI 总线。
- PHY 芯片主要负责:CSMA/CD、模数转换、编解码、串并转换。
- MAC 芯片主要负责:
- 比特流和数据帧的转换(7 字节的前导码 Preamble 和 1 字节的帧首定界符 SFD)
- CRC 校验
- Packet Filtering(L2 Filtering、VLAN Filtering、Manageability/Host Filtering)
- Tx/Rx FIFO:Tx 表示发送(Transport),Rx 是接收(Receive)。
- DMA(Direct Memory Access):直接存储器存取 I/O 模块。
CPU 与网卡的协同
以往,从网卡的 I/O 区域,包括 I/O 寄存器或 I/O 内存中读取数据,这都要 CPU 亲自去读,然后把数据放到 RAM 中,也就占用了 CPU 的运算资源。直到出现了 DMA 技术,其基本思想是外设和 RAM 之间开辟直接的数据传输通路。一般情况下,总线所有的工作周期(总线周期)都用于 CPU 执行程序。DMA 控制就是当外设完成数据 I/O 的准备工作之后,会占用总线的一个工作周期,和 RAM 直接交换数据。这个周期之后,CPU 又继续控制总线执行原程序。如此反复的,直到整个数据块的数据全部传输完毕,从而解放了 CPU。
- 首先,内核在 RAM 中为收发数据建立一个环形的缓冲队列,通常叫 DMA 环形缓冲区,又叫 BD(Buffer descriptor)表。
- 内核将这个缓冲区通过 DMA 映射,把这个队列交给网卡;
- 网卡收到数据,就直接放进这个环形缓冲区,也就是直接放进 RAM 了;
- 然后,网卡驱动向系统产生一个中断,内核收到这个中断,就取消 DMA 映射,这样,内核就直接从主内存中读取数据;
网络设配器的收包流程
- 网卡驱动申请 Rx descriptor ring,本质是一致性 DMA 内存,保存了若干的 descriptor。将 Rx descriptor ring 的总线地址写入网卡寄存器 RDBA。
- 网卡驱动为每个 descriptor 分配 skb_buff 数据缓存区,本质上是在内存中分配的一片缓冲区用来接收数据帧。将数据缓存区的总线地址保存到 descriptor。
- 网卡接收到高低电信号。
- PHY 芯片首先进行数模转换,即将电信号转换为比特流。
- MAC 芯片再将比特流转换为数据帧(Frame)。
- 网卡驱动将数据帧写入 Rx FIFO。
- 网卡驱动找到 Rx descriptor ring 中下一个将要使用的 descriptor。
- 网卡驱动使用 DMA 通过 PCI 总线将 Rx FIFO 中的数据包复制到 descriptor 保存的总线地址指向的数据缓存区中。其实就是复制到 skb_buff 中。
- 因为是 DMA 写入,所以内核并没有监控数据帧的写入情况。所以在复制完后,需要由网卡驱动启动硬中断通知 CPU 数据缓存区中已经有新的数据帧了。每一个硬件中断会对应一个中断号,CPU 执行硬下述中断函数。实际上,硬中断的中断处理程序,最终是通过调用网卡驱动程序来完成的。硬中断触发的驱动程序首先会暂时禁用网卡硬中断,意思是告诉网卡再来新的数据就先不要触发硬中断了,只需要把数据帧通过 DMA 拷入主存即可。
- NAPI(以 e1000 网卡为例):
e1000_intr() -> __napi_schedule() -> __raise_softirq_irqoff(NET_RX_SOFTIRQ)
- 非 NAPI(以 dm9000 网卡为例):
dm9000_interrupt() -> dm9000_rx() -> netif_rx() -> napi_schedule() -> __napi_schedule() -> __raise_softirq_irqoff(NET_RX_SOFTIRQ)
- NAPI(以 e1000 网卡为例):
- 硬中断后继续启动软中断,启用软中断目的是将数据帧的后续处理流程交给软中断处理程序异步的慢慢处理。此时网卡驱动就退出硬件中断了,其他外设可以继续调用操作系统的硬件中断。但网络 I/O 相关的硬中断,需要等到软中断处理完成并再次开启硬中断后,才能被再次触发。ksoftirqd 执行软中断函数
net_rx_action()
:- NAPI(以 e1000 网卡为例),触发
napi()
系统调用,napi()
逐一消耗 Rx Ring Buffer 指向的 skb_buff 中的数据包:net_rx_action() -> e1000_clean() -> e1000_clean_rx_irq() -> e1000_receive_skb() -> netif_receive_skb()
- 非 NAPI(以 dm9000 网卡为例):
net_rx_action() -> process_backlog() -> netif_receive_skb()
- NAPI(以 e1000 网卡为例),触发
- 网卡驱动通过
netif_receive_skb()
将 sk_buff 上送到协议栈。 - 重新开启网络 I/O 硬件中断,有新的数据帧到来时可以继续触发网络 I/O 硬件中断,继续通知 CPU 来消耗数据帧。
传统方式和 NAPI 方式
值得注意的是,传统收包是每个报文都触发中断,如果中断太频繁,CPU 就总是处理中断,其他任务无法得到调度,于是 NAPI(New API)收包方式出现了,其思路是采用「中断+轮询」的方式收包以提高吞吐。NAPI 收包需要网卡驱动支持,例如 Intel e1000 系列网卡。下图为传统方式和 NAPI 方式收包流程差异:
中断方式与轮询方式
Linux 内核在接收数据时有两种方式可供选择,一种是中断方式,另外一种是轮询方式。
从本质上来讲,中断,是一种电信号,当设备有某种事件发生的时候,它就会产生中断,通过总线把电信号发送给中断控制器,如果中断的线是激活的,中断控制器就把电信号发送给处理器的某个特定引脚。处理器于是立即停止自己正在做的事,跳到内存中内核设置的中断处理程序的入口点,进行中断处理。
使用中断方式,首先在使用该驱动之前,需要将该中断对应的中断类型号和中断处理程序注册进去。网卡驱动在初始化时会将具体的 xx_open 函数挂接在驱动的 open 接口上。网卡的中断一般会分为两种,一种是发送中断,另一种是接收中断。Linux 内核需要分别对这两种中断类型号进行注册。对于中断方式来说,由于每收到一个包都会产生一个中断,而处理器会迅速跳到中断服务程序中去处理收包,因此中断接收方式的实时性高,但如果遇到数据包流量很大的情况时,过多的中断会增加系统的负荷。
- 发送中断处理程序(xx_isr_tx)的工作主要是监控数据发送状态、更新数据发送统计等。
- 接收中断处理程序(xx_isr_rx)的工作主要是接收数据并传递给协议层、监控数据接收状态、更新数据接收统计等。如果采用轮询方式,就不需要使能网卡的中断状态,也不需要注册中断处理程序。操作系统会专门开启一个任务去定时检查 BD 表,如果发现当前指针指向的 BD 非空闲,则将该 BD 对应的数据取出来,并恢复 BD 的空闲状态。由于是采用任务定时检查的原理,从而轮询接收方式的实时性较差,但它没有中断那种系统上下文切换的开销,因此轮询方式在处理大流量数据包时会显得更加高效。
网络设配器的发包过程
NOTE:发包过程只作为简单介绍。
- 网卡驱动创建 Tx descriptor ring,将 Tx descriptor ring 的总线地址写入网卡寄存器 TDBA。
- 协议栈通过
dev_queue_xmit()
将 sk_buffer 下送到网卡驱动。 - 网卡驱动将 sk_buff 放入 Tx descriptor ring,更新网卡寄存器 TDT。
- DMA 感知到 TDT 的改变后,找到 Tx descriptor ring 中下一个将要使用的 descriptor。
- DMA 通过 PCI 总线将 descriptor 的数据缓存区复制到 Tx FIFO。
- 复制完后,通过 MAC 芯片将数据包发送出去。
- 发送完后,网卡更新网卡寄存器 TDH,启动硬中断通知 CPU 释放数据缓存区中的数据包。
sk_buff(Socket Buffer)
Linux 内核中,用 sk_buff(skb)来描述一个缓存,所谓分配缓存空间,就是建立一定数量的 sk_buff。sk_buff 是 Linux 内核网络协议栈实现中最重要的结构体,它是网络数据报文在内核中的表现形式。用户态应用程序(应用层)可以通过系统调用接口访问 BSD Socket 层,传递给 Socket 的数据首先会保存在 sk_buff 对应的缓冲区中,sk_buff 的结构定义在 include/linux/skbuff.h 文件中。它保存数据报文的结构为一个双向链表,如下所示:
当数据被储存到了 sk_buff 缓存区中,网卡驱动的发送函数 hard_start_xmit 也随之被调用,流程图如下所示:
- 首先创建一个 Socket,然后调用 write() 之类的写函数通过 Socket 访问网卡驱动,同时将数据保存在 sk_buff 缓冲区。
- Socket 调用发送函数 hard_start_xmit。hard_start_xmit 函数在初始化过程中会被挂接成类似于 xx_tx 的某个具体的发送函数,xx_tx 主要实现如下步骤:
- 从 Tx BD 表中取出一个空闲的 BD。
- 根据 sk_buff 中保存的数据修改 BD 的属性,一个是数据长度,另一个是数据报文缓存指针。值得注意的是,数据报文缓存指针对应的必须是物理地址,这是因为 DMA 在获取 BD 中对应的数据时只能识别物理地址。
- 修改该 BD 的状态为就绪态,DMA 模块将自动发送处于就绪态 BD 中所对应的数据。
- 移动发送 BD 表的指针指向下一个 BD。
- DMA 开始将处于就绪态 BD 缓存内的数据发送至网络,当发送完成后自动恢复该 BD 为空闲态。
当网卡接收到数据时,DMA 会自动将数据保存起来并通知 CPU 处理,CPU 通过中断或轮询的方式发现有数据接收进来后,再将数据保存到 sk_buff 缓冲区中,并通过 Socket 接口读出来。流程图如下所示:
- 网卡接收到数据后,DMA 搜索 Rx BD 表,取出空闲的 BD,并将数据自动保存到该 BD 的缓存中,修改 BD 为就绪态,并同时触发中断(该步骤可选)。
- 处理器可以通过中断或者轮询的方式检查接收 BD 表的状态,无论采用哪种方式,它们都需要实现以下步骤。
- 从接收 BD 表中取出一个空闲的 BD。
- 如果当前 BD 为就绪态,检查当前 BD 的数据状态,更新数据接收统计。
- 从 BD 中取出数据保存在 sk_buff 的缓冲区中。
- 更新 BD 的状态为空闲态。
- 移动接收 BD 表的指针指向下一个 BD。
- 用户调用 read 之类的读函数,从 sk_buff 缓冲区中读出数据,同时释放该缓冲区。
DMA 与 Buffer descriptor
网卡驱动会在 RAM 中建立并为例两个环形队列,称为 BD(Buffer descriptor)表,一个收(Rx)、一个发(Tx),每一个表项称为 descriptor(描述符)。descriptor 所存放的内容是由 CPU 决定的,一般会存放 descriptor 所指代的 Data buffer(实际的数据存储空间)的指针、数据长度以及一些标志位。
Rx/Tx 的 BD 表首地址分别存放于 CPU 的寄存器中,这样 CPU 就可以通过 BD 表项中的指针,索引到实际 Data buffer 的数据存储空间。每使用一次 DMA 传输数据,DB 表项就会下移一个。所以,DMA 并不是直接操作 Data Buffer 的,而是通过 descriptor 索引真实数据再执行传输。
Linux 内核通过调用
dma_map_single(struct device *dev,void *buffer,size_t size,enum dma_data_direction direction)
来建立 DMA 映射关系。struct device *dev
:描述一个设备;buffer
:把哪个地址映射给设备,也就是某一个skb。要映射全部,做一个双向链表的循环即可;size
:缓存大小;direction
:映射方向,即谁传给谁。一般来说,是双向映射,数据得以在设备和内存之间双向流动;
对于 PCI 设备而言,通过函数 pci_map_single 把 buffer 交给设备,设备可以直接从里边读/取数据。
参考文档
https://blog.csdn.net/zhangtaoym/article/details/75948505
https://blog.csdn.net/jiangganwu/article/details/83037139
https://blog.csdn.net/kklvsports/article/details/74452953
https://wenku.baidu.com/view/1d8f60bc1a37f111f1855bed.html
https://blog.csdn.net/sdulibh/article/details/46843011
https://blog.csdn.net/YuZhiHui_No1/article/details/38666589
https://blog.csdn.net/YuZhiHui_No1/column/info/linux-skb转自:https://www.cnblogs.com/jmilkfan-fanguiju/p/12789806.html
-
数据从网卡到应用的过程
2019-04-07 09:35:11本文写写数据从网卡到应用的过程,内容与图片很多整理自《网络是怎样连接的》、《Tomcat内核设计与剖析》,有的图片因清晰度不够我进行了重绘。 总览 本文围绕这张图从下至上展开。假设一个HTTP请求的数据到达网卡,... -
网卡将接收到的信号转换成数字信息
2021-07-16 08:11:53接收操作的第一步是网卡接收到信号,然后将其还原成数字信息。局域网中传输的网络包信号是由1和0组成的数字信息与用来同步的时钟信号叠加而成的,因此只要从中分离出时钟信号,然后根据时钟信号进行同步,就可以读取... -
Linux 网卡数据收发过程分析
2021-06-16 15:47:27一般来说,网卡主要有两个重要的功能:接收数据 和 发送数据。 所以,当网卡接收到数据包后,要通知 Linux 内核有数据需要处理。另外,网卡驱动应该提供让 Linux 内核把数据把发送出去的接口。 net_device 结构是 ... -
网络数据接收过程分析
2014-08-20 10:52:57网络数据接收过程分析(七)---tcp_v… (0/767)2008-09-19 14:56 网络数据接收过程分析(六)---tcp缓… (0/635)2008-09-19 14:53 网络数据接收过程分析(六)---tcp缓… (0/1086)2008-09-19 ... -
网卡如何接收数据包
2018-05-03 14:45:17MAC包中含有发送端和接收端的MAC地址信息。既然是驱动程序创建的MAC包头信息,当然可以随便输入地址信息的,主机伪装就是这么实现的。 驱动程序将MAC包拷贝到网卡芯片内部的缓存区,就算完事了。有网卡芯片接手... -
网络数据包转发和接收全过程
2020-12-21 11:59:41可参考:http://lxr.linux.no/linux+v2.6.30.4/net/网络设备接口部分主要负责从物理介质接收和发送数据,实现的文件在linu/driver/net目录下面。网络接口核心部分是整个网络接口的关键部位,它为网络协议提供统一的... -
网卡参数设置建议与各个网卡参数含义详解
2020-03-16 10:46:27我们都知道无盘就是客户机没有硬盘,而无盘实际上就是把客户机的硬盘放在了服务器上,通过一种虚拟化技术来工作的,而在这个虚拟过程中,网卡是非常关键的一环,他就好像有盘客户机的数据线。只是这根数据线远远比... -
linux内核Ethernet以太网卡驱动收发数据过程
2021-05-13 03:04:03linux内核Ethernet以太网卡驱动收发数据过程linux内核Ethernet以太网卡驱动收发数据过程下图简单描述了网卡驱动与Linux内核之间的联系:关于上图的一些说明:系统初始化:1. 协议模块调用 dev_add_pack() 来注册协议... -
"本地连接"有发送无接收数据的解决方法
2021-07-10 01:57:42"本地连接"有发送无接收数据的解决方法发布时间:2013-07-23 23:34:03 作者:佚名 我要评论在日常的网络维护中,常常出现"本地连接"有发送无接收,这个故障曾经让我伤透脑筋,现在也没有一个正确的解决方案,而下面... -
数据包从网卡到应用进程
2021-11-18 21:04:20从空间来看,从网卡到内核...还原后的数据信息会在网卡上,根据 FCS(帧校验序列,Frame Check Sequence)校验数据,判断数据在传输过程是否因噪音等影响导致信号失真,从而导致数据错误,需要丢弃这种无效的数据包。... -
Linux网络 - 数据包的接收过程 [很完整]
2020-01-13 09:58:19Linux网络 - 数据包的接收过程 -
数据包接收系列 — 数据包的接收过程(宏观整体)
2020-09-22 10:17:12本文只讨论以太网的物理网卡,不涉及虚拟设备,并且以一个UDP包的接收过程作为示例. 本示例里列出的函数调用关系来自于kernel 3.13.0,如果你的内核不是这个版本,函数名称和相关路径可能不一样,但背后的原理应该... -
网卡驱动收发包过程
2018-09-12 15:48:02网卡工作在物理层和数据链路层,主要由PHY/MAC芯片、Tx/Rx FIFO、DMA等组成,其中网线通过变压器接PHY芯片、PHY芯片通过MII接MAC芯片、MAC芯片接PCI总线 PHY芯片主要负责:CSMA/CD、模数转换、编解码、串并转换 ... -
2021/12/14---搭建5300网卡获取csi数据
2021-12-14 15:21:351、5300网卡,有半高和全高之分。(后期补图片) 2、一台电脑,一个路由器(不加密,不连外网) 3、一个Ubuntu启动盘,用来重装系统。本人所用系统为:ubuntu14.04.1,内核为4.1.10+ ubuntu14.04.1的下载资料如下: ... -
Linux网络 - 数据包的接收
2022-05-10 17:15:58Linux网络协议栈是处理网络数据包的典型系统,它包含了从物理层直到应用层的全过程。 大体流程 数据包到达网卡设备。 网卡设备依据配置进行DMA操作。(第1次拷贝:网卡寄存器->内核为网卡分配的缓冲区ring ... -
Linux PCI网卡驱动的详细分析
2020-07-03 09:58:01学习应该是一个先把问题简单化,在把问题复杂化的过程。一开始就着手处理复杂的问题,难免让人有心惊胆颤,捉襟见肘的感觉。读Linux网卡驱动也是一样。那长长的源码夹杂着那些我们陌生的变量和符号,望而生畏便是... -
网卡驱动收发包过程图解
2021-12-22 19:19:06网卡工作在物理层和数据链路层,主要由PHY/MAC芯片、Tx/Rx FIFO、DMA等组成,其中网线通过变压器接PHY芯片、PHY芯片通过MII接MAC芯片、MAC芯片接PCI总线 PHY芯片主要负责:CSMA/CD、模数转换、编解码、串并转换 ... -
Linux网卡基本结构和传输流程总结
2022-03-27 13:37:08简介 网卡的功能主要有两个: 一、是将电脑的数据封装为帧,并通过网线(对无线网络来说就是电磁波)将数据发送到网络上去;...物理层定义了数据传送与接收所需要的电与光信号、线路状态、时钟基准、数据编码和电路 -
Linux网络-数据包的接收流程(基于RTL8139网卡驱动程序)
2018-01-20 01:19:17本文将介绍Linux系统中,基于RTL8139网卡驱动程序,是如何一步一步将接收到的数据包传送到内核的网络协议栈的。 下图展示了数据包(packet)如何进入内存,并被内核的网络模块开始处理: +-----+ | | Memroy +... -
数据在网络中的发送和接收过程--简略版(TCP/IP五层结构)
2019-09-25 13:49:04若使用UDP,不论从应用层来的数据信息有多大,UDP照单全收,UDP是一次性发送一个完整的报文,同样在接收时也是直接接收整个报文; 若使用TCP,不论从应用层来的数据块有多大,或者有多小,TCP总是... -
树莓派Zero配置USB无线网卡-发送端(AP)与接收端(STA)配置区别
2021-03-04 20:52:41贪便宜买了个树莓派zero,不带板载WiFi及蓝牙,配置过程的话接收端还好,基本多数Linux免驱无线网卡设置好树莓派文件都能搞定(设置方法见“作为接收端配置”),但本意希望能将网卡设为AP组建局域网,结果差点没被...