深入理解Linux网络技术内幕——帧的接收与传输

帧的接收

NAPI与netif_rx(非NAPI)

Linux内核获取网络帧到达通知的方式有两中:中断和轮询。(中断值设备向内核发出中断,轮询指linux内核主动轮询设备)

在早起的linux内核中,网络帧主要以中断的方式通知linux内核帧的到达。这是非NAPI方式。

现在的操作系统中,linux内核使用NAPI方式, 获取帧到达的消息。NAPI混合使用了中断和轮询。

netif_rx(非NAPI):

每一个帧接收完毕时,设备向内核发送一个中断。(在低流量负载的情况下,这种方式对比轮询优势明显。但是由于中断处理需要一些额外的开销,在高流量负载下,由于要频繁处理中断,因此会造成不少的网络开销。)

NAPI:

在低流量负载下,使用轮询方式通知内核网络帧接收完毕。在高流量负载下使用轮询方式。

在NAPI机制下:

对于设备来说,当一个帧接收完毕,进入设备输入队列,这时驱动程序不会立即向内核发送中断,而是先判断内核是否还在处理设备输入队列中的帧,如果是,则先不发送中断,内核自然会处理设备输入队列中的帧。

对于内核来说,从接到一个中断开始处理设备输入队列中的网络帧,处理完一个网络帧时,内核不会先去做其他事,而是先轮询设备输入队列,看看还有没有其他帧要处理。如果有,则循环处理;如果设备输入队列中的帧都处理完了,就可以去做其他事,等待设备驱动程序下一次发来中断。

NAIP机制实现

我们知道,每个cpu都有这样的一个队列,它主要是用来存储incoming frame。由于他是每个cpu都有一个队列,因此在不同的cpu之间我们就不要任何锁来控制并发的处理这个帧队列。我们在操作系统层要取得帧数据,都是通过这个数据来读取。

struct softnet_data
{
    struct Qdisc        *output_queue;      //qdisc是queueing discipline的简写,也就是排队规则,即qos.这里也就是输出帧的控制。
    struct sk_buff_head input_pkt_queue;    //当输入帧被驱动取得之前,就保存在这个队列里,(不适用与napi驱动,napi有自己的私有队列)
    struct list_head    poll_list;          //表示有输入帧待处理的设备链表。
    struct sk_buff      *completion_queue;  //表示已经成功被传递出的帧的链表。

    struct napi_struct  backlog;            //用来兼容非napi的驱动。
};

napi在结构体softnet_data中使用了struct list_head    poll_list;     和struct napi_struct  backlog;            //用来兼容非napi的驱动。

struct napi_struct {
    /* The poll_list must only be managed by the entity which
     * changes the state of the NAPI_STATE_SCHED bit.  This means
     * whoever atomically sets that bit can add this napi_struct
     * to the per-cpu poll_list, and whoever clears that bit
     * can remove from the list right before clearing the bit.
     */
    struct list_head    poll_list; //设备列表(在入口队列有新帧等待处理的设备)

    unsigned long       state;
    int         weight;
    int         (*poll)(struct napi_struct *, int); //把缓冲区从设备的入口队列(NAPI私有队列,softnet_data->input_pkt_queue不同)退出
#ifdef CONFIG_NETPOLL
    spinlock_t      poll_lock;
    int         poll_owner;
#endif

    unsigned int        gro_count;

    struct net_device   *dev;
    struct list_head    dev_list;
    struct sk_buff      *gro_list;
    struct sk_buff      *skb;
};

我们需要注意poll_list成员,它表示在输入队列中有帧需要处理的设备链表,这是用来支持轮询的。这个链表中的设备都处于关中断的状态。内核在适当时间进行轮询处理这些设备接收到的帧。

时间: 2024-08-24 20:52:27

深入理解Linux网络技术内幕——帧的接收与传输的相关文章

深入理解Linux网络技术内幕——IPv4 报文的传输发送

报文传输,指的是报文离开本机,发往其他系统的过程. 传输可以由L4层协议发起,也可以由报文转发发起. 在深入理解Linux网络技术内幕--IPv4 报文的接收(转发与本地传递)一文中,我们可以看到,报文转发最后会调用dst_output与邻居子系统进行交互,然后传给设备驱动程序. 这里,我们从L4层协议发起的传输,最后也会经历这一过程(调用dst_output).本文讨论的是L4层协议发起的传输,在IPv4协议处理(IP层)中的一些环节. 大蓝图 我们先看下传输环节的大蓝图,以便对传输这一过程有

深入理解Linux网络技术内幕——中断与网络驱动程序

接收到帧时通知驱动程序 在网络环境中,设备(网卡)接收到一个数据帧时,需要通知驱动程序进行处理.有一下几种通知机制: 轮询: 内核不断检查设备是否有话要说.(比较耗资源,但在一些情况下却是最佳方法) 中断: 特定事件发生时,设备驱动程序代表内核指示设备产生硬件中断,内核中断其它活动满足设备的需要.多数网络驱动程序使用中断. 中断期间处理多帧: 中断被通知,且驱动程序执行.然后保持帧的接收(载入),直到输入队列达到指定的数目.或者一直做下去知道队列清空.或者经过指定时间. 定时器驱动的中断事件 驱

深入理解Linux网络技术内幕——IPv4 概念

1.大蓝图 大蓝图展示了IPv4协议与其他子系统之间的联系,保罗设备驱动.Netfilter.L4 层协议等之间的互动. IPv4协议中的报文 我们可以大致看出数据在IPv4协议中的流向, 接收报文 设备驱动处理完硬件介绍到的数据后,IPv4协议的ip_rcv函数(net_receive_skb调用)得到了属于IPv4的报文,接着调用ip_rcv_finish对报文进行分析.判断是该转发还是交付本地上层协议. 如果是本地报文,则传给ip_local_deliver处理,如果是转发,那就交付ip_

《深入理解Linux网络技术内幕》阅读笔记 --- 路由

一.Linux内核中路由相关的主要数据结构 struct fib_result:对路由表查找后返回该结构,它的内容并不是简单的包含下一跳信息,而且包含其他特性,例如策略路由所需的更多参数. struct fib_rule:表示由策略路由在路由流量时选择路由表的规则 struct fib_node:一条路由表项.例如,该数据结构用于存储由route add或ip route add命令添加一条路由时生成的信息. struct fn_zone:一个zone表示子网掩码长度相同的一组路由 struct

深入理解Linux网络技术内幕——协议处理函数

网络帧在进入网络层时,需要区分不同的网络协议进行处理,这就需要涉及协议处理函数. 首先我们从驱动接收到一个数据帧,分析数据帧在协议栈中自下而上的传输流程. 设备驱动程序在接收到一个数据帧时,会将其保存在一个sk_buff缓冲区数据结构,并对其进行初始化. struct sk_buff { ...... __be16 protocol:16; ...... } 在这个缓冲区结构体中,有一个protocol字段,用于标识网络层的协议. 我们知道网络帧在设备驱动程序中处理后,设备驱动程序会调用neti

深入理解Linux网络技术内幕——网络设备初始化

概述 内核的初始化过程过程中,与网络相关的工作如下所示: 内核引导时执行start_kernel,start_kernel结束之前会调用rest_init,rest_init初始化内核线程init(在Linux3-12中为kernel_init). asmlinkage void __init start_kernel(void) { ... parse_early_param();//间接调用parse_args parse_args(...); //处理内核引导程序(boot loader)

深入理解Linux网络技术内幕——设备的注册于初始化(一)

副标题:设备注册相关的基本结构的原理框架 设备注册与删除时间 设备在下列两种情况下进行注册: 1)加载NIC驱动时 2)插入热插拔设备时 这里NIC与热插拔设备有些不同.a.对于非热插拔NIC来说,NIC的注册是伴随着其驱动的发生的,而NIC可以内建到内核,也可以作为模块载入,如果内建入内核,则NIC设备和初始化均发生在引导时,如果NIC作为模块加载,则NIC的注册和驱动初始化均发生在模块加载时.b. 对于热插拔NIC设备来说,其驱动已经加载,因此设备的注册发生在插入设备,内核通知关联驱动时.

深入理解Linux网络技术内幕——路由子系统的概念与高级路由

本文讨论IPv4的路由子系统.(IPv6对路由的处理不同). 基本概念 路由子系统工作在三层,用来转发入口流量. 路由子系统主要设计 路由器.路由.路由表等概念. 路由器: 配备多个网络接口卡(NIC),并且能利用自身网络信息进行入口流量转发的设备. 路由: 流量转发,决定目的地的过程 路由表:转发信息库,该库中储存路由需要本地接收还是转发的信息, 以及转发流量时所需要的信息.(即,信息库用来判断,要不要转发,如果要转发,向哪里转发). 我们了解,路由器有多个网卡,但是多个NIC的设备不一定就是

深入理解Linux网络技术内幕——PCI层和网络接口卡

概述 内核的PCI子系统(即PCI层)提供了不同设备一些通用的功能,以便简化各种设备驱动程序. PCI层重要结构体如下: pci_device_id 设备标识,根据PCI标志定义的ID,而不是Linux本地的. pci_dev 类似于网络设备的net_device.每个PCI会被分配一个net_dev实例. pci_driver PCI层和设备驱动程序之间的接口.主要由一些函数指针组成.如下所示: struct pci_driver { struct list_head node; char *