zoukankan      html  css  js  c++  java
  • 硬中断和软中断(转)

    来自:http://blog.csdn.net/zhangskd/article/details/21992933

     

    记录一个软中断问题:http://huoding.com/2013/10/30/296

    概述

    从本质上来讲,中断是一种电信号,当设备有某种事件发生时,它就会产生中断,通过总线把电信号发送给中断控制器。

    如果中断的线是激活的,中断控制器就把电信号发送给处理器的某个特定引脚。处理器于是立即停止自己正在做的事,

    跳到中断处理程序的入口点,进行中断处理。

    (1) 硬中断

    由与系统相连的外设(比如网卡、硬盘)自动产生的。主要是用来通知操作系统系统外设状态的变化。比如当网卡收到数据包

    的时候,就会发出一个中断。我们通常所说的中断指的是硬中断(hardirq)。

    (2) 软中断

    为了满足实时系统的要求,中断处理应该是越快越好。linux为了实现这个特点,当中断发生的时候,硬中断处理那些短时间

    就可以完成的工作,而将那些处理事件比较长的工作,放到中断之后来完成,也就是软中断(softirq)来完成。

    (3) 中断嵌套

    Linux下硬中断是可以嵌套的,但是没有优先级的概念,也就是说任何一个新的中断都可以打断正在执行的中断,但同种中断

    除外。软中断不能嵌套,但相同类型的软中断可以在不同CPU上并行执行。

    (4) 软中断指令

    int是软中断指令。

    中断向量表是中断号和中断处理函数地址的对应表。

    int n - 触发软中断n。相应的中断处理函数的地址为:中断向量表地址 + 4 * n。

    (5)硬中断和软中断的区别

    软中断是执行中断指令产生的,而硬中断是由外设引发的。

    硬中断的中断号是由中断控制器提供的,软中断的中断号由指令直接指出,无需使用中断控制器。

    硬中断是可屏蔽的,软中断不可屏蔽。

    硬中断处理程序要确保它能快速地完成任务,这样程序执行时才不会等待较长时间,称为上半部。

    软中断处理硬中断未完成的工作,是一种推后执行的机制,属于下半部。 

    开关

    (1) 硬中断的开关

    简单禁止和激活当前处理器上的本地中断:

    local_irq_disable();

    local_irq_enable();

    保存本地中断系统状态下的禁止和激活:

    unsigned long flags;

    local_irq_save(flags);

    local_irq_restore(flags);

    (2) 软中断的开关

    禁止下半部,如softirq、tasklet和workqueue等:

    local_bh_disable();

    local_bh_enable();

    需要注意的是,禁止下半部时仍然可以被硬中断抢占。

    (3) 判断中断状态

    #define in_interrupt() (irq_count()) // 是否处于中断状态(硬中断或软中断)

    #define in_irq() (hardirq_count()) // 是否处于硬中断

    #define in_softirq() (softirq_count()) // 是否处于软中断

    硬中断

    (1) 注册中断处理函数

    注册中断处理函数:

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. /** 
    2.  * irq: 要分配的中断号 
    3.  * handler: 要注册的中断处理函数 
    4.  * flags: 标志(一般为0) 
    5.  * name: 设备名(dev->name) 
    6.  * dev: 设备(struct net_device *dev),作为中断处理函数的参数 
    7.  * 成功返回0 
    8.  */  
    9.   
    10. int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,   
    11.     const char *name, void *dev);  

    中断处理函数本身:

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. typedef irqreturn_t (*irq_handler_t) (int, void *);  
    2.   
    3. /** 
    4.  * enum irqreturn 
    5.  * @IRQ_NONE: interrupt was not from this device 
    6.  * @IRQ_HANDLED: interrupt was handled by this device 
    7.  * @IRQ_WAKE_THREAD: handler requests to wake the handler thread 
    8.  */  
    9. enum irqreturn {  
    10.     IRQ_NONE,  
    11.     IRQ_HANDLED,  
    12.     IRQ_WAKE_THREAD,  
    13. };  
    14. typedef enum irqreturn irqreturn_t;  
    15. #define IRQ_RETVAL(x) ((x) != IRQ_NONE)  

    (2) 注销中断处理函数

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. /** 
    2.  * free_irq - free an interrupt allocated with request_irq 
    3.  * @irq: Interrupt line to free 
    4.  * @dev_id: Device identity to free 
    5.  * 
    6.  * Remove an interrupt handler. The handler is removed and if the 
    7.  * interrupt line is no longer in use by any driver it is disabled. 
    8.  * On a shared IRQ the caller must ensure the interrupt is disabled 
    9.  * on the card it drives before calling this function. The function does 
    10.  * not return until any executing interrupts for this IRQ have completed. 
    11.  * This function must not be called from interrupt context. 
    12.  */  
    13.   
    14. void free_irq(unsigned int irq, void *dev_id);  

    软中断

    (1) 定义

    软中断是一组静态定义的下半部接口,可以在所有处理器上同时执行,即使两个类型相同也可以。

    但一个软中断不会抢占另一个软中断,唯一可以抢占软中断的是硬中断。

    软中断由softirq_action结构体表示:

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. struct softirq_action {  
    2.     void (*action) (struct softirq_action *); /* 软中断的处理函数 */  
    3. };  

    目前已注册的软中断有10种,定义为一个全局数组:

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. static struct softirq_action softirq_vec[NR_SOFTIRQS];  
    2.   
    3. enum {  
    4.     HI_SOFTIRQ = 0, /* 优先级高的tasklets */  
    5.     TIMER_SOFTIRQ, /* 定时器的下半部 */  
    6.     NET_TX_SOFTIRQ, /* 发送网络数据包 */  
    7.     NET_RX_SOFTIRQ, /* 接收网络数据包 */  
    8.     BLOCK_SOFTIRQ, /* BLOCK装置 */  
    9.     BLOCK_IOPOLL_SOFTIRQ,  
    10.     TASKLET_SOFTIRQ, /* 正常优先级的tasklets */  
    11.     SCHED_SOFTIRQ, /* 调度程序 */  
    12.     HRTIMER_SOFTIRQ, /* 高分辨率定时器 */  
    13.     RCU_SOFTIRQ, /* RCU锁定 */  
    14.     NR_SOFTIRQS /* 10 */  
    15. };  

    (2) 注册软中断处理函数

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. /** 
    2.  * @nr: 软中断的索引号 
    3.  * @action: 软中断的处理函数 
    4.  */  
    5.   
    6. void open_softirq(int nr, void (*action) (struct softirq_action *))  
    7. {  
    8.     softirq_vec[nr].action = action;  
    9. }  

    例如:

    open_softirq(NET_TX_SOFTIRQ, net_tx_action);

    open_softirq(NET_RX_SOFTIRQ, net_rx_action);

    (3) 触发软中断 

    调用raise_softirq()来触发软中断。

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. void raise_softirq(unsigned int nr)  
    2. {  
    3.     unsigned long flags;  
    4.     local_irq_save(flags);  
    5.     raise_softirq_irqoff(nr);  
    6.     local_irq_restore(flags);  
    7. }  
    8.   
    9. /* This function must run with irqs disabled */  
    10. inline void rasie_softirq_irqsoff(unsigned int nr)  
    11. {  
    12.     __raise_softirq_irqoff(nr);  
    13.   
    14.     /* If we're in an interrupt or softirq, we're done 
    15.      * (this also catches softirq-disabled code). We will 
    16.      * actually run the softirq once we return from the irq 
    17.      * or softirq. 
    18.      * Otherwise we wake up ksoftirqd to make sure we 
    19.      * schedule the softirq soon. 
    20.      */  
    21.     if (! in_interrupt()) /* 如果不处于硬中断或软中断 */  
    22.         wakeup_softirqd(void); /* 唤醒ksoftirqd/n进程 */  
    23. }  


    Percpu变量irq_cpustat_t中的__softirq_pending是等待处理的软中断的位图,通过设置此变量

    即可告诉内核该执行哪些软中断。

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. static inline void __rasie_softirq_irqoff(unsigned int nr)  
    2. {  
    3.     trace_softirq_raise(nr);  
    4.     or_softirq_pending(1UL << nr);  
    5. }  
    6.   
    7. typedef struct {  
    8.     unsigned int __softirq_pending;  
    9.     unsigned int __nmi_count; /* arch dependent */  
    10. } irq_cpustat_t;  
    11.   
    12. irq_cpustat_t irq_stat[];  
    13. #define __IRQ_STAT(cpu, member) (irq_stat[cpu].member)  
    14. #define or_softirq_pending(x) percpu_or(irq_stat.__softirq_pending, (x))  
    15. #define local_softirq_pending() percpu_read(irq_stat.__softirq_pending)  

    唤醒ksoftirqd内核线程处理软中断。

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. static void wakeup_softirqd(void)  
    2. {  
    3.     /* Interrupts are disabled: no need to stop preemption */  
    4.     struct task_struct *tsk = __get_cpu_var(ksoftirqd);  
    5.   
    6.     if (tsk && tsk->state != TASK_RUNNING)  
    7.         wake_up_process(tsk);  
    8. }  

    在下列地方,待处理的软中断会被检查和执行:

    1. 从一个硬件中断代码处返回时

    2. 在ksoftirqd内核线程中

    3. 在那些显示检查和执行待处理的软中断的代码中,如网络子系统中

    而不管是用什么方法唤起,软中断都要在do_softirq()中执行。如果有待处理的软中断,

    do_softirq()会循环遍历每一个,调用它们的相应的处理程序。

    在中断处理程序中触发软中断是最常见的形式。中断处理程序执行硬件设备的相关操作,

    然后触发相应的软中断,最后退出。内核在执行完中断处理程序以后,马上就会调用

    do_softirq(),于是软中断开始执行中断处理程序完成剩余的任务。

    下面来看下do_softirq()的具体实现。

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. asmlinkage void do_softirq(void)  
    2. {  
    3.     __u32 pending;  
    4.     unsigned long flags;  
    5.   
    6.     /* 如果当前已处于硬中断或软中断中,直接返回 */  
    7.     if (in_interrupt())   
    8.         return;  
    9.   
    10.     local_irq_save(flags);  
    11.     pending = local_softirq_pending();  
    12.     if (pending) /* 如果有激活的软中断 */  
    13.         __do_softirq(); /* 处理函数 */  
    14.     local_irq_restore(flags);  
    15. }  
    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. /* We restart softirq processing MAX_SOFTIRQ_RESTART times, 
    2.  * and we fall back to softirqd after that. 
    3.  * This number has been established via experimentation. 
    4.  * The two things to balance is latency against fairness - we want 
    5.  * to handle softirqs as soon as possible, but they should not be 
    6.  * able to lock up the box. 
    7.  */  
    8. asmlinkage void __do_softirq(void)  
    9. {  
    10.     struct softirq_action *h;  
    11.     __u32 pending;  
    12.     /* 本函数能重复触发执行的次数,防止占用过多的cpu时间 */  
    13.     int max_restart = MAX_SOFTIRQ_RESTART;  
    14.     int cpu;  
    15.   
    16.     pending = local_softirq_pending(); /* 激活的软中断位图 */  
    17.     account_system_vtime(current);  
    18.     /* 本地禁止当前的软中断 */  
    19.     __local_bh_disable((unsigned long)__builtin_return_address(0), SOFTIRQ_OFFSET);  
    20.     lockdep_softirq_enter(); /* current->softirq_context++ */  
    21.     cpu = smp_processor_id(); /* 当前cpu编号 */  
    22.   
    23. restart:  
    24.     /* Reset the pending bitmask before enabling irqs */  
    25.     set_softirq_pending(0); /* 重置位图 */  
    26.     local_irq_enable();  
    27.     h = softirq_vec;  
    28.     do {  
    29.         if (pending & 1) {  
    30.             unsigned int vec_nr = h - softirq_vec; /* 软中断索引 */  
    31.             int prev_count = preempt_count();  
    32.             kstat_incr_softirqs_this_cpu(vec_nr);  
    33.   
    34.             trace_softirq_entry(vec_nr);  
    35.             h->action(h); /* 调用软中断的处理函数 */  
    36.             trace_softirq_exit(vec_nr);  
    37.   
    38.             if (unlikely(prev_count != preempt_count())) {  
    39.                 printk(KERN_ERR "huh, entered softirq %u %s %p" "with preempt_count %08x,"  
    40.                     "exited with %08x? ", vec_nr, softirq_to_name[vec_nr], h->action, prev_count,  
    41.                     preempt_count());  
    42.             }  
    43.             rcu_bh_qs(cpu);  
    44.         }  
    45.         h++;  
    46.         pending >>= 1;  
    47.     } while(pending);  
    48.   
    49.     local_irq_disable();  
    50.     pending = local_softirq_pending();  
    51.     if (pending & --max_restart) /* 重复触发 */  
    52.         goto restart;  
    53.   
    54.     /* 如果重复触发了10次了,接下来唤醒ksoftirqd/n内核线程来处理 */  
    55.     if (pending)  
    56.         wakeup_softirqd();   
    57.   
    58.     lockdep_softirq_exit();  
    59.     account_system_vtime(current);  
    60.     __local_bh_enable(SOFTIRQ_OFFSET);  
    61. }  

    (4) ksoftirqd内核线程

    内核不会立即处理重新触发的软中断。

    当大量软中断出现的时候,内核会唤醒一组内核线程来处理。

    这些线程的优先级最低(nice值为19),这能避免它们跟其它重要的任务抢夺资源。

    但它们最终肯定会被执行,所以这个折中的方案能够保证在软中断很多时用户程序不会

    因为得不到处理时间而处于饥饿状态,同时也保证过量的软中断最终会得到处理。

    每个处理器都有一个这样的线程,名字为ksoftirqd/n,n为处理器的编号。

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
      1. static int run_ksoftirqd(void *__bind_cpu)  
      2. {  
      3.     set_current_state(TASK_INTERRUPTIBLE);  
      4.     current->flags |= PF_KSOFTIRQD; /* I am ksoftirqd */  
      5.   
      6.     while(! kthread_should_stop()) {  
      7.         preempt_disable();  
      8.   
      9.         if (! local_softirq_pending()) { /* 如果没有要处理的软中断 */  
      10.             preempt_enable_no_resched();  
      11.             schedule();  
      12.             preempt_disable():  
      13.         }  
      14.   
      15.         __set_current_state(TASK_RUNNING);  
      16.   
      17.         while(local_softirq_pending()) {  
      18.             /* Preempt disable stops cpu going offline. 
      19.              * If already offline, we'll be on wrong CPU: don't process. 
      20.              */  
      21.              if (cpu_is_offline(long)__bind_cpu))/* 被要求释放cpu */  
      22.                  goto wait_to_die;  
      23.   
      24.             do_softirq(); /* 软中断的统一处理函数 */  
      25.   
      26.             preempt_enable_no_resched();  
      27.             cond_resched();  
      28.             preempt_disable();  
      29.             rcu_note_context_switch((long)__bind_cpu);  
      30.         }  
      31.   
      32.         preempt_enable();  
      33.         set_current_state(TASK_INTERRUPTIBLE);  
      34.     }  
      35.   
      36.     __set_current_state(TASK_RUNNING);  
      37.     return 0;  
      38.   
      39. wait_to_die:  
      40.     preempt_enable();  
      41.     /* Wait for kthread_stop */  
      42.     set_current_state(TASK_INTERRUPTIBLE);  
      43.     while(! kthread_should_stop()) {  
      44.         schedule();  
      45.         set_current_state(TASK_INTERRUPTIBLE);  
      46.     }  
      47.   
      48.     __set_current_state(TASK_RUNNING);  
      49.     return 0;  
  • 相关阅读:
    Charles截获iPhone网络请求
    android小Demo--圆球跟随手指轨迹移动
    《腾讯网UED体验设计之旅》读后感
    eatwhatApp开发实战(十四)
    [Unity2d系列教程] 006.Unity如何根据图片自动生成Animator
    [Unity2d系列教程] 005.Unity如何使用外部触控插件FingerGuesture
    eatwhatApp开发实战(十三)
    微服务平台技术架构
    Istio 流量劫持过程
    Istio 组件常用端口
  • 原文地址:https://www.cnblogs.com/sunxucool/p/5632287.html
Copyright © 2011-2022 走看看