linux中断线程化分析【转】

简介:

转自:http://blog.csdn.net/qq405180763/article/details/24120895

最近在为3.8版本的Linux内核打RT_PREEMPT补丁,并且优化系统实时性,这篇文章主要对RTlinux中中断线程化部分进行分析。我们知道在RT_PREEMPT补丁中之所以要将中断线程化就是因为硬中断的实时性太高,会影响实时进程的实时性,所以需要将中断处理程序线程化并设置优先级,使中断处理线程的优先级比实时进程优先级低,从而提高系统实时性。

网上看到一些网友说在2.6.25.8版本的内核,linux引入了中断线程化,具体是不是2.6.25.8版本开始引入中断线程化我没有去求证,因为版本比较老了改动很多,但据我的查证从2.6.30开始内核引入request_threaded_irq函数,从这个版本开始可以通过在申请中断时为request_irq设置不同的参数决定是否线程化该中断。而在2.6.39版内核__setup_irq引入irq_setup_forced_threading函数,开始可以通过#  define force_irqthreads(true)强制使中断线程化,那么从这个版本开始想实现中断线程化就已经变得很简单了,让force_irqthreads为真即可,所以在3.8版本的实时补丁中,正是这一段代码实现了中断的线程化:

[plain]  view plain  copy print?
  1. #ifdef CONFIG_IRQ_FORCED_THREADING  
  2. -extern bool force_irqthreads;  
  3. +# ifndef CONFIG_PREEMPT_RT_BASE  
  4. +   extern bool force_irqthreads;  
  5. +# else  
  6. +#  define force_irqthreads (true)  
  7. +# endif  
  8.  #else  
  9. -#define force_irqthreads   (0)  
  10. +#define force_irqthreads   (false)  
  11.  #endif  

下面我们开始正式介绍中断线程化是怎么实现的。

Linux内核常见申请中断的函数request_irq,在内核源码include/linux/interrupt.h头文件中可以看到request_irq仅包含return request_threaded_irq(irq, handler, NULL, flags, name, dev);调用,request_threaded_irq函数在源码目录kernel/irq/manage.c文件中,下面通过分析manage.c中各个相关函数解读中断线程化的实现过程。

根据request_irq的调用,首先分析request_threaded_irq

[plain]  view plain  copy print?
  1. int request_threaded_irq(unsigned int irq, irq_handler_t handler,  
  2.              irq_handler_t thread_fn, unsigned long irqflags,  
  3.              const char *devname, void *dev_id)  
  4. {  
  5.     struct irqaction *action;  
  6.     struct irq_desc *desc;  
  7.     int retval;  
  8.   
  9.     /*  
  10.      * Sanity-check: shared interrupts must pass in a real dev-ID,  
  11.      * otherwise we'll have trouble later trying to figure out  
  12.      * which interrupt is which (messes up the interrupt freeing  
  13.      * logic etc).  
  14.      */  
  15.     if ((irqflags & IRQF_SHARED) && !dev_id)    //共享中断必须有唯一确定的设备号,不然中断处理函数找不到发出中断请求的设备,注释写的很清楚  
  16.         return -EINVAL;  
  17.   
  18.     desc = irq_to_desc(irq);  
  19.     if (!desc)  
  20.         return -EINVAL;  
  21.   
  22.     if (!irq_settings_can_request(desc) ||  
  23.         WARN_ON(irq_settings_is_per_cpu_devid(desc)))  
  24.         return -EINVAL;  
  25.   
  26.     if (!handler) { //handler和thread_fn都没有指针传入肯定是出错了,有thread_fn无handler则将irq_default_primary_handler给handler  
  27.         if (!thread_fn)  
  28.             return -EINVAL;  
  29.         handler = irq_default_primary_handler;  
  30.     }  
  31.   
  32.     action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);  
  33.     if (!action)  
  34.         return -ENOMEM;  
  35.   
  36.     action->handler = handler;  
  37.     action->thread_fn = thread_fn;  
  38.     action->flags = irqflags;  
  39.     action->name = devname;  
  40.     action->dev_id = dev_id;  
  41.   
  42.     chip_bus_lock(desc);  
  43.     retval = __setup_irq(irq, desc, action);    //在__setup_irq中确定是否线程化并完成中断处理函数绑定  
  44.     chip_bus_sync_unlock(desc);  
  45.   
  46.     if (retval)  
  47.         kfree(action);  
  48.   
  49. #ifdef CONFIG_DEBUG_SHIRQ_FIXME  
  50.     if (!retval && (irqflags & IRQF_SHARED)) {  
  51.         /*  
  52.          * It's a shared IRQ -- the driver ought to be prepared for it  
  53.          * to happen immediately, so let's make sure....  
  54.          * We disable the irq to make sure that a 'real' IRQ doesn't  
  55.          * run in parallel with our fake.  
  56.          */  
  57.         unsigned long flags;  
  58.   
  59.         disable_irq(irq);  
  60.         local_irq_save(flags);  
  61.   
  62.         handler(irq, dev_id);  
  63.   
  64.         local_irq_restore(flags);  
  65.         enable_irq(irq);  
  66.     }  
  67. #endif  
  68.     return retval;  
  69. }  

request_threaded_irq函数基本上是将传入的参数放到action结构体,然后调用__setup_irq函数,线程化的具体过程在__setup_irq函数中

[plain]  view plain  copy print?
  1. static int  
  2. __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)  
  3. {  
  4.     struct irqaction *old, **old_ptr;  
  5.     unsigned long flags, thread_mask = 0;  
  6.     int ret, nested, shared = 0;  
  7.     cpumask_var_t mask;  
  8.   
  9.     if (!desc)  
  10.         return -EINVAL;  
  11.   
  12.     if (desc->irq_data.chip == &no_irq_chip)  
  13.         return -ENOSYS;  
  14.     if (!try_module_get(desc->owner))  
  15.         return -ENODEV;  
  16.   
  17.     /*  
  18.      * Check whether the interrupt nests into another interrupt  
  19.      * thread.  
  20.      */  
  21.     nested = irq_settings_is_nested_thread(desc);  
  22.     if (nested) {  
  23.         if (!new->thread_fn) {  
  24.             ret = -EINVAL;  
  25.             goto out_mput;  
  26.         }  
  27.         /*  
  28.          * Replace the primary handler which was provided from  
  29.          * the driver for non nested interrupt handling by the  
  30.          * dummy function which warns when called.  
  31.          */  
  32.         new->handler = irq_nested_primary_handler;  
  33.     } else {  
  34.         if (irq_settings_can_thread(desc))  //request_irq调用通过设置参数_IRQ_NOTHREAD=0线程化,  
  35.                             //没有手动设置IRQ_NOTHREAD=1的中断都被线程化。Linux内核从2.6.39版本开始对中断线程化  
  36.             irq_setup_forced_threading(new);    //实时补丁使force_irqthreads=true,开启强制线程化中断  
  37.     }  
  38.   
  39.     /*  
  40.      * Create a handler thread when a thread function is supplied  
  41.      * and the interrupt does not nest into another interrupt  
  42.      * thread.  
  43.      */  
  44.     if (new->thread_fn && !nested) {  
  45.         struct task_struct *t;  
  46.         static const struct sched_param param = {  
  47.             .sched_priority = MAX_USER_RT_PRIO/2,   //所有被线程化中断优先级都为50  
  48.         };  
  49.   
  50.         t = kthread_create(irq_thread, new, "irq/%d-%s", irq,   //为中断创建内核线程  
  51.                    new->name);  
  52.         if (IS_ERR(t)) {  
  53.             ret = PTR_ERR(t);  
  54.             goto out_mput;  
  55.         }  
  56.   
  57.         sched_setscheduler(t, SCHED_FIFO, ¶m);  
  58.   
  59.         /*  
  60.          * We keep the reference to the task struct even if  
  61.          * the thread dies to avoid that the interrupt code  
  62.          * references an already freed task_struct.  
  63.          */  
  64.         get_task_struct(t);  
  65.         new->thread = t;  
  66.         /*  
  67.          * Tell the thread to set its affinity. This is  
  68.          * important for shared interrupt handlers as we do  
  69.          * not invoke setup_affinity() for the secondary  
  70.          * handlers as everything is already set up. Even for  
  71.          * interrupts marked with IRQF_NO_BALANCE this is  
  72.          * correct as we want the thread to move to the cpu(s)  
  73.          * on which the requesting code placed the interrupt.  
  74.          */  
  75.         set_bit(IRQTF_AFFINITY, &new->thread_flags);  
  76.     }  
  77.   
  78.     if (!alloc_cpumask_var(&mask, GFP_KERNEL)) {  
  79.         ret = -ENOMEM;  
  80.         goto out_thread;  
  81.     }  
  82.   
  83.     /*  
  84.      * Drivers are often written to work w/o knowledge about the  
  85.      * underlying irq chip implementation, so a request for a  
  86.      * threaded irq without a primary hard irq context handler  
  87.      * requires the ONESHOT flag to be set. Some irq chips like  
  88.      * MSI based interrupts are per se one shot safe. Check the  
  89.      * chip flags, so we can avoid the unmask dance at the end of  
  90.      * the threaded handler for those.  
  91.      */  
  92.     if (desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)  
  93.         new->flags &= ~IRQF_ONESHOT;  
  94.   
  95.     /*  
  96.      * The following block of code has to be executed atomically  
  97.      */  
  98.     raw_spin_lock_irqsave(&desc->lock, flags);  
  99.     old_ptr = &desc->action;  
  100.     old = *old_ptr; //action和desc都是指针,用指向指针的指针获取action的地址,再使old指向action  
  101.     if (old) {  //如果该中断号的处理程序链表desc->action本身就是空,就无所谓共享了  
  102.         /*  
  103.          * Can't share interrupts unless both agree to and are  
  104.          * the same type (level, edge, polarity). So both flag  
  105.          * fields must have IRQF_SHARED set and the bits which  
  106.          * set the trigger type must match. Also all must  
  107.          * agree on ONESHOT.  
  108.          */  
  109.         if (!((old->flags & new->flags) & IRQF_SHARED) ||  
  110.             ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK) ||  
  111.             ((old->flags ^ new->flags) & IRQF_ONESHOT))  
  112.             goto mismatch;  
  113.   
  114.         /* All handlers must agree on per-cpuness */  
  115.         if ((old->flags & IRQF_PERCPU) !=  
  116.             (new->flags & IRQF_PERCPU))  
  117.             goto mismatch;  
  118.   
  119.         /* add new interrupt at end of irq queue */  
  120.         do {  
  121.             /*  
  122.              * Or all existing action->thread_mask bits,  
  123.              * so we can find the next zero bit for this  
  124.              * new action.  
  125.              */  
  126.             thread_mask |= old->thread_mask;  
  127.             old_ptr = &old->next;  
  128.             old = *old_ptr; //在desc->action链表中找到空指针,为里后面将new加进去  
  129.         } while (old);  
  130.         shared = 1;  
  131.     }  
  132.   
  133.     /*  
  134.      * Setup the thread mask for this irqaction for ONESHOT. For  
  135.      * !ONESHOT irqs the thread mask is 0 so we can avoid a  
  136.      * conditional in irq_wake_thread().  
  137.      */  
  138.     if (new->flags & IRQF_ONESHOT) {  
  139.         /*  
  140.          * Unlikely to have 32 resp 64 irqs sharing one line,  
  141.          * but who knows.  
  142.          */  
  143.         if (thread_mask == ~0UL) {  
  144.             ret = -EBUSY;  
  145.             goto out_mask;  
  146.         }  
  147.         /*  
  148.          * The thread_mask for the action is or'ed to  
  149.          * desc->thread_active to indicate that the  
  150.          * IRQF_ONESHOT thread handler has been woken, but not  
  151.          * yet finished. The bit is cleared when a thread  
  152.          * completes. When all threads of a shared interrupt  
  153.          * line have completed desc->threads_active becomes  
  154.          * zero and the interrupt line is unmasked. See  
  155.          * handle.c:irq_wake_thread() for further information.  
  156.          *  
  157.          * If no thread is woken by primary (hard irq context)  
  158.          * interrupt handlers, then desc->threads_active is  
  159.          * also checked for zero to unmask the irq line in the  
  160.          * affected hard irq flow handlers  
  161.          * (handle_[fasteoi|level]_irq).  
  162.          *  
  163.          * The new action gets the first zero bit of  
  164.          * thread_mask assigned. See the loop above which or's  
  165.          * all existing action->thread_mask bits.  
  166.          */  
  167.         new->thread_mask = 1 << ffz(thread_mask);  
  168.   
  169.     } else if (new->handler == irq_default_primary_handler &&  
  170.            !(desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)) {  
  171.         /*  
  172.          * The interrupt was requested with handler = NULL, so  
  173.          * we use the default primary handler for it. But it  
  174.          * does not have the oneshot flag set. In combination  
  175.          * with level interrupts this is deadly, because the  
  176.          * default primary handler just wakes the thread, then  
  177.          * the irq lines is reenabled, but the device still  
  178.          * has the level irq asserted. Rinse and repeat....  
  179.          *  
  180.          * While this works for edge type interrupts, we play  
  181.          * it safe and reject unconditionally because we can't  
  182.          * say for sure which type this interrupt really  
  183.          * has. The type flags are unreliable as the  
  184.          * underlying chip implementation can override them.  
  185.          */  
  186.         pr_err("Threaded irq requested with handler=NULL and !ONESHOT for irq %d\n",  
  187.                irq);  
  188.         ret = -EINVAL;  
  189.         goto out_mask;  
  190.     }  
  191.   
  192.     if (!shared) {  //中断处理链表为空,自己创建链表  
  193.         init_waitqueue_head(&desc->wait_for_threads);  
  194.   
  195.         /* Setup the type (level, edge polarity) if configured: */  
  196.         if (new->flags & IRQF_TRIGGER_MASK) {  
  197.             ret = __irq_set_trigger(desc, irq,  
  198.                     new->flags & IRQF_TRIGGER_MASK);  
  199.   
  200.             if (ret)  
  201.                 goto out_mask;  
  202.         }  
  203.   
  204.         desc->istate &= ~(IRQS_AUTODETECT | IRQS_SPURIOUS_DISABLED | \  
  205.                   IRQS_ONESHOT | IRQS_WAITING);  
  206.         irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);  
  207.   
  208.         if (new->flags & IRQF_PERCPU) {  
  209.             irqd_set(&desc->irq_data, IRQD_PER_CPU);  
  210.             irq_settings_set_per_cpu(desc);  
  211.         }  
  212.   
  213.         if (new->flags & IRQF_ONESHOT)  
  214.             desc->istate |= IRQS_ONESHOT;  
  215.   
  216.         if (irq_settings_can_autoenable(desc))  
  217.             irq_startup(desc, true);  
  218.         else  
  219.             /* Undo nested disables: */  
  220.             desc->depth = 1;  
  221.   
  222.         /* Exclude IRQ from balancing if requested */  
  223.         if (new->flags & IRQF_NOBALANCING) {  
  224.             irq_settings_set_no_balancing(desc);  
  225.             irqd_set(&desc->irq_data, IRQD_NO_BALANCING);  
  226.         }  
  227.   
  228.         if (new->flags & IRQF_NO_SOFTIRQ_CALL)  
  229.             irq_settings_set_no_softirq_call(desc);  
  230.   
  231.         /* Set default affinity mask once everything is setup */  
  232.         setup_affinity(irq, desc, mask);  
  233.   
  234.     } else if (new->flags & IRQF_TRIGGER_MASK) {  
  235.         unsigned int nmsk = new->flags & IRQF_TRIGGER_MASK;  
  236.         unsigned int omsk = irq_settings_get_trigger_mask(desc);  
  237.   
  238.         if (nmsk != omsk)  
  239.             /* hope the handler works with current  trigger mode */  
  240.             pr_warning("irq %d uses trigger mode %u; requested %u\n",  
  241.                    irq, nmsk, omsk);  
  242.     }  
  243.   
  244.     new->irq = irq;  
  245.     *old_ptr = new; //添加到desc->action链表  
  246.   
  247.     /* Reset broken irq detection when installing new handler */  
  248.     desc->irq_count = 0;  
  249.     desc->irqs_unhandled = 0;  
  250.   
  251.     /*  
  252.      * Check whether we disabled the irq via the spurious handler  
  253.      * before. Reenable it and give it another chance.  
  254.      */  
  255.     if (shared && (desc->istate & IRQS_SPURIOUS_DISABLED)) {  
  256.         desc->istate &= ~IRQS_SPURIOUS_DISABLED;  
  257.         __enable_irq(desc, irq, false);  
  258.     }  
  259.   
  260.     raw_spin_unlock_irqrestore(&desc->lock, flags);  
  261.   
  262.     /*  
  263.      * Strictly no need to wake it up, but hung_task complains  
  264.      * when no hard interrupt wakes the thread up.  
  265.      */  
  266.     if (new->thread)  
  267.         wake_up_process(new->thread);    //内核线程开始运行  
  268.   
  269.     register_irq_proc(irq, desc);   //创建/proc/irq/目录及文件(smp_affinity,smp_affinity_list 等 )  
  270.     new->dir = NULL;  
  271.     register_handler_proc(irq, new);  
  272.     free_cpumask_var(mask); //创建proc/irq/<irq>/handler/   
  273.   
  274.     return 0;  
  275.   
  276. mismatch:  
  277.     if (!(new->flags & IRQF_PROBE_SHARED)) {  
  278.         pr_err("Flags mismatch irq %d. %08x (%s) vs. %08x (%s)\n",  
  279.                irq, new->flags, new->name, old->flags, old->name);  
  280. #ifdef CONFIG_DEBUG_SHIRQ  
  281.         dump_stack();  
  282. #endif  
  283.     }  
  284.     ret = -EBUSY;  
  285.   
  286. out_mask:  
  287.     raw_spin_unlock_irqrestore(&desc->lock, flags);  
  288.     free_cpumask_var(mask);  
  289.   
  290. out_thread:  
  291.     if (new->thread) {  
  292.         struct task_struct *t = new->thread;  
  293.   
  294.         new->thread = NULL;  
  295.         kthread_stop(t);  
  296.         put_task_struct(t);  
  297.     }  
  298. out_mput:  
  299.     module_put(desc->owner);  
  300.     return ret;  
  301. }  

__setup_irq的内容比较多点,首先通过nested判断该中断是否属于其他中断进程,即和别的中断共享同一个中断号,如果不是,判断是否强制将该中断线程化,很明显打了实时补丁后使能强制线程化中断,强制线程化如果thread_fn为空会使thread_fn指向handler,而handler指向默认的句柄函数,其实在强制中断线程化没有开启的情况下,request_threaded_irq函数根据thread_fn是否为空判断是否将该中断线程化。这里强制线程化后thread_fn显然不会为空。
接下来因为是首次在该中断线创建处理函数,申请一个内核线程,设置线程调度策略(FIFO)和优先级(50),为了让使用该中断号的其他进程共享这条中断线,还必须建立一个中断处理进程action的单向链表,设置一些共享标识等。但是如果现在申请的这个中断与其他已经建立中断内核线程的中断共享中断线,那么就不需要再次建立内核线程和队列,只需在队列中找到空指针(一般是末尾)并插入队列即可。做完这些之后唤醒内核进程(kthread_create)创建的内核进程不能马上执行,需要唤醒。

在Linux中申请中断还可以通过request_any_context_irq、devm_request_threaded_irq等函数,他们最终都调用request_threaded_irq,request_threaded_irq函数的完整形式如下:

[plain]  view plain  copy print?
  1. int request_threaded_irq(unsigned int irq, irq_handler_t handler,  
  2.              irq_handler_t thread_fn, unsigned long irqflags,  
  3.              const char *devname, void *dev_id)  

在没有强制中断线程化的时候,thread_fn不为空即可将该中断线程化。












本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sky-heaven/p/7457910.html,如需转载请自行联系原作者

相关文章
|
23天前
|
Linux C++
LInux下Posix的传统线程示例
LInux下Posix的传统线程示例
19 1
|
30天前
|
消息中间件 存储 算法
【软件设计师备考 专题 】操作系统的内核(中断控制)、进程、线程概念
【软件设计师备考 专题 】操作系统的内核(中断控制)、进程、线程概念
82 0
|
29天前
|
算法 Unix Linux
Linux与Qt线程优先级的对应关系:一次全面解析
Linux与Qt线程优先级的对应关系:一次全面解析
21 0
|
1月前
|
监控 Shell Linux
【Shell 命令集合 网络通讯 】Linux 分析串口的状态 statserial命令 使用指南
【Shell 命令集合 网络通讯 】Linux 分析串口的状态 statserial命令 使用指南
33 0
|
30天前
|
消息中间件 Linux 调度
【Linux 进程/线程状态 】深入理解Linux C++中的进程/线程状态:阻塞,休眠,僵死
【Linux 进程/线程状态 】深入理解Linux C++中的进程/线程状态:阻塞,休眠,僵死
67 0
|
1月前
|
资源调度 算法 Linux
Linux进程/线程的调度机制介绍:详细解析Linux系统中进程/线程的调度优先级规则
Linux进程/线程的调度机制介绍:详细解析Linux系统中进程/线程的调度优先级规则
73 0
|
2天前
|
固态存储 Ubuntu Linux
Linux(29) 多线程快速解压缩|删除|监视大型文件
Linux(29) 多线程快速解压缩|删除|监视大型文件
10 1
|
22天前
|
Prometheus 监控 数据可视化
linux分析方法与技巧
【4月更文挑战第3天】在Linux环境中,进行日志分析和系统性能分析的关键方法包括:使用`cat`, `less`, `tail`查看和过滤日志,`logrotate`管理日志文件,`rsyslog`或`syslog-ng`聚合日志,以及通过`top`, `mpstat`, `pidstat`, `free`, `iostat`, `netstat`, `strace`, `sar`, `dstat`等工具监控CPU、内存、磁盘I/O和网络。对于高级分析,可利用Brendan Gregg的性能工具,以及Grafana、Prometheus等可视化工具。
17 2
linux分析方法与技巧
|
28天前
|
监控 Linux 调度
【Linux 应用开发 】Linux 下应用层线程优先级管理解析
【Linux 应用开发 】Linux 下应用层线程优先级管理解析
45 0
|
29天前
|
监控 Linux Shell
Linux 进程问题调查探秘:分析和排查频繁创建进程问题
Linux 进程问题调查探秘:分析和排查频繁创建进程问题
39 0