linux内核数据包转发流程(二):中断

    【版权声明:转载请保留出处:blog.csdn.net/gentleliu。邮箱:shallnew*163.com】

    内核在处理2层数据包之前,必须先处理中断系统。设立中断系统,才有可能每秒处理成千的帧。

    当收到一个帧时,驱动程序会代表内核指示设备产生一个硬件中断,内核将中断其它的活动,然后调用一个驱动程序所注冊的处理函数,以满足设备的须要。当事件是接收到一个帧时,处理函数就会把该帧排入队列某处,然后通知内核。
    使用轮询技术会轻易浪费掉非常多系统资源,由于内核会持续去读取检查是否有有帧的到来。

但使用中断会在每接收到一帧时都强制产生中断。会让cpu处理中断浪费很多时间。在高流量负载下。中断代码会持续抢占正在处理的代码。到某一时刻,输入队列会满,导致旧的帧没办法处理,新的帧又无法排入队列。出现receive-livelock。
    中断长处就是帧的接受及其处理之间延时非常短。缺点就是在高负载下无法良好执行。
    
    当cpu接收一个中断通知信息时,会调用与该中断事件关联的处理函数,这样的关联性有编号表示。在该处理函数运行期间,内核处于中断上下文(interrupt context)中,服务于该中断事件的cpu会被关闭中断功能,即此时不能服务其他中断事件。也不能运行其他进程,cpu全然属于该中断处理函数,不能被抢占。简而言之,中断处理函数是非抢占的,且非可再进入的(reentrant)。这能够减少竞争情况的可能性,可是这对性能有潜在的严重影响。
    因此,中断处理函数所做工作应该尽快完毕。 中断事件能够先对cpu抢占。这是由于假设操作系统让硬件等太久。可能会遗失数据,还有一方面。假设内核或用户空间进程必须被延迟或被抢占。则没有数据会遗失。所以,如今中断处理函数分为上半部和下半部。一般(1)任务对时间敏感或(2)和任务相关或(3)须要保证不被其它中断打断放在上半部运行。能够推迟不太紧急的任务放在下半部运行。

下半部分为主要分为软中断,tasklet。工作队列。

下半部的基础架构为:1、把下半部分类成适当类型。2、注冊下半部类型及其处理函数间的关联关系。3、为下半部函数调度。以准备运行。4、通知内核有已调度的BH存在。

内核(linux-2.6.32)下半部类型有:(定义在include/linux/interrupt.h)

enum
{
    HI_SOFTIRQ=0,
    TIMER_SOFTIRQ,
    NET_TX_SOFTIRQ,
    NET_RX_SOFTIRQ,
    BLOCK_SOFTIRQ,
    BLOCK_IOPOLL_SOFTIRQ,
    TASKLET_SOFTIRQ,
    SCHED_SOFTIRQ,
    HRTIMER_SOFTIRQ,
    RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */

    NR_SOFTIRQS
};


软IRQ通常会在相关联的子系统内注冊。
内核初始化期间,softirq_init会注冊TASKLET_SOFTIRQ以及HI_SOFTIRQ相关联的处理函数。
void __init softirq_init(void)
{
    ......

    open_softirq(TASKLET_SOFTIRQ, tasklet_action);
    open_softirq(HI_SOFTIRQ, tasklet_hi_action);
}


网络子系统分两种soft IRQ。NET_TX_SOFTIRQ和NET_RX_SOFTIRQ。分别处理发送数据包和接收数据包。这两个soft IRQ在net_dev_init函数(net/core/dev.c)中注冊:
    open_softirq(NET_TX_SOFTIRQ, net_tx_action);
    open_softirq(NET_RX_SOFTIRQ, net_rx_action);
收发数据包的软中断处理函数被注冊为net_rx_action和net_tx_action。


当中open_softirq实现为:

void open_softirq(int nr, void (*action)(struct softirq_action *))
{
    softirq_vec[nr].action = action;
}


原文地址:https://www.cnblogs.com/bhlsheji/p/5397605.html