| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 网络协议 -> 网卡-驱动-DMA API-TCP/IP -> 正文阅读 |
|
[网络协议]网卡-驱动-DMA API-TCP/IP |
目录网卡的工作原理网卡工作原理 分析网卡的工作原理其实是分析网卡的驱动程序 网络是独立的一个模块。为了屏蔽网络环境中物理网络设备的多样性,Linux对所有的设备进行抽象并定义了一个统一的概念,称之为接口。所有对网络硬件的访问都是通过接口进行的,接口提供了一个对所有类型的硬件一致化的操作集合来处理基本数据发送和接收。一个网络接口被看作是一个发送和接收数据包的实体。对于每个网络接口,都用一个net_device的数据结构来表示。net_device中有很多提供系统访问和协议层调用的设备方法,包括提供设备初始化和往系统注册用的init函数,打开和关闭网络设备的open和stop函数,处理数据包发送的函数hard_start_xmit,以及中断处理函数。 所有被发送和接收的包都用数据结构sk_buff表示。要发送数据时,网络系统将分局系统路由表选择相应的网络接口进行数据传输;当接收数据包时,通过驱动程序登记的中断服务程序进行数据的接口处理。 网卡初始化网络设备初始化主要工作时检测设备的存在、初始化描述设备的net_device结构及在系统中登记该设备。 初始化过程首先检测网络物理设备是否存在,这是通过检测物理设备的硬件特征来完成;然后对设备进行资源配置,这些完成之后就要构造设备的net_device数据结构,用检测到值对net_device中的变量初始化;最后Linux内核中注册该设备并申请内存空间。 网卡的打开与关闭为了使用网络设备,需要打开网卡,打开和关闭的一个接口是由shell命令ifconfig调用的,而ifconfig则要调用一个通用的设备打开函数dev_open(net/core/dev.c),相应的还有一个dev_close函数,这两个函数提供独立于设备的操作接口的打开和关闭功能。 一般打开函数执行的操作包括注册中断函数,分配并初始化网卡所需要的接收与发送缓冲区,启动硬件检查网络连接线状态等。 从网卡到内存数据包的发送和接收是实现Linux网络驱动程序中两个最关键的过程。 中断与轮询 在轮询方式中,系统每隔一定的时间间隔就去检查一次物理设备,若设备有数据到达,就调用读取数据的程序。Linux中通过定时器实现,但是此法有一个明显的缺点:不管设备是否有数据,系统总是要固定的消耗CPU资源去查看设备,并且可能对一些紧急数据处理予以延迟。从资源的利用率以及工作的效率上看不是最优。 中断方式利用硬件体系结构的中断机制实现设备和系统的应答对话,即当物理设备需要CPU处理数据时,就向CPU发送一个终端信号,系统则在收到信号后调用相应的中断服务程序响应对设备中断的处理。**因此,基本在所有的网络设备驱动程序中都是用中断方式的。**网卡发送/接收数据,都必须以中断的方式告诉系统,系统处理中断后做出相应操作。 每一个网卡上都有一块FIFO存储器,对于NIC(Network Interface Controller),FIFO存储器是用来通过系统总线传送数据到系统存储器之前,缓存从LAN上接收到的数据。对与快速以太网还有一个直接内存存取(DMA:Directly Memory Access)控制器,用于提供对系统存储器的可靠访问。 驱动为网卡分配一个环形缓冲区,在一段连续的物理内存中实现。
网卡上存在一定大小的FIFO存储器,DMA缓冲区是由系统/驱动程序分配的一段连续的物理内存。 Linux网络 - 数据包的接收过程 DMA与缓冲区驱动程序调用dma_map函数分配DMA缓冲区 地址转换
PCI设备会有BAR(base address register),表示自己在PCI总线上的地址范围,CPU并不能通过总线地址A(位于BAR范围内)直接访问总线上的PCI设备,PCI host bridge会在MMIO(即物理地址)和总线地址之间进行mapping。 对于驱动程序,它往往是通过ioremap()把物理地址B映射成虚拟地址C,这时候,驱动程序就可以通过ioread32?来访问PCI总线上的地址A了。 如果PCI设备支持DMA,那么在驱动中可以通过kmalloc或者其他类似接口分配一个DMA buffer,并且返回了虚拟地址X,MMU将X地址映射成了物理地址Y,从而定位了DMA buffer在系统内存中的位置。因此,驱动可以通过访问地址X来操作DMA buffer,但是PCI 设备并不能通过X地址来访问DMA buffer,因为MMU对设备不可见,而且系统内存所在的系统总线和PCI总线属于不同的地址空间。 在一些简单的系统中(没有IOMMU),设备可以通过DMA直接访问物理地址Y,但是在大多数的系统中,使用IOMMU硬件block将DMA可访问的总线地址翻译成物理地址,也就是把上图中的地址Z翻译成Y。 驱动程序在调用dma_map_single这样的接口函数的时候会传递一个虚拟地址X,在这个函数中会设定IOMMU的页表,将地址X映射到Z,并且将返回z这个总线地址。驱动可以把Z这个总线地址设定到设备上的DMA相关的寄存器中。这样,当设备发起对地址Z开始的DMA操作的时候,IOMMU可以进行地址映射,并将DMA操作定位到Y地址开始的DMA buffer。 缓冲区的使用方式分为 一致性DMA映射 与 流式DMA映射 一致性DMA映射Consistent / coherent DMA mapping:持续使用该DMA buffer(不是一次性的),因此Consistent DMA总是在初始化的时候进行map,在shutdown的时候unmap。 网卡驱动和网卡DMA控制器往往是通过一些内存中的描述符(形成环或者链)进行交互,这些保存描述符的memory一般采用Consistent DMA mapping。 流式DMA映射streaming DMA mapping:流式DMA映射是一次性的,一般是需要进行DMA传输的时候才进行mapping,一旦DMA传输完成,就立刻ummap(除非使用dma_sync_*的接口,下面会描述)。并且硬件可以为顺序化访问进行优化。 应用程序调用驱动程序分配缓冲区所谓DMA可以使用任意的内存地址是因为:应用层通过malloc 获得的DMA缓冲区内存,无法知道他的物理地址是多少,而且可能不连续。 想知道的问题:这个DMA缓冲区地址区间在哪?是如何划分的? 数据传输可以以两种方式触发:一种软件请求数据,另一种由硬件异步传输。
网卡传输也是如此,网卡有一个循环缓冲区(通常叫做 DMA 环形缓冲区)建立在与处理器共享的内存中。每一个输入数据包被放置在环形缓冲区中下一个可用缓冲区,并且发出中断。然后驱动程序将网络数据包传给内核的其它部分处理,并在环形缓冲区中放置一个新的 DMA 缓冲区。 驱动分配缓冲区的方式驱动可以通过系统接口(例如__get_free_page*())或者类似kmalloc() or kmem_cache_alloc()这样的通用内存分配的接口来分配DMA buffer,这些接口函数返回的虚拟地址可以直接用于DMA mapping接口API,并通过DMA操作在外设和dma buffer中交换数据。 DMA shadow buffer两篇论文重新理解:固定的是虚拟地址 最左边这一列 扩展到VMIOMMU里存的是全部GPA-HPA的映射 存疑: |
|
网络协议 最新文章 |
使用Easyswoole 搭建简单的Websoket服务 |
常见的数据通信方式有哪些? |
Openssl 1024bit RSA算法---公私钥获取和处 |
HTTPS协议的密钥交换流程 |
《小白WEB安全入门》03. 漏洞篇 |
HttpRunner4.x 安装与使用 |
2021-07-04 |
手写RPC学习笔记 |
K8S高可用版本部署 |
mySQL计算IP地址范围 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/25 22:35:40- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |