Linux 报文从网卡到协议栈之间处理的过程

这篇博客详细探讨了Linux内核如何处理网络收包,从硬件接收、DMA传输、硬件中断处理到软中断NET_RX_ACTION的触发,最后进入内核协议栈。重点分析了e1000网卡驱动的probe函数,包括e100_open中的中断处理函数e100_intr,以及软中断处理函数net_rx_action和e100_poll。
摘要由CSDN通过智能技术生成

背景

         作为一个Linux内核开发的程序员,时常会被问到内核收报的处理过程,很多时候可以快速讲出一个大概,但关注的重点多在内核协议栈的报文处理过程,对于报文从到达网卡再到上送协议栈之间的处理过程总是很模糊,因此想就此过程进行学习研究,故有了本篇博客。

收包过程

        网络收报处理的过程分为几个步骤:

        1)硬件接收,网卡通过物理层或者数据链路层接收到数据帧

        2) DMA传输,网卡通过DMA引擎将报文拷贝到ring_buffer缓冲区,并触发硬件中断,通知CPU有报文到来。

        3)CPU硬件中断处理,将报文从ring_buffer中拷贝到内核报文缓冲区skb_buffer中,并放入报文接收队列中。

        4)触发收报软中断NET_RX_ACTION

        5)软中断处理,将报文从接收队列移入处理队列,并上送内核协议栈

        6)内核协议栈报文处理

        这里需要说明的是Linux 内核只负责L2-L4层的内容,L1物理层硬件负责,L4以上应用层负责。

主题分析

        网络收报离不开网卡驱动,以Ubuntu 为例,常用的Intel网卡驱动为e1000模块,这里以e1000为例,介绍网卡收报过程,在此之前需要对驱动加载、网卡探测、请求中断等一系列过程有个了解。

        在Linux 内核中,驱动以模块的形式存在,即驱动实质是一个内核模块,在Linux 内核启动,模块初始化过程中加载注册驱动,函数调用过程如下:

         驱动注册过程如下:

static int __init e100_init_module(void)
{
	if (((1 << debug) - 1) & NETIF_MSG_DRV) {
		pr_info("%s, %s\n", DRV_DESCRIPTION, DRV_VERSION);
		pr_info("%s\n", DRV_COPYRIGHT);
	}
	return pci_register_driver(&e100_driver);
}

        由此可知e1000驱动为e100_driver,通过pci_register_driver注册到内核中,e100_driver定义如下:

static struct pci_driver e100_driver = {
	.name =         DRV_NAME, /* 驱动名 */
	.id_table =     e100_id_table,
	.probe =        e100_probe, /* 网卡探测函数 */
	.remove =       e100_remove, /* 网卡移除时调用,主要释放网卡相关资源 */
#ifdef CONFIG_PM
	/* Power Management hooks */
	.suspend =      e100_suspend,
	.resume =       e100_resume,
#endif
	.shutdown =     e100_shutdown, /* 网卡shutdown */
	.err_handler = &e100_err_handler,
};

        pci_register_driver注册的实质就是将对应网卡的驱动挂载在相应的总线下,这样当网卡插入pci总线时,总线启动扫描并遍历下面挂载的所有驱动,依据设备信息(如厂商Id,设备ID信息等)去匹配相应驱动id_table内容,匹配后调用驱动的probe函数为网卡请求中断号,注册中断处理函数等一系列操作。

        从e100_driver的定义可以出,驱动的几个核心函数为:e100_probe、e100_remove、e100_shutdown,这里我们主要介绍和收报有关的e100_probe 函数的处理逻辑。

probe函数

        probe函数处理逻辑如下:

static int e100_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
	struct net_device *netdev;
	struct nic *nic;
	int err;

	/* 申请并分配网络设备结构体变量netdev */
	if (!(netdev = alloc_etherdev(sizeof(struct nic))))
		return -ENOMEM;

	/* 设置网络设备硬件特征 */
	netdev->hw_features |= NETIF_F_RXFCS;
	netdev->priv_flags |= IFF_SUPP_NOFCS;
	netdev->hw_features |= NETIF_F_RXALL;

	/* 设置网络设备操作函数: 如open ,close,tx_xmit, do_ioctl等 */
	netdev->netdev_ops = &e100_netdev_ops;
	SET_ETHTOOL_OPS(netdev, &e100_ethtool_ops); /*设置网络设备配置操作接口 */
	netdev->watchdog_timeo = E100_WATCHDOG_PERIOD;
	strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1);

	nic = netdev_priv
  • 21
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值