中断下半部机制 - 软中断及tasklet

1. 引入软中断

一个中断处理程序的一个或几个中断服务例程在执行结束之前,内核处于中断环境中,当前CPU不再响应同类型的中断,如果不允许中断嵌套,则CPU需要屏蔽掉所有中断。也就是说,一个CPU忙于服务于一个中断事件时,就不能处理其他中断,同时CPU不能执行其他进程,即不能被抢占,这种情况下,如果在中断服务例程中消耗的时间过多,就会对性能产生潜在的影响。

一般情况下,一个中断事件所触发的动作可能需要占用很多CPU时间,但通常其中多数内容都是可以等待的。为了保证对硬件保持较短的响应时间,在一个中断事件到来时,可以先抢占CPU,将必须尽快处理的事情做完,然后释放CPU,在稍后的某一时刻,当内核不需要再做一些紧迫之事,再处理中断事件剩下的事情。

这些可以延后的处理程序被称为中断下半部。例如网卡收包的处理,当CPU收到一个收包中断时,需要把数据包从DMA中搬运到内存中内核预先设置好的位置,并设置某些标记来通知内核有数据包到来,然后内核分配一个skb缓冲区,将数据内容拷贝到缓冲区里,并初始化一个skb实例,然后将数据包交给上层协议栈处理,这个过程非常复杂,需要耗费大量CPU时间,不可能都在中断处理程序中完成。有了中断下半部机制,在中断处理程序中只需将数据包放到内存并设好标记,这可以很快完成,而剩下实际的数据包处理过程则放到下半部中去执行。

内核使用软中断(softirq)和微任务(tasklet)两种可延迟函数来实现中断下半部机制,他们是一种非紧迫、可中断的内核函数,因为他们在执行过程是开中断的。

tasklet是在软中断之上实现的,所以在内核代码中“软中断”通常表示可延迟函数的所有种类。另外一个被广泛使用的术语“中断上下文”表示内核当前正在执行一个中断处理程序或一个可延迟函数。

2. 软中断的实现

内核2.6.31中使用有限个软中断,所有的软中断类型定义如下。

/*PLEASE, avoid to allocate new softirqs, if you need not _really_ high frequencythreaded job scheduling. For almost all the purposes
taskletsare more than enough. F.e. all serial device BHs et
al.should be converted to tasklets, not to softirqs.
 */
enum
{
    HI_SOFTIRQ=0,
    TIMER_SOFTIRQ,
    NET_TX_SOFTIRQ,
    NET_RX_SOFTIRQ,
    BLOCK_SOFTIRQ,
    TASKLET_SOFTIRQ,
    SCHED_SOFTIRQ,
    HRTIMER_SOFTIRQ,
    RCU_SOFTIRQ,  /*Preferable RCU should always be the last softirq */
    NR_SOFTIRQS
};

内核中共有NR_SOFTIRQS种软中断,标号从0到NR_SOFTIRQS-1,优先级由高到低,即HI_SOFTIRQ的优先级最高,在执行软中断处理函数时,将按照优先级的顺序来执行。

2.1 相关数据结构

在每个进程的thread_info结构中,都有一个抢占计数器preempt_count。

struct thread_info {
    ……
    __u32      cpu;       /* current CPU */
    int    preempt_count;    /* 0 => preemptable */
    ……
};

在thread_info结构体中,preempt_count成员用来跟踪内核抢占和内核控制路径的嵌套,这个整型数包含三个计数器和两个标记位:

  • bits 0-7:这个计数器表示在内核代码中禁用本地内核抢占的次数,等于0表示允许内核抢占。
  • bits 8-15:可延迟函数被禁用的程度,为0表示可延迟函数处于激活状态。
  • bits 16-25:本地CPU上中断处理程序的嵌套数,嵌套(nested)数还受堆栈大小的限制,所以不一定能达到1024。
  • bit 26:是否处于不可屏蔽中断(NMI)上下文中。
  • bit 28:PREEMPT_ACTIVE标记,在进程调度的时候表示进程是通过抢占而被调度的。

每个进程都有一个thread_info结构,所以每个进程都有自己的preempt_count,通过current_thread_info()->preempt_count可以获取该计数器。

每个CPU都维护一个全局的irq_cpustat_t结构体,在mips中该结构体只有一个成员:挂起的软中断的掩码,它是32位整型,也就是说系统最多支持32个软中断,现有的软中断在上面已列出。

typedef struct {
    unsigned int __softirq_pending;
}____cacheline_aligned irq_cpustat_t;
irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned;

通过查看__softirq_pending成员的值(每个bit位对应一个软中断号)就可以知道哪些软中断正等待被处理,函数接口为local_softirq_pending()。

#define__IRQ_STAT(cpu, member) (irq_stat[cpu].member)
#define local_softirq_pending() \
       __IRQ_STAT(smp_processor_id(),__softirq_pending)

2.2 处理softirq

注册软中断:

所有软中断的处理函数都放到一个全局数组softirq_vec中,

static struct softirq_action softirq_vec[NR_SOFTIRQS]__cacheline_aligned_in_smp;

注册软中断处理函数的接口为open_softirq():

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

该函数接受两个参数:软中断类型的标号nr和将要注册的处理函数action。从第二个参数action可以看出,注册的处理函数需要一个参数:指向特定类型软中断的struct softirq_action实例&#

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值