特别声明:本系列文章LiAnLab.org著作权所有,转载请注明出处。by  @宋宝华Barry

底半部:线程化IRQ

线程化中断的支持在2009年已经进入Linux官方内核,详见Thomas Gleixner的patch:

http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=3aa551c9b4c40018f0e261a178e3d25478dc04a9

该patch提供一个能力,驱动可以通过

int request_threaded_irq(unsigned int irq, irq_handler_t handler,

irq_handler_t thread_fn, unsigned long irqflags,

const char *devname, void *dev_id)

申请一个线程化的IRQ,kernel会为中断的底半部创建一个名字为irq/%d-%s的线程,%d对应着中断号。其中顶半部(硬中断)handler在做完必要的处理工作之后,会返回IRQ_WAKE_THREAD,之后kernel会唤醒irq/%d-%s线程,而该kernel线程会调用thread_fn函数,因此,该线程成为底半部。在后续维护的过程中,笔者曾参与进一步完善该功能的讨论,后续patch包括nested、oneshot等的支持,详见patch:

http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=399b5da29b9f851eb7b96e2882097127f003e87c

http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=70aedd24d20e75198f5a0b11750faabbb56924e2

http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=b25c340c195447afb1860da580fe2a85a6b652c5

该机制目前在kernel中使用已经十分广泛,可以认为是继softirq(含tasklet)和workqueue之后的又一大中断底半部方式。

顶半部:强制线程化

在使能Linux RT-Preempt后,默认情况下会强制透过request_irq()申请的IRQ的顶半部函数在线程中执行,我们都知道request_irq的原型为:

static inline int __must_check

request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,

const char *name, void *dev)

{

return request_threaded_irq(irq, handler, NULL, flags, name, dev);

}

这意味着通过request_irq()申请的IRQ,在没有Rt-Preepmt的情况下,kernel并不会为其创建irq线程,因为它在最终调用request_threaded_irq()的时候传递的thread_fn为NULL。

如果使能了RT-Preempt Patch的情况下,其中的genirq-force-threading.patch会强制ARM使用threaded irq:

Index: linux-stable/arch/arm/Kconfig

===================================================================

--- linux-stable.orig/arch/arm/Kconfig

+++ linux-stable/arch/arm/Kconfig

@@ -40,6 +40,7 @@ config ARM

select GENERIC_IRQ_SHOW

select ARCH_WANT_IPC_PARSE_VERSION

select HARDIRQS_SW_RESEND

+ select IRQ_FORCED_THREADING

select CPU_PM if (SUSPEND || CPU_IDLE)

select GENERIC_PCI_IOMAP

select HAVE_BPF_JIT

在RT-Preempt Patch中,会针对使能了IRQ_FORCED_THREADING的情况,对这一原先没有线程化IRQ的case进行强制线程化,代码见__setup_irq():

887 static int

888 __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)

889 {

890 ...

903

904 /*

905 * Check whether the interrupt nests into another interrupt

906 * thread.

907 */

908 nested = irq_settings_is_nested_thread(desc);

909 if (nested) {

910 ...

920 } else {

921 if (irq_settings_can_thread(desc))

922 irq_setup_forced_threading(new);

923 }

925 /*

926 * Create a handler thread when a thread function is supplied

927 * and the interrupt does not nest into another interrupt

928 * thread.

929 */

930 if (new->thread_fn && !nested) {

931 struct task_struct *t;

932

933 t = kthread_create(irq_thread, new, "irq/%d-%s", irq,

934 new->name);

935 ...

939 /*

940 * We keep the reference to the task struct even if

941 * the thread dies to avoid that the interrupt code

942 * references an already freed task_struct.

943 */

944 get_task_struct(t);

945 new->thread = t;

946 }

我们重点看一下其中的921行:

867 static void irq_setup_forced_threading(struct irqaction *new)

868 {

869 if (!force_irqthreads)

870 return;

871 if (new->flags & (IRQF_NO_THREAD | IRQF_PERCPU | IRQF_ONESHOT))

872 return;

873

874 new->flags |= IRQF_ONESHOT;

875

876 if (!new->thread_fn) {

877 set_bit(IRQTF_FORCED_THREAD, &new->thread_flags);

878 new->thread_fn = new->handler;

879 new->handler = irq_default_primary_handler;

880 }

881 }

第878行和879行,强制将原先的handler复制给thread_fn,而又强制把原来的handler变更为irq_default_primary_handler(),而这个函数,其实神马都不做,只是直接返回IRQ_WAKE_THREAD:

613 /*

614 * Default primary interrupt handler for threaded interrupts. Is

615 * assigned as primary handler when request_threaded_irq is called

616 * with handler == NULL. Useful for oneshot interrupts.

617 */

618 static irqreturn_t irq_default_primary_handler(int irq, void *dev_id)

619 {

620 return IRQ_WAKE_THREAD;

621 }

第874的IRQF_ONESHOT就用到了我们前面说的oneshot功能。

所以,RT-Preempt实际上是把原先的顶半部底半部化了,而现在伪造了一个假的顶半部,它只是直接返回一个IRQ_WAKE_THREAD标记而已。

我们来看一下一个中断发生后,Linux RT-Preempt处理的全过程,首先是会跳到

arch/arm/kernel/entry-armv.S

arch/arm/include/asm/entry-macro-multi.S

中的汇编入口,再进入arm/kernel/irq.c下的asm_do_IRQ 、handle_IRQ,之后generic的handle_irq_event_percpu()被调用:

133 handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action)

134 {

135 irqreturn_t retval = IRQ_NONE;

136 unsigned int flags = 0, irq = desc->irq_data.irq;

137

138 do {

139 irqreturn_t res;

140

141 trace_irq_handler_entry(irq, action);

142 res = action->handler(irq, action->dev_id);

143 trace_irq_handler_exit(irq, action, res);

144

145 if (WARN_ONCE(!irqs_disabled(),"irq %u handler %pF enabled interruptsn",

146 irq, action->handler))

147 local_irq_disable();

148

149 switch (res) {

150 case IRQ_WAKE_THREAD:

151 /*

152 * Catch drivers which return WAKE_THREAD but

153 * did not set up a thread function

154 */

155 if (unlikely(!action->thread_fn)) {

156 warn_no_thread(irq, action);

157 break;

158 }

159

160 irq_wake_thread(desc, action);

161

162 /* Fall through to add to randomness */

163 case IRQ_HANDLED:

164 flags |= action->flags;

165 break;

166

167 default:

我们关注其中的第142行,本质上是调用irq_default_primary_handler(),接到150行,由于 irq_default_primary_handler()返回了IRQ_WAKE_THREAD,因此,generic的中断处理流程会执行 irq_wake_thread(desc, action);去唤醒前面的irq/%d-%s线程,该线程的代码是

789 static int irq_thread(void *data)

790 {

791 static const struct sched_param param = {

792 .sched_priority = MAX_USER_RT_PRIO/2,

793 };

794 struct irqaction *action = data;

795 struct irq_desc *desc = irq_to_desc(action->irq);

796 irqreturn_t (*handler_fn)(struct irq_desc *desc,

797 struct irqaction *action);

798

799 if (force_irqthreads && test_bit(IRQTF_FORCED_THREAD,

800 &action->thread_flags))

801 handler_fn = irq_forced_thread_fn;

802 else

803 handler_fn = irq_thread_fn;

804

805 sched_setscheduler(current, SCHED_FIFO, ¶m);

806 current->irq_thread = 1;

807

808 while (!irq_wait_for_interrupt(action)) {

809 irqreturn_t action_ret;

810

811 irq_thread_check_affinity(desc, action);

812

813 action_ret = handler_fn(desc, action);

814 if (!noirqdebug)

815 note_interrupt(action->irq, desc, action_ret);

816

817 wake_threads_waitq(desc);

818 }

819

820 /*

821 * This is the regular exit path. __fr

其中的813行会调用最终的被赋值给thread_fn的原来的handler,这样原来的中断顶半部就整个在irq_thread里面执行了,实现了所谓的顶半部的线程化。

绕开顶半部线程化

当然,在使能了RT-Preempt的情况之下,我们仍然可以绕开顶半部线程化的过程,避免前面的强势变更,只需要申请中断的时候设置IRQ_NOTHREAD标志,如其中的patch:

Subject: arm: Mark pmu interupt IRQF_NO_THREAD

From: Thomas Gleixner

Date: Wed, 16 Mar 2011 14:45:31 +0100

PMU interrupt must not be threaded. Remove IRQF_DISABLED while at it

as we run all handlers with interrupts disabled anyway.

Signed-off-by: Thomas Gleixner

---

arch/arm/kernel/perf_event.c | 2 +-

1 file changed, 1 insertion(+), 1 deletion(-)

Index: linux-stable/arch/arm/kernel/perf_event.c

===================================================================

--- linux-stable.orig/arch/arm/kernel/perf_event.c

+++ linux-stable/arch/arm/kernel/perf_event.c

@@ -430,7 +430,7 @@ armpmu_reserve_hardware(struct arm_pmu *

}

err = request_irq(irq, handle_irq,

- IRQF_DISABLED | IRQF_NOBALANCING,

+ IRQF_NOBALANCING | IRQF_NO_THREAD,

"arm-pmu", armpmu);

if (err) {

r_err("unable to request IRQ%d for ARM PMU countersn",

linux rt patch 强实时,Linux RT(2)-硬实时Linux(RT-Preempt Patch)的中断线程化相关推荐

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

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

  2. Linux中断子系统---中断申请request_irq()与中断线程化request_threaded_irq()

    一.申请中断request_irq() Linux中使用中断需要先进行申请,申请中断的API函数如下: int request_irq(unsigned int irq,irq_handler_t h ...

  3. arm linux内核实时补丁,宋宝华: Linux实时补丁的原理和实践

    2012年的文章,重新在微信公众号发表. 第一章:硬实时Linux(RT-Preempt Patch)在PC上的编译.使用和测试第二章:硬实时Linux(RT-Preempt Patch)的中断线程化 ...

  4. 【Linux进程、线程、任务调度】四多核下负载均衡 中断负载均衡,RPS软中断负载均衡 cgroups与CPU资源分群分配 Linux为什么不是硬实时 preempt-rt对Linux实时性的改造

    学习交流加 个人qq: 1126137994 个人微信: liu1126137994 学习交流资源分享qq群: 962535112 上一篇文章(点击链接:点击链接阅读上一篇文章)讲了: CPU/IO消 ...

  5. 硬实时RTLinux?为Linux打实时preempt_rt补丁

    开发环境为vm-ware创建的ubuntu 20.04虚拟机,4G内存,80G硬盘,4核处理器. 一.准备工作 1.内核与补丁下载: 下载与Linux系统相近版本的内核源码(Linux系统版本可通过 ...

  6. 实时操作系统LynxOS、QNX、Linux的分析和比较

    ----本文对四种实时操作系统(RTOS)特性进行分析和比较.它们是:Lynx实时系统公司的LynxOS.QNX软件系统有限公司的QNX以及两种具有代表性的实时Linux--新墨西哥工学院的RT-Li ...

  7. linux实时realtime,康佳特与OSADL携手优化 Real-Time Linux 的支持 顺利实现硬实时

    德国康佳特科技,宣布与开源自动化开发实验室(OSADL, Open Source Automation Development Lab) 合作优化对实时Linux的板级支持,并且该板已在OSADL测试 ...

  8. 超干干货:Linux 系统最强总结~

    更多专业文档请访问 www.itilzj.com 因公众号更改推送规则,请点"在看"并加"星标"第一时间获取精彩分享 Linux 基础 操作系统 操作系统 Op ...

  9. 【Linux 内核】实时调度类 ① ( 进程分类 | 实时进程、普通进程 | Linux 内核 SCHED_FIFO、SCHED_RR 调度策略 | 实时调度实体 sched_rt_entity )

    文章目录 一.进程分类 ( 实时进程 | 普通进程 ) 二.Linux 内核调度策略 1.SCHED_FIFO 调度策略 2.SCHED_RR 调度策略 三.实时调度实体 sched_rt_entit ...

最新文章

  1. 20145231 《信息安全系统设计基础》第11周学习总结
  2. 对实体类的所有String类型的成员变量值trim
  3. 阿里云(一)云存储OSS的命令行osscmd的安装和使用
  4. 什么是Internet
  5. elasticsearch mapping之index
  6. CodeForces - 600E Lomsat gelral(树上启发式合并)
  7. pycharm镜像源_pycharm安装第三方库
  8. jquery-表格的增删编辑演练-有一个小bug的
  9. matlab移相变压器,18脉移相变压器+三相不可控桥式整流的MATLAB仿真
  10. 申通完美支撑“双11”亿级包裹背后的云基础设施
  11. JAVA基础【刘意】27天全集【Day02小结】
  12. 知行之桥®中文版EDI系统正式发布
  13. 产品经理技能学习:流程图绘制及规范
  14. 【有利可图网】PS实战系列:PS+SAI把照片制成唯美手绘效果
  15. share.weiyun.com 微云无法打开 解决办法
  16. 双非计算机硕士何去何从(2)
  17. 网页怎么算切屏_十种切屏抓取方法(图形)
  18. shell脚本实践:自动清理文件,以时间方式形成路径的图片或者是Excel、pdf等文件
  19. CCF计算机软件能力认证试题练习:201612-2 工资计算
  20. linux 批量解压.7z脚本

热门文章

  1. PowerMock 入门
  2. 八种常见排序算法细讲
  3. Android开发 之《最强大脑》“数字华容道”
  4. Sun Jan 05 2020 00:00:00 GMT 0800 (中国标准时间) 时间转换为 2020-01-05 08:00:00
  5. 2022年电赛E题声源定位跟踪系统
  6. 反恐精英服务器维修,China.com 反恐精英专区
  7. 大一计算机专业自我总结,计算机专业大学的毕业生自我鉴定
  8. 电子商务系统的设计与实现:数据库设计
  9. 蓝桥六届 打印大X JAVA
  10. 八数码问题-8puzzle