Linux下半部处理之软中断
Linux下半部处理之软中断
一、中断处理为什么要下半部?Linux在中断处理中间中断处理分了上半部和下半部,目的就是提高系统的响应能力和并发能力。通俗一点来讲:当一个中断产生,调用该中断对应的处理程序(上半部)然后告诉系统,对应的后半部可以执行了。然后中断处理程序就返回,下半部会在合适的时机有系统调用。这样一来就大大的减少了中断处理所需要的时间。二、那些工作应该放在上半部,那些应该放在下半部? 没有严格的规则,只有一些提示: 1、对时间非常敏感,放在上半部。 2、与硬件相关的,放在上半部。 3、不能被其他中断打断的工作,放在上半部。 以上三点之外的,考虑放在下半部。
三、下半部机制在Linux中是怎么实现的? 下半部在Linux中有以下实现机制: 1、BH(在2.5中删除) 2、任务队列(task queue,在2.5删除) 3、软中断(softirq,2.3开始。本文重点) 4、tasklet(2.3开始) 5、工作队列(work queue,2.5开始)
四、软中断是怎么实现的(以下代码出自2.6.32)? 软中断不会抢占另外一个软中断,唯一可以抢占软中断的是中断处理程序。 软中断可以在不同CPU上并发执行(哪怕是同一个软中断)
1、软中断是编译期间静态分配的,定义如下: struct softirq_action { void (*action)(struct softirq_action *); };
/* * PLEASE, avoid to allocate new softirqs, if you need not _really_ high * frequency threaded job scheduling. For almost all the purposes * tasklets are 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, BLOCK_IOPOLL_SOFTIRQ, TASKLET_SOFTIRQ, SCHED_SOFTIRQ, HRTIMER_SOFTIRQ, RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */ NR_SOFTIRQS };
/* * map softirq index to softirq name. update 'softirq_to_name' in * kernel/softirq.c when adding a new softirq. */ extern char *softirq_to_name[NR_SOFTIRQS];
static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
说明: (1)、软中断的个数书上说是32,看来到这个版本已经发生变化了。 (2)、void (*action)(struct softirq_action *);传递整个结构体指针在于当结构体成员发生变化是,接口不变。
2、系统执行软中断一个注册的软中断必须被标记后才会执行(触发软中断),通常中断处理程序会在返回前标记它的软中断。在下列地方,待处理的软中断会被执行: (1)、从一个硬件中断代码处返回。 (2)、在ksoftirqd内核线程。 (3)、在那些显示检查和执行待处理的软中断代码中。
ksoftirqd说明: 每个处理器都有一个这样的线程。所有线程的名字都叫做ksoftirq/n,区别在于n,它对应的是处理器的编号。在一个双CPU的机器上就���两个这样的线程,分别叫做ksoftirqd/0和ksoftirqd/1。为了保证只要有空闲的处理器,它们就会处理软中断,所以给每个处理器都分配一个这样的线程。
执行软中断的代码如下: asmlinkage void __do_softirq(void) { struct softirq_action *h; __u32 pending; int max_restart = MAX_SOFTIRQ_RESTART; int cpu;
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);
local_irq_enable();
h = softirq_vec;
do { if (pending & 1) { int prev_count = preempt_count(); kstat_incr_softirqs_this_cpu(h - softirq_vec);
trace_softirq_entry(h, softirq_vec); h->action(h); 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) goto restart;
if (pending) wakeup_softirqd();
lockdep_softirq_exit();
account_system_vtime(current); _local_bh_enable(); }
3、编写自己的软中断 (1)、分配索引,在HI_SOFTIRQ与NR_SOFTIRQS中间添加自己的索引号。 (2)、注册处理程序,处理程序:open_softirq(索引号,处理函数)。 (3)、触发你的软中断:raise_softirq(索引号)。
4、软中断处理程序注意 (1)、软中断处理程序执行的时候,允许响应中断,但自己不能休眠。 (2)、如果软中断在执行的时候再次触发,则别的处理器可以同时执行,所以加锁很关键。
评论暂时关闭