网络报文收发——前期准备阶段

概述

最近在学习Linux网络相关内容,把自己学到的分享一下。关于网络报文的收发过程,结合自己学习的一些东西进行了整理。因为直接说网络报文的收发的话,很多数据都不知道来源,所以这里先整理一下前期的一些工作。
前期初始化阶段:包括ksoftirq内核线程的创建,各协议对应的处理函数的注册,网络设备的初始化,网卡启动等接口。
在细化上述内容之前,先了解Linux中的fs_initcall和subsys_initcall等都是初始化模块的入口,模块的初始化都是通过这些接口注册后执行的。

ksoftirq内核线程创建

软中断是在专门的内核线程ksoftirqd中进行的。该进程的数量N=机器的核数
在这里插入图片描述
对应代码流程
在这里插入图片描述
ksoftirqd创建完成之后,会进入线程循环函数ksoftirqd_should_run和run_ksoftirqd,在这里面不断地判断有没有软中断要被处理

网络子系统初始化

subsys_initcall被用来初始化子系统,网络子系统的初始化执行的是net_dev_init,在该初始化函数里会为每个CPU申请一个softnet_data数据结构,其中的poll_list等待驱动将poll函数注册进来。open_softir为每一个软中断注册一个处理函数,在函数实现里可以看到注册的函数记录在softirq_vec变量内,在之后ksoftirqd收到软中断后就会使用该变量找到对应的处理函数
在这里插入图片描述

协议栈注册

内核实现了网络层的ip协议和传输层的tcp协议及udp协议,对应的实现函数分别是ip_rcv(),tcp_v4_rcv(),udp_rcv()。内核协议通过fs_initcall调用inet_init函数将上述函数注册到inet_protos和ptype_base数据结构中。如下图
在这里插入图片描述
代码流程:
在这里插入图片描述

网卡驱动初始化

在这里插入图片描述
其中igb_probe(probe函数)的主要作用:
在这里插入图片描述
Module_init接口初始化驱动程序
Igb_init_module->pci_register_driver->igb_probe(igb_driver_name)
在上图可以看到,网卡驱动部分实现了ethtool所需的接口,这里会完成函数地址的注册。ethtool发起一个系统调用后,内核会找到对应操作的回调函数。(网卡的实现函数在igb_ethtool.c文件下)
ethtool命令之所以能查看网卡收发包统计、能修改网卡自适应模式、能调整RX 队列的数量和大小,是因为ethtool命令最终调用到了网卡驱动的相应方法,而不是ethtool本身有这个超能力。
图中第六步注册的igb_netdev_opt中包含igb_open等函数,在网卡启动阶段调用
图中第七步igb_probe初始化过程中还会调用igb_alloc_q_vector,注册NAPI机制必须的poll函数(igb网卡对应的为igb_poll函数)

启动网卡

当上面的初始化都完成以后,就可以启动网卡了。前面网卡驱动初始化时,驱动向内核注册了 structure net_device_ops 变量,它包含着网卡启用、发包、设置mac 地址等回调函数(函数指针)。当启用一个网卡时(例如,通过 ifconfig eth0 up),net_device_ops 中的 igb_open方法会被调用。它通常会做以下事情:
在这里插入图片描述
//file: drivers/net/ethernet/intel/igb/igb_main.c
static int __igb_open(struct net_device netdev, bool resuming)
{
/
allocate transmit descriptors /
err = igb_setup_all_tx_resources(adapter);
/
allocate receive descriptors /
err = igb_setup_all_rx_resources(adapter);
/
注册中断处理函数 /
err = igb_request_irq(adapter);
if (err)
goto err_req_irq;
/
启用NAPI */
for (i = 0; i < adapter->num_q_vectors; i++)
napi_enable(&(adapter->q_vector[i]->napi));

}
在上面__igb_open函数调用了igb_setup_all_tx_resources,和igb_setup_all_rx_resources。在igb_setup_all_rx_resources这一步操作中,分配了RingBuffer,并建立内存和Rx队列的映射关系。(Rx Tx 队列的数量和大小可以通过 ethtool 进行配置)。我们再接着看中断函数注册igb_request_irq:
static int igb_request_irq(struct igb_adapter *adapter)
{
if (adapter->msix_entries) {
err = igb_request_msix(adapter);
if (!err)
goto request_done;

}
}
static int igb_request_msix(struct igb_adapter *adapter)
{

for (i = 0; i < adapter->num_q_vectors; i++) {

err = request_irq(adapter->msix_entries[vector].vector,
igb_msix_ring, 0, q_vector->name,
}
在上面的代码中跟踪函数调用, __igb_open => igb_request_irq => igb_request_msix, 在igb_request_msix中,对于多队列的网卡,为每一个队列都注册了中断,其对应的中断处理函数是igb_msix_ring(该函数也在drivers/net/ethernet/intel/igb/igb_main.c下)。 并且msix方式下,每个 RX 队列有独立的MSI-X 中断,从网卡硬件中断的层面就可以设置让收到的包被不同的 CPU处理。(可以通过 irqbalance ,或者修改 /proc/irq/IRQ_NUMBER/smp_affinity能够修改和CPU的绑定行为)。
以上准备工作完成就可以开始报文的接收和发送了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值