`
haoningabc
  • 浏览: 1482241 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

softirq原理以及源码分析(转)

阅读更多
转 http://blog.csdn.net/ustc_dylan/article/details/6334000?reload 

Linux 的softirq机制是与SMP紧密不可分的。为此,整个softirq机制的设计与实现中自始自终都贯彻了一个思想:“谁触发,谁执行”(Who marks,Who runs),也即触发软中断的那个CPU负责执行它所触发的软中断,而且每个CPU都由它自己的软中断触发与控制机制。这个设计思想也使得softirq 机制充分利用了SMP系统的性能和特点。 多个softirq可以并行执行,甚至同一个softirq可以在多个processor上同时执行。


一、softirq的实现
     每个softirq在内核中通过struct softirq_action来表示,另外,通过全局属组softirq_vec标识当前内核支持的所有的softirq。
/* softirq mask and active fields moved to irq_cpustat_t in
 * asm/hardirq.h to get better cache usage. KAO
 */
struct softirq_action
{
    void    (*action)(struct softirq_action *);
};
static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
Linux内核最多可以支持32个softirq(思考:为什么是32个?),但当前只实现了10个,如下:
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
};

二、softirq处理函数

    struct softirq_action结构体中,只有一个函数指针成员action,即指向用户定义的softirq处理函数。当执行时,可以通过如下代码:
                     softirq_vec[i]->action(i);
    一个注册的softirq在执行之前必须被激活,术语称为"raise the softirq"。被激活的softirq通常并不会立即执行,一般会在之后的某个时刻检查当前系统中是否有被pending的softirq,如果有就去执行,Linux内核中检查是否有softirq挂起的检查点主要有以下三类:
(1)硬件中断代码返回的时候
/*
 * Exit an interrupt context. Process softirqs if needed and possible:
 */
void irq_exit(void)
{
    account_system_vtime(current);
    trace_hardirq_exit();
    sub_preempt_count(IRQ_EXIT_OFFSET);
    if (!in_interrupt() && local_softirq_pending())
        invoke_softirq();
    rcu_irq_exit();
#ifdef CONFIG_NO_HZ
    /* Make sure that timer wheel updates are propagated */
    if (idle_cpu(smp_processor_id()) && !in_interrupt() && !need_resched())
        tick_nohz_stop_sched_tick(0);
#endif
    preempt_enable_no_resched();
}

(2)ksoftirqd内核服务线程运行的时候
static int run_ksoftirqd(void * __bind_cpu)
{
    ... ...
        while (local_softirq_pending()) {
            /* Preempt disable stops cpu going offline.
             If already offline, we'll be on wrong CPU:
             don't process */
            if (cpu_is_offline((long)__bind_cpu))
                goto wait_to_die;
            do_softirq();
            preempt_enable_no_resched();
            cond_resched();
            preempt_disable();
            rcu_note_context_switch((long)__bind_cpu);
        }
        preempt_enable();
        set_current_state(TASK_INTERRUPTIBLE);
    }
    __set_current_state(TASK_RUNNING);
    return 0;
... ...
}

(3)在一些内核子系统中显示的去检查挂起的softirq
int netif_rx_ni(struct sk_buff *skb)
{
    int err;
    preempt_disable();
    err = netif_rx(skb);
    if (local_softirq_pending())
        do_softirq();
    preemptenable();
    return err;
}

下面重点分析以下do_softirq(),了解Linux内核到底是怎么来处理softirq的。
asmlinkage void do_softirq(void)
{
    unsigned long flags;
    struct thread_info *curctx;
    union irq_ctx *irqctx;
    u32 *isp;
    if (in_interrupt()) /*首先判断是否在中断上下文中*/
        return;
    local_irq_save(flags);
    if (local_softirq_pending()) {
        curctx = current_thread_info();
        irqctx = __get_cpu_var(softirq_ctx);
        irqctx->tinfo.task = curctx->task;
        irqctx->tinfo.previous_esp = current_stack_pointer;
        /* build the stack frame on the softirq stack */
        isp = (u32 *) ((char *)irqctx + sizeof(*irqctx));
        call_on_stack(__do_softirq, isp);
        /*
         * Shouldnt happen, we returned above if in_interrupt():
         */
        WARN_ON_ONCE(softirq_count());
    }
    local_irq_restore(flags);
}

实际的处理函数为__do_softirq:
asmlinkage void __do_softirq(void)
{
    struct softirq_action *h;
    __u32 pending;
    int max_restart = MAX_SOFTIRQ_RESTART; /*不启动ksoftirqd之前,最大的处理softirq的次数,经验值*/
    int cpu;
    /*取得当前被挂起的softirq,同时这里也解释了为什么Linux内核最多支持32个softirq,因为pending只有32bit*/
    pending = local_softirq_pending(); 
    account_system_vtime(current);
    __local_bh_disable((unsigned long)__builtin_return_address(0));
    lockdep_softirq_enter();
    cpu = smp_processor_id();
restart:
    /* Reset the pending bitmask before enabling irqs */
    set_softirq_pending(0);/*获取了pending的softirq之后,清空所有pending的softirq的标志*/
    local_irq_enable();

    h = softirq_vec;
    do {

        if (pending & 1) { /*从最低位开始,循环右移逐位处理pending的softirq*/
            int prev_count = preempt_count();
            kstat_incr_softirqs_this_cpu(h - softirq_vec);
            trace_softirq_entry(h, softirq_vec);

            h->action(h); /*执行softirq的处理函数*/
            trace_softirq_exit(h, softirq_vec);
            if (unlikely(prev_count != preempt_count())) {
                printk(KERN_ERR "huh, entered softirq %td %s %p"
                 "with preempt_count %08x,"
                 " exited with %08x?/n", h - softirq_vec,
                 softirq_to_name[h - softirq_vec],
                 h->action, prev_count, preempt_count());
                preempt_count() = prev_count;
            }
            rcu_bh_qs(cpu);

        }
        h++;
        pending >>= 1;  /*循环右移*/
    } while (pending);
    local_irq_disable();

    pending = local_softirq_pending();
    if (pending && --max_restart)  /*启动ksoftirqd的阈值*/
        goto restart;
    if (pending)  /*启动ksoftirqd去处理softirq,此时说明pending的softirq比较多,比较频繁,上面的处理过程中,又不断有softirq被pending*/
        wakeup_softirqd();
    lockdep_softirq_exit();

 

    account_system_vtime(current);
    _local_bh_enable();
}

三、使用softirq
     softirq一般用在对实时性要求比较强的地方,当前的Linux内核中,只有两个子系统直接使用了softirq:网络子系统和块设备子系统。另外,增加新的softirq需要重新编译内核,因此,除非必须需要,最好考虑tasklet和kernel timer是否适合当前需要。
     如果必须需要使用softirq,那么需要考虑的一个重要的问题就是新增加的softirq的优先级,默认情况下,softirq的数值越小优先级越高,根据实际经验,新增加的softirq最好在BLOCK_SOFTIRQ和TASKLET_SOFTIRQ之间。
     softirq的处理函数通过open_softirq进行注册,此函数接收两个参数,一个是softirq的整数索引,另一个是该softirq对应的处理函数。例如在网络子系统中,注册了如下两个softirq及其处理函数:
open_softirq(NET_TX_SOFTIRQ, net_tx_action);
open_softirq(NET_RX_SOFTIRQ, net_rx_action);
    前面提到,软中断处理函数注册后,还需要将该软中断激活,此软中断才能被执行,激活操作是通过raise_softirq函数来实现,在网络子系统中激活代码如下:
/* Called with irq disabled */
static inline void ____napi_schedule(struct softnet_data *sd,
                 struct napi_struct *napi)
{
    list_add_tail(&napi->poll_list, &sd->poll_list);
    __raise_softirq_irqoff(NET_RX_SOFTIRQ);
}

这里的__raise_softirq_irqoff和raise_softirq的区别是,前者在事先已经关中断的情况下可以被使用,后者自己完成中断的关闭和恢复。
分享到:
评论

相关推荐

    Linux的内核软中断(softirq)执行分析.zip_leavinghzf_linux_theory989

    另外,内核源码中包含了大量的软中断处理函数,通过阅读和分析这些代码,可以深入理解软中断的具体实现。 在修改内核的软中断部分时,需要格外小心,因为这直接影响到系统的稳定性和性能。任何改动都应遵循内核开发...

    cpu占用率查看源码

    对于开发者来说,了解这些源码有助于理解系统的工作原理,优化性能监控工具,甚至排查系统性能问题。 在Linux系统中,CPU占用率可以通过读取/proc/stat文件获取。这个文件包含了系统运行的各种统计信息,包括CPU...

    tasklet.pdf

    ### Tasklet原理与源码分析 #### 一、Tasklet基本概念 Tasklet是一种用于Linux内核中的中断下半部机制,它主要建立在softirq的基础上。这种机制在本质上与softirq相似,但提供了更为简单的编程接口和较为宽松的锁...

    linux内核IMQ源码实现分析.pdf

    通过对内核源码的分析,我们可以深入了解IMQ的工作原理和技术细节。 首先,数据包在协议栈中的截留和重新注入是通过NF_QUEUE机制实现的。NF_QUEUE是一个内核的Netfilter模块,它提供了一种将数据包传递给用户空间...

    cs8900_linux驱动代码

    在阅读和理解这个驱动代码时,需要熟悉Linux内核编程接口、网络协议栈的工作原理以及相关的硬件知识。此外,理解设备的硬件手册也是必要的,因为它们详细描述了设备的寄存器布局和操作方法。 总的来说,cs8900 ...

    Linux操作系统源代码详细分析.pdf

    本文件旨在深入分析Linux操作系统的源代码,通过对核心部分的解读来帮助读者更好地理解Linux内核的工作原理。 #### 二、Linux操作系统特性 1. **抢先式多任务处理**:Linux支持真正的多任务处理,能够同时运行多个...

    linux设备驱动+源代码

    在Linux环境下,驱动程序是开源的,开发者可以通过阅读和修改源代码来理解其工作原理,甚至根据需要进行定制。这份"Linux设备驱动+源代码"的资料,对于学习和研究Linux内核、驱动开发以及硬件交互具有极大的价值。 ...

    Linux2.6内核标准教程(共计8-- 第1个)

    5.5.1 softirq延迟处理 234 5.5.2 tasklet延迟处理 239 5.5.3 work queue延迟处理 242 第6章 时间度量 249 6.1 硬件支持 250 6.1.1 实时钟RTC 250 6.1.2 系统时钟 250 6.2 软件架构 252 6.2.1 ...

    Linux2.6内核标准教程(共计8--第6个)

    5.5.1 softirq延迟处理 234 5.5.2 tasklet延迟处理 239 5.5.3 work queue延迟处理 242 第6章 时间度量 249 6.1 硬件支持 250 6.1.1 实时钟RTC 250 6.1.2 系统时钟 250 6.2 软件架构 252 6.2.1 ...

    Linux2.6内核标准教程(共计8--第3个)

    5.5.1 softirq延迟处理 234 5.5.2 tasklet延迟处理 239 5.5.3 work queue延迟处理 242 第6章 时间度量 249 6.1 硬件支持 250 6.1.1 实时钟RTC 250 6.1.2 系统时钟 250 6.2 软件架构 252 6.2.1 ...

    Linux2.6内核标准教程(共计8--第4个)

    5.5.1 softirq延迟处理 234 5.5.2 tasklet延迟处理 239 5.5.3 work queue延迟处理 242 第6章 时间度量 249 6.1 硬件支持 250 6.1.1 实时钟RTC 250 6.1.2 系统时钟 250 6.2 软件架构 252 6.2.1 ...

    Linux2.6内核标准教程(共计8--第2个)

    5.5.1 softirq延迟处理 234 5.5.2 tasklet延迟处理 239 5.5.3 work queue延迟处理 242 第6章 时间度量 249 6.1 硬件支持 250 6.1.1 实时钟RTC 250 6.1.2 系统时钟 250 6.2 软件架构 252 6.2.1 ...

    Linux2.6内核标准教程(共计8--第7个)

    5.5.1 softirq延迟处理 234 5.5.2 tasklet延迟处理 239 5.5.3 work queue延迟处理 242 第6章 时间度量 249 6.1 硬件支持 250 6.1.1 实时钟RTC 250 6.1.2 系统时钟 250 6.2 软件架构 252 6.2.1 ...

    Linux2.6内核标准教程(共计8--第5个)

    5.5.1 softirq延迟处理 234 5.5.2 tasklet延迟处理 239 5.5.3 work queue延迟处理 242 第6章 时间度量 249 6.1 硬件支持 250 6.1.1 实时钟RTC 250 6.1.2 系统时钟 250 6.2 软件架构 252 6.2.1 ...

    Linux2.6内核标准教程(共计8--第8个)

    5.5.1 softirq延迟处理 234 5.5.2 tasklet延迟处理 239 5.5.3 work queue延迟处理 242 第6章 时间度量 249 6.1 硬件支持 250 6.1.1 实时钟RTC 250 6.1.2 系统时钟 250 6.2 软件架构 252 6.2.1 ...

    hrtimer.rar_high_hrtimer_hrtimer pudn

    2. **API函数**: `hrtimer_start_range_ns`、`hrtimer_forward`、`hrtimer_get_softirq`等函数的声明,提供了操作hrtimer的接口。 3. **枚举类型**: 如`hrtimer_mode`定义了定时器的运行模式,如单次、周期性等。 ...

Global site tag (gtag.js) - Google Analytics