Linux软中断
Linux软中断
在由内核执行的几个任务之间有些不是紧急的,在必要情况下他们可以延迟一段时间。一个中断处理程序的几个中断服务例程之间是串行执行的,并且通常在一个中断的处理程序结束前,不应该再次出现这个中断。相反,可延迟中断可以在开中断的情况下执行。linux中所谓的可延迟函数,包括软中断和tasklet以及通过中作队列执行的函数(这个以后说),软中断的分配是静态的(即值编译时定义),而tasklet的分配和初始化可以在运行时进行。
软中断
软中断所使用的数据结构定义为
- static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
其中softirq_action类型为一个函数指针,从这里也可以看出,软中断的个数是有限的有NR_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
- };
可以看出,一共10个软中断。
- struct softirq_action
- {
- void (*action)(struct softirq_action *);
- };
软中断的初始化
- /*初始化软中断*/
- void open_softirq(int nr, void (*action)(struct softirq_action *))
- {
- softirq_vec[nr].action = action;
- }
上面函数中,参数nr为softirq_vec[]数组的下标,初始化就是初始化softirq_vec[]数组内容。
初始化了软中断后,要执行,接下来要做的是激活软中断,运用下面函数
- /*激活软中断*/
- void raise_softirq(unsigned int nr)
- {
- unsigned long flags;
- /*保存eflags寄存器IF标志的状态值
- 并禁用本地CPU上得中断*/
- local_irq_save(flags);
- raise_softirq_irqoff(nr);
- local_irq_restore(flags);
- }
具体的激活工作由raise_softirq_irqoff函数实现
- /*
- * This function must run with irqs disabled!
- */
- inline void raise_softirq_irqoff(unsigned int nr)
- {
- /*把软中断标记为挂起*/
- __raise_softirq_irqoff(nr);
- /*
- * If we're in an interrupt or softirq, we're done
- * (this also catches softirq-disabled code). We will
- * actually run the softirq once we return from
- * the irq or softirq.
- *
- * Otherwise we wake up ksoftirqd to make sure we
- * schedule the softirq soon.
- */
- if (!in_interrupt())
- wakeup_softirqd();/*唤醒本地的内核线程*/
- }
守护线程softirqd就是对软中断的处理
- static int ksoftirqd(void * __bind_cpu)
- {
- /*设置进程状态为可中断*/
- set_current_state(TASK_INTERRUPTIBLE);
- while (!kthread_should_stop()) {/*不应该马上返回*/
- preempt_disable();
- /*实现软中断中一个关键数据结构是每个
- CPU都有的32位掩码(描述挂起的软中断),
- 他存放在irq_cpustat_t数据结构的__softirq_pending
- 字段中。为了获取或设置位掩码的值,
- 内核使用宏local_softirq_pending,他选择cpu的
- 软中断为掩码*/
- if (!local_softirq_pending()) {/*位掩码为0,标示没有软中断*/
- preempt_enable_no_resched();
- schedule();
- preempt_disable();
- }
- __set_current_state(TASK_RUNNING);
- 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_sched_qs((long)__bind_cpu);
- }
- preempt_enable();
- set_current_state(TASK_INTERRUPTIBLE);
- }
- __set_current_state(TASK_RUNNING);
- return 0;
- wait_to_die:
- preempt_enable();
- /* Wait for kthread_stop */
- set_current_state(TASK_INTERRUPTIBLE);
- while (!kthread_should_stop()) {
- schedule();
- set_current_state(TASK_INTERRUPTIBLE);
- }
- __set_current_state(TASK_RUNNING);
- return 0;
- }
|
评论暂时关闭