网络数据包收发流程(一):从驱动到协议栈

Linux MAC VLAN实现 概述 1、macvlan是一种网卡虚拟化解决方案,能够将一块物理网卡虚拟成多块虚拟网卡。 2、macvlan 这种技术听起来有点像 VLAN,但它们的实现机制是完全不一样的。macvlan 子接口和原来的主接口是完全独立的,可以单独配置 MAC 地址和 IP 地址,而 VLAN 子接口和主接口共用相同的 MAC 地址。VLAN 用来划分广播域,而 macvlan 共享同一个广播域。 3、macvlan 会根据收到包的目的 MAC 地址判断这个包需要交给哪个虚拟网卡,虚拟网卡再把包交给上层的协议栈处理。 ma 阅读详情
 
网络数据包收发流程(一):从驱动到协议栈

早就想整理网络数据包收发流程了,一直太懒没动笔。今天下决心写了

一、硬件环境

intel82546:PHY与MAC集成在一起的PCI网卡芯片,很强大
bcm5461: PHY芯片,与之对应的MAC是TSEC
TSEC: Three Speed Ethernet Controller,三速以太网控制器,PowerPc 架构CPU里面的MAC模块
注意,TSEC内部有DMA子模块

话说现在的CPU越来越牛叉了,什么功能都往里面加,最常见的如MAC功能。
TSEC只是MAC功能模块的一种,其他架构的cpu也有和TSEC类似的MAC功能模块。
这些集成到CPU芯片上的功能模块有个学名,叫平台设备,即 platform device。

二、网络收包原理


网络驱动收包大致有3种情况:

no NAPI:mac每收到一个以太网包,都会产生一个接收中断给cpu,即完全靠中断方式来收包
缺点是当网络流量很大时,cpu大部分时间都耗在了处理mac的中断。

netpoll:在网络和I/O子系统尚不能完整可用时,模拟了来自指定设备的中断,即轮询收包。
缺点是实时性差

NAPI: 采用 中断 + 轮询 的方式:mac收到一个包来后会产生接收中断,但是马上关闭。
直到收够了netdev_max_backlog个包(默认300),或者收完mac上所有包后,才再打开接收中断
通过sysctl来修改 net.core.netdev_max_backlog
或者通过proc修改 /proc/sys/net/core/netdev_max_backlog


下面只写内核配置成使用NAPI的情况,只写TSEC驱动。(非NAPI的情况和PCI网卡驱动 以后再说)
内核版本 linux 2.6.24

三、NAPI 相关数据结构

每个网络设备(MAC层)都有自己的net_device数据结构,这个结构上有napi_struct。
每当收到数据包时,网络设备驱动会把自己的napi_struct挂到CPU私有变量上。
这样在软中断时,net_rx_action会遍历cpu私有变量的poll_list,
执行上面所挂的napi_struct结构的poll钩子函数,将数据包从驱动传到网络协议栈。

四、内核启动时的准备工作

4.1 初始化网络相关的全局数据结构,并挂载处理网络相关软中断的钩子函数
start_kernel()
--> rest_init()
--> do_basic_setup()
--> do_initcall
-->net_dev_init

__init net_dev_init()
{
//每个CPU都有一个CPU私有变量 _get_cpu_var(softnet_data)
//_get_cpu_var(softnet_data).poll_list很重要,软中断中需要遍历它的

for_each_possible_cpu(i) {
struct softnet_data *queue;
queue = &per_cpu(softnet_data, i);
skb_queue_head_init(&queue->input_pkt_queue);
queue->completion_queue = NULL;
INIT_LIST_HEAD(&queue->poll_list);
queue->backlog.poll = process_backlog;
queue->backlog.weight = weight_p;
}
open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL);//在软中断上挂网络发送handler
open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL);//在软中断上挂网络接收handler
}

4.2 加载网络设备的驱动
NOTE:这里的网络设备是指MAC层的网络设备,即TSEC和PCI网卡(bcm5461是phy)
在网络设备驱动中创建net_device数据结构,并初始化其钩子函数 open(),close() 等
挂载TSEC的驱动的入口函数是 gfar_probe

// 平台设备 TSEC 的数据结构
static struct platform_driver gfar_driver = {
.probe = gfar_probe,
.remove = gfar_remove,
.driver = {
.name = "fsl-gianfar",
},
};

int gfar_probe(struct platform_device *pdev)
{
dev = alloc_etherdev(sizeof (*priv)); // 创建net_device数据结构

dev->open = gfar_enet_open;
dev->hard_start_xmit = gfar_start_xmit;
dev->tx_timeout = gfar_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
#ifdef CONFIG_GFAR_NAPI
netif_napi_add(dev, &priv->napi,gfar_poll,GFAR_DEV_WEIGHT);//软中断里会调用poll钩子函数
#endif
#ifdef CONFIG_NET_POLL_CONTROLLER
dev->poll_controller = gfar_netpoll;
#endif
dev->stop = gfar_close;
dev->change_mtu = gfar_change_mtu;
dev->mtu = 1500;
dev->set_multicast_list = gfar_set_multi;
dev->set_mac_address = gfar_set_mac_address;
dev->ethtool_ops = &gfar_ethtool_ops;
}

五、启用网络设备
5.1 用户调用ifconfig等程序,然后通过ioctl系统调用进入内核
socket的ioctl()系统调用
--> sock_ioctl()
--> dev_ioctl() //判断SIOCSIFFLAGS
--> __dev_get_by_name(net, ifr->ifr_name) //根据名字选net_device
--> dev_change_flags() //判断IFF_UP
--> dev_open(net_device) //调用open钩子函数

对于TSEC来说,挂的钩子函数是 gfar_enet_open(net_device)

5.2 在网络设备的open钩子函数里,分配接收bd,挂中断ISR(包括rx、tx、err),对于TSEC来说

gfar_enet_open
--> 给Rx Tx Bd 分配一致性DMA内存
--> 把Rx Bd的“EA地址”赋给数据结构,物理地址赋给TSEC寄存器
--> 把Tx Bd的“EA地址”赋给数据结构,物理地址赋给TSEC寄存器
--> 给 tx_skbuff 指针数组 分配内存,并初始化为NULL
--> 给 rx_skbuff 指针数组 分配内存,并初始化为NULL

--> 初始化Tx Bd
--> 初始化Rx Bd,提前分配存储以太网包的skb,这里使用的是一次性dma映射
(注意:#define DEFAULT_RX_BUFFER_SIZE 1536保证了skb能存一个以太网包)
rxbdp = priv->rx_bd_base;
for (i = 0; i < priv->rx_ring_size; i++) {
struct sk_buff *skb = NULL;
rxbdp->status = 0;
//这里真正分配skb,并且初始化rxbpd->bufPtr, rxbdpd->length
skb = gfar_new_skb(dev, rxbdp);
priv->rx_skbuff[i] = skb;

rxbdp++;
}
rxbdp--;
rxbdp->status |= RXBD_WRAP; // 给最后一个bd设置标记WRAP标记

--> 注册TSEC相关的中断handler: 错误,接收,发送
request_irq(priv->interruptError, gfar_error, 0, "enet_error", dev)
request_irq(priv->interruptTransmit, gfar_transmit, 0, "enet_tx", dev)//包发送完
request_irq(priv->interruptReceive, gfar_receive, 0, "enet_rx", dev)//包接收完

-->gfar_start(net_device)
// 使能Rx、Tx
// 开启TSEC的 DMA 寄存器
// Mask 掉我们不关心的中断event


最终,TSEC相关的Bd等数据结构应该是下面这个样子的

六、中断里接收以太网包

TSEC的RX已经使能了,网络数据包进入内存的流程为:
网线 --> Rj45网口 --> MDI 差分线
--> bcm5461(PHY芯片进行数模转换) --> MII总线
--> TSEC的DMA Engine 会自动检查下一个可用的Rx bd
--> 把网络数据包 DMA 到 Rx bd 所指向的内存,即skb->data


接收到一个完整的以太网数据包后,TSEC会根据event mask触发一个 Rx 外部中断。
cpu保存现场,根据中断向量,开始执行外部中断处理函数do_IRQ()

do_IRQ 伪代码
{
上半部处理硬中断
查看中断源寄存器,得知是网络外设产生了外部中断
执行网络设备的rx中断handler(设备不同,函数不同,但流程类似,TSEC是gfar_receive)
1. mask 掉 rx event,再来数据包就不会产生rx中断
2. 给napi_struct.state加上 NAPI_STATE_SCHED 状态
3. 挂网络设备自己的napi_struct结构到cpu私有变量_get_cpu_var(softnet_data).poll_list
4. 触发网络接收软中断
下半部处理软中断
依次执行所有软中断handler,包括timer,tasklet等等
执行网络接收的软中断handler net_rx_action
1. 遍历cpu私有变量_get_cpu_var(softnet_data).poll_list
2. 取出poll_list上面挂的napi_struct 结构,执行钩子函数napi_struct.poll()
(设备不同,钩子函数不同,流程类似,TSEC是gfar_poll)
3. 若poll钩子函数处理完所有包,则打开rx event mask,再来数据包的话会产生rx中断
4. 调用napi_complete(napi_struct *n)
把napi_struct 结构从_get_cpu_var(softnet_data).poll_list 上移走
同时去掉 napi_struct.state 的 NAPI_STATE_SCHED 状态
}

6.1 TSEC的接收中断处理函数
gfar_receive
{
#ifdef CONFIG_GFAR_NAPI
// test_and_set当前net_device的napi_struct.state 为 NAPI_STATE_SCHED
// 在软中断里调用 net_rx_action 会检查状态 napi_struct.state

if (netif_rx_schedule_prep(dev, &priv->napi)) {
tempval = gfar_read(&priv->regs->imask);
tempval &= IMASK_RX_DISABLED; //mask掉rx,不再产生rx中断
gfar_write(&priv->regs->imask, tempval);
// 将当前net_device的 napi_struct.poll_list 挂到
// CPU私有变量__get_cpu_var(softnet_data).poll_list 上,并触发软中断
// 所以,在软中断中调用 net_rx_action 的时候,就会执行当前net_device的
// napi_struct.poll()钩子函数,即 gfar_poll()

__netif_rx_schedule(dev, &priv->napi);
}
#else
gfar_clean_rx_ring(dev, priv->rx_ring_size);
#endif
}

6.2 网络接收软中断net_rx_action
net_rx_action()
{
struct list_head *list = &__get_cpu_var(softnet_data).poll_list;
//通过 napi_struct.poll_list, 将N多个 napi_struct 链接到一条链上
//通过 CPU私有变量,我们找到了链头,然后开始遍历这个链


int budget = netdev_budget; //这个值就是 net.core.netdev_max_backlog,通过sysctl来修改

while (!list_empty(list)) {
struct napi_struct *n;
int work, weight;
local_irq_enable();
//从链上取一个 napi_struct 结构(接收中断处理函数里加到链表上的,如gfar_receive)
n = list_entry(list->next, struct napi_struct, poll_list);
weight = n->weight;
work = 0;
if (test_bit(NAPI_STATE_SCHED, &n->state)) //检查状态标记,此标记在接收中断里加上的
work = n->poll(n, weight); //使用NAPI的话,使用的是网络设备自己的napi_struct.poll
//对于TSEC是,是gfar_poll
WARN_ON_ONCE(work > weight);
budget -= work;
local_irq_disable();

if (unlikely(work == weight)) {
if (unlikely(napi_disable_pending(n)))
__napi_complete(n); //操作napi_struct,把去掉NAPI_STATE_SCHED状态,从链表中删去
else
list_move_tail(&n->poll_list, list);
}
netpoll_poll_unlock(have);
}
out:
local_irq_enable();
}

static int gfar_poll(struct napi_struct *napi, int budget)
{
struct gfar_private *priv = container_of(napi, struct gfar_private, napi);
struct net_device *dev = priv->dev; //TSEC对应的网络设备
int howmany;
//根据dev的rx bd,获取skb并送入协议栈,返回处理的skb的个数,即以太网包的个数
howmany = gfar_clean_rx_ring(dev, budget);
// 下面这个判断比较有讲究的
// 收到的包的个数小于budget,代表我们在一个软中断里就全处理完了,所以打开 rx中断
// 要是收到的包的个数大于budget,表示一个软中断里处理不完所有包,那就不打开rx 中断,
// 待到下一个软中断里再接着处理,直到把所有包处理完(即howmany<budget),再打开rx 中断

if (howmany < budget) {
netif_rx_complete(dev, napi);
gfar_write(&priv->regs->rstat, RSTAT_CLEAR_RHALT);
//打开 rx 中断,rx 中断是在gfar_receive()中被关闭的
gfar_write(&priv->regs->imask, IMASK_DEFAULT);
}
return howmany;
}

gfar_clean_rx_ring(dev, budget)
{
bdp = priv->cur_rx;
while (!((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))) {
rmb();
skb = priv->rx_skbuff[priv->skb_currx];//从rx_skbugg[]中获取skb
howmany++;
dev->stats.rx_packets++;
pkt_len = bdp->length - 4; //从length中去掉以太网包的FCS长度
gfar_process_frame(dev, skb, pkt_len);
dev->stats.rx_bytes += pkt_len;
dev->last_rx = jiffies;
bdp->status &= ~RXBD_STATS; //清rx bd的状态

skb = gfar_new_skb(dev, bdp); // Add another skb for the future
priv->rx_skbuff[priv->skb_currx] = skb;

if (bdp->status & RXBD_WRAP) //更新指向bd的指针
bdp = priv->rx_bd_base; //bd有WARP标记,说明是最后一个bd了,需要“绕回来”
else
bdp++;
priv->skb_currx = (priv->skb_currx + 1) & RX_RING_MOD_MASK(priv->rx_ring_size);
}
priv->cur_rx = bdp; /* Update the current rxbd pointer to be the next one */
return howmany;
}

gfar_process_frame()
-->RECEIVE(skb) //调用netif_receive_skb(skb)进入协议栈


#ifdef CONFIG_GFAR_NAPI
#define RECEIVE(x) netif_receive_skb(x)
#else
#define RECEIVE(x) netif_rx(x)
#endif

------------------------------------ 华丽的分割线 ---------------------------------------


呼,netif_receive_skb(skb)可算到协议栈了,歇会儿....

 

数据包从网卡到应用进程 从空间来看,从网卡到内核空间,再到用户空间,整个过程就是内核将网卡中的数据包拷贝到应用程序。 1.网卡 网卡收到的是数据包的电信号,收到该电信号之后,会将该电信号转化为数字信息,然后进行FCS校验,最后检查数据包的MAC地址是否匹配。 网卡将电信号转化为数字信息之后包含的数据包的信息如下所示: 还原后的数据信息会在网卡上,根据 FCS(帧校验序列,Frame Check Sequence)校验数据,判断数据在传输过程是否因噪音等影响导致信号失真,从而导致数据错误,需要丢弃这种无效的数据包。... 阅读详情

相关推荐

网卡驱动收发包原理深入分析

深入分析82571网卡收发包原理。具有极大代表性,其它所有网卡原理均类似。明白此文档,开发网卡驱动或浏览相关驱动代码,及分析网卡速率瓶颈等工作都会豁然开朗。

linux驱动源码分析(xilinx的千兆网口)

1.涉及的文件: macb_main.c phy_device.c dp83867.c phy_device.c 在内核启动就会运行该驱动,它是匹配PHY和配置phy的重要接口。 2.驱动之间的关系 macb_mii_probe ——> phy_connect_direct ---》 phy_attach_direct phy_init -----》 phy_driver_r...

MENGHUANBEIKE的专栏 3127

Windows安装ros2(Jazzy版本+win10/win11)-1.配置windows终端

更高版本的terminal可能设置里更加丰富, 有默认以管理员身份启动的选项等,可自行配置。打开微软应用商店, 在其中搜索并下载 Terminal 即可(注意别挂梯子)下载并安装, 安装的位置自行替换,建议别放到系统盘中。点击任务栏中的搜索, 输入store。

weixin_48398592的博客 1454

使用GDB+QEMU调试Linux内核环境搭建-进阶篇

  使用GDB+QEMU调试Linux内核环境搭建-进阶篇1 前言2 QEMU命令行参数3 调试的kernel信息设置4 调试virtio_net内核模块的start_xmit函数4.1 启动qemu vm4.2 启动gdb4.2 加载virtio-net内核模块的symbol file4.2.1 获取Guest中virtio-net内核模块的 .text,.data和 .bss4.2.2 在gdb中添加virtio-net内核模块的symbol信息4.2.3 gdb调试start_xmit函数

alexanderwang7的博客 2170

三角洲行动交易行自动化脚本

DFMarketBot - 三角洲行动自动交易助手 DFMarketBot是一款专为三角洲行动游戏设计的交易行自动化工具,通过OCR识别和鼠标模拟实现自动补卡、补子弹功能。项目采用Python开发,支持Windows平台,主要功能包括: 自动检测并购买指定物品(房卡/子弹) 价格监控与智能购买(低于设定价格自动下单) 自定义购买数量与次数限制 支持窗口/无窗口两种游戏模式 使用前需安装Tesseract OCR引擎并配置游戏为1920*1080分辨率。通过JSON文件可灵活配置监控物品清单、理想价格和购买策

愤怒的阿木木的博客 3万+

Cisco思科 电子科技大学计算机网络 SRWE 期末技能考试 (PTSA)

电子科技大学2024春_SRWE_计算机网络系统 SRWE 期末技能考试 (PTSA)

2301_76745065的博客 5095

控制MAC层数据发送过程遇到的问题

做TDMA的一个关键是控制住信号从MAC层向驱动层的数据的发送,从原来的事件触发转换成定时器触发。在原有的80211协议中有两个地方会触发该操作。一个操作是: (1)ieee80211_subif_start_xmit (tx.c): -Addsthe 802.11 header. -Initializestransmission time in devtrans_start (2)ie

九阳神功爱喝茶的专栏 2366

ArcGIS供地图斑图形及属性入库操作(附练习数据)

已知数据: 供地图斑位置图。 供地图斑表格 成果要求:将供地图斑导入mdb数据库并将供地图斑表格的属性进行挂接。 接下来我们开始操作: 1、 打开arcmap,在目录内连接的文件夹内找到供地图斑位置图。图片 2、 将annotation和polygon添加到arcmap图片 3、 右击文件所在文件夹新建mdb数据库图片 4、 右击数据库新建要素数据集。(要素数据集非常重要,稍后所有操作都在数据集内进行)选择图斑投影方式及代号。图片 5、 右击要素数据集—导入(将图层内的数据导入数据库)图片 6、 数据导入.

qq_43173805的博客 8132

linux内核收发包原理,网络数据包收发流程(一):从驱动到协议栈

早就想整理网络数据包收发流程了,一直太懒没动笔。今天下决心写了一、硬件环境intel82546:PHY与MAC集成在一起的PCI网卡芯片,很强大bcm5461: PHY芯片,与之对应的MAC是TSECTSEC: Three Speed Ethernet Controller,三速以太网控制器,PowerPc 架构CPU里面的MAC模块注意,TSEC内部有DMA子模块话说现在的CPU越...

weixin_36355328的博客 965

基于Halium 9为小米平板4移植Ubuntu Touch:内核适配与驱动调试实战

在移动设备生态中,Linux内核作为核心,通过硬件抽象层(HAL)管理底层硬件驱动,为上层系统提供统一接口。其模块化设计允许深度定制,是实现跨平台移植的技术基础。Halium项目正是利用这一原理,构建了一个中间层,将安卓内核的硬件支持能力“翻译”成标准Linux发行版能识别的格式,从而在手机、平板等移动设备上运行Ubuntu Touch、postmarketOS等移动Linux系统,极大地拓展了老旧设备的再利用价值。这一技术为开发者提供了在常见安卓硬件上进行Linux系统开发与定制的可能,尤其在嵌入式与物联

weixin_42639246的博客 374

从零上手HiAgent:构建能听懂指令的智能体框架实战指南

智能体(Agent)作为人工智能领域的重要范式,正从传统的预设流程自动化转向以目标驱动的自主规划与执行。其核心原理在于利用大语言模型(LLM)作为决策大脑,通过任务规划、工具调用与记忆机制,将模糊的自然语言指令转化为可执行的工作流。这一技术显著提升了人机协作的效率和自然度,其价值在自动化办公、数据分析与智能流程编排等场景中尤为突出。本文聚焦于HiAgent这一具体框架,深入解析其作为智能体开发平台的核心架构,涵盖规划器、执行器、工具集等关键组件,并提供从环境配置、自定义工具开发到复杂任务链构建的完整工程实践

weixin_29266007的博客 896

e1000e网络数据包收发流程(一):从驱动到协议栈

学习资料 转载:http://blog.chinaunix.net/uid-24148050-id-464587.html 一、硬件环境 intel82546:PHY与MAC集成在一起的PCI网卡芯片,很强大bcm5461: PHY芯片,与之对应的MAC是TSECTSEC: Three Speed Ethernet Controller,三速以太网控制器,PowerPc 架...

wangyangzhizunwudi的博客 775

基于卷积神经网络的球罐结构损伤识别

球罐结构损伤识别方法研究 摘要:本研究针对球形储罐(球罐)结构健康监测需求,提出基于卷积神经网络(CNN)的损伤识别方法。通过ANSYS有限元仿真构建包含5种裂纹深度的500组振动信号数据集,设计包含卷积层、池化层和全连接层的CNN网络架构。实验结果表明,该方法在训练集和测试集上分别达到98.4%和96.0%的准确率,验证了其在球罐焊缝损伤识别中的有效性。研究提供了完整的MATLAB实现代码,为球罐结构智能化监测提供了技术参考。 关键词:球罐结构;卷积神经网络;损伤识别;振动信号;深度学习

AI的内部世界的博客 120

网络数据包收发流程(一):从驱动到协议栈 .

http://blog.youkuaiyun.com/jw212/article/details/6738457 早就想整理网络数据包收发流程了,一直太懒没动笔。今天下决心写了 一、硬件环境 intel82546:PHY与MAC集成在一起的PCI网卡芯片,很强大 bcm5461:   PHY芯片,与之对应的MAC是TSEC TSEC:      Three Speed Ethernet

happylife1527的专栏 687

【大语言模型 37】知识蒸馏深度实战:大模型向小模型迁移

本文深入探讨了知识蒸馏技术在大语言模型领域的应用,详细阐述了如何将庞大复杂的大模型知识有效迁移到轻量级小模型中。文章系统介绍了知识蒸馏的理论基础、多种蒸馏损失函数设计、特征匹配与注意力迁移技术、渐进式蒸馏策略以及全面的效果评估方法。通过实战案例和完整代码实现,读者将掌握从理论到实践的知识蒸馏全流程,了解如何在保持模型性能的同时显著降低计算资源需求,实现大语言模型的高效部署与应用。

熵数实验室 1509

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

上一篇: MIPS GCC 嵌入式汇编
下一篇: 网络数据包收发流程(二):不配置NAPI的情况
LonlyGod
博客等级 码龄15年 32粉丝 1原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值