转自: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版本的实时补丁中,正是这一段代码实现了中断的线程化:

  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

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

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

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

__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函数的完整形式如下:

  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不为空即可将该中断线程化。

linux中断线程化分析【转】的更多相关文章

  1. 记一个实时Linux的中断线程化问题

    背景 有一个项目对实时性要求比较高,于是在linux内核上打了RT_PREEMPT补丁. 最终碰到的一个问题是,芯片本身性能不强,CPU资源不足,急需优化. 初步分析 看了下cpu占用率,除了主应用之 ...

  2. linux中断源码分析 - 中断发生(三)

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 回顾 上篇文章linux中断源码分析 - 初始化(二)已经描述了中断描述符表和中断描述符数组的初始化,由于在初始 ...

  3. linux中断源码分析 - 软中断(四)

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 在上一篇文章中,我们看到中断实际分为了两个部分,俗称就是一部分是硬中断,一部分是软中断.软中断是专门用于处理中断 ...

  4. linux中断源码分析 - 初始化(二)

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 本篇文章主要讲述源码中是如何对中断进行一系列的初始化的. 回顾 在上一篇概述中,介绍了几个对于中断来说非常重要的 ...

  5. linux中断源码分析 - 概述(一)

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 关于中断和异常 一般在书中都会把中断和异常一起说明,因为它们具有相同的特点,同时也有不同的地方.在CPU里,中断 ...

  6. Linux中断 - GIC代码分析

    一.前言 GIC(Generic Interrupt Controller)是ARM公司提供的一个通用的中断控制器,其architecture specification目前有四个版本,V1-V4(V ...

  7. 【转】Linux 中断学习之小试牛刀篇

    原文网址:http://www.linuxidc.com/Linux/2011-02/32129.htm 前言 在前面分析了中断的基本原理后,就可以写一个内核中断程序来体验以下,也可以借此程序继续深入 ...

  8. Linux中断管理 (1)Linux中断管理机制

    目录: <Linux中断管理> <Linux中断管理 (1)Linux中断管理机制> <Linux中断管理 (2)软中断和tasklet> <Linux中断管 ...

  9. Linux中断 - 驱动申请中断API

    一.前言 本文主要的议题是作为一个普通的驱动工程师,在撰写自己负责的驱动的时候,如何向Linux Kernel中的中断子系统注册中断处理函数?为了理解注册中断的接口,必须了解一些中断线程化(threa ...

随机推荐

  1. Atom IDE开发工具, ASCII艺术评论, ninimap 插件

    1 ASCII Art Comments One neat trick is to use ASCII art to create huge comments visible in the minim ...

  2. [剑指Offer] 56.删除链表中重复的结点

    题目描述 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针. 例如,链表1->2->3->3->4->4->5 处理后 ...

  3. Windows7系统目录迁移:Users,Progr…

    微软设计了比如:我的文档.我的OOXX,之类的东西,在WIN7下面更连游戏.下载等等目录都设计好了,我也很乖巧的把各种文件都分门别类的放进去了. 同时也很厉害的设计在了“%HOMEDRIVE%”里面, ...

  4. 【Python】爬虫与反爬虫大战

    爬虫与发爬虫的厮杀,一方为了拿到数据,一方为了防止爬虫拿到数据,谁是最后的赢家? 重新理解爬虫中的一些概念 爬虫:自动获取网站数据的程序反爬虫:使用技术手段防止爬虫程序爬取数据误伤:反爬虫技术将普通用 ...

  5. Android UI设计的基本元素有哪些

    在android app开发如火如荼的今天,如何让自己的App受人欢迎.如何增加app的下载量和使用量....成为很多android应用开发前,必须讨论的问题.而ui设计则是提升客户视觉体验度.提升下 ...

  6. Android <Android应用开发实战> 资源类型<二>

    1.菜单资源菜单不仅可以在onCreateContextMenu或onCreateOptionsMenu方法中通过代码创建,还可以在res/menu目录中建立相应的菜单资源文件,并在上面两个方法中加载 ...

  7. redis中如何对 key 进行分类

    因为redis中的 hash是不支持设置过期时间的,如果我们要 设置过期时间,还要分类存储,可以用下面折中的方法 其实就是我们把 key 定义的有规律一些,通过在key的字符串内部 分类,上图只是因为 ...

  8. some interesting words

    No one gets rich betting against the market. Never bet against the Fed. Bulls make money, bears make ...

  9. 面包旅行Android业务设计分析

    面包旅行的业务设计不错,Android app也是清晰简洁又大方的样子,所以画了个业务脑图出来. 重要的几个业务特点分析如下: 1.账号绑定社交账号,方便社交推广 2.城市猎人活动,通过内容.时间.地 ...

  10. tomcat 访问400 的一种情况

    tomcat 高版本对访问url做了较高的校验,如果url中包含特殊字符,tomcat会自动拦截,返回400错误.如果要包含特殊字符,需要事先进行转译. 我原来用的apache-tomcat-6.0. ...