如何设置PREEMPT-RT中断线程优先级以优化中断线程化原理?

摘要:本文介绍实时linux方案PREEMPT-RT提升系统实时性的机制之一--中断线程化,以及中断线程优先级如何配置,希望能对你有所帮助。
目录一、什么是中断线程化1. 普通Linux中断处理2. 实时性的不足3. 中断线程化二、中断线程优先级配置使用chrt修改中断线程优先级使用rtirq修改中断线程优先级安装rtirqrtirq使用说明rtirq配置三、总结 本文介绍实时linux方案PREEMPT-RT提升系统实时性的机制之一--中断线程化,以及中断线程优先级配置,希望能为感兴趣的读者提供些许参考。 什么是实时 实时的分类 为什么普通Linux不实时? 常见的RTOS latency和jitter 一、什么是中断线程化 1. 普通Linux中断处理 中断是一种异步事件处理机制,用于响应硬件请求,它会打断进程的正常调度和执行,然后调用内核中断处理程序来响应设备的请求。 由于中断处理程序会打断其他进程的运行,所以,为了减少对正常进程运行调度的影响,中断处理程序就需要尽可能快地运行。如果中断本身要做的事情不多,那么处理起来也不会有太大问题;但如果中断要处理的事情很多,中断服务程序就有可能要运行很长时间。特别是,中断处理程序在响应中断时,还会临时关闭中断。若中断任务繁重,这就会导致上一次中断处理完成之前,其他中断都不能响应,甚至丢失中断。 所以Linux 将中断处理过程分成了两个阶段,也就是上半部和下半部: 上半部:快速处理硬件中断,inux不支持中断嵌套,它在中断禁止模式下运行,主要处理跟硬件紧密相关的或时间敏感的工作,即硬中断(Interrupt Request,IRQ)。 下半部:延迟处理上半部遗留任务,通常以内核线程方式运行,即软中断(SoftIRQ)。 硬中断处理程序(ISR)迅速响应硬件事件,执行最少工作,大部分任务推迟至软中断SoftIRQ或任务队列workqueue。 软中断是一种机制,用于在稍后执行较耗时的中断处理任务。一般以内核线程的方式执行,并且每个 CPU 都对应一个软中断内核线程,名字为 “ksoftirqd/CPU 编号”,如0 号 CPU 对应的软中断内核线程的名字就是 ksoftirqd/0。软中断在软中断上下文运行,执行时机有2个,一个是硬件中断返回后立即执行,这时和硬件中断一样,完全抢占进程上下文,不允许被调度和抢占,其执行时间为不超过2ms,如果执行的软中断超过了2ms会将软中断延迟到软中断线程 “ksoftirqd/CPU 编号”中执行。 系统中软硬件中断通过proc 文件系统来查看: /proc/softirqs 提供了软中断的运行情况; /proc/interrupts 提供了硬中断的运行情况。 2. 实时性的不足 传统的中断处理方式虽然提升了系统的吞吐量,但从实时任务响应实时性方面来看存在以下不足: 中断禁用:inux不支持中断嵌套,在硬中断处理过程中,其他中断会被屏蔽,增加了延迟。 中断串行处理:inux不支持中断嵌套,同一cpu上的外设中断处理串行执行,这在非紧急外设中断多的情况下导致紧急外设中断处理的不确定性。 不可预知的延迟:不同的驱动程序,硬中断处理程序执行需要的时间不同,不可预知,可能导致高优先级任务被中断抢占延迟执行,影响任务的实时性能。 软中断的不确定性:软中断在硬中断退出后前期处理是完全抢占进程上下文的,它的处理时间和调度顺序也可能影响到实时任务的执行。 这些不足使得普通linux中断处理方式难以满足高实时性要求的应用场景。 3. 中断线程化 为了解决上述问题,PREEMPT-RT补丁引入了中断线程化机制,于2009年合入linux主线版本2.6.30。顾名思义就是将原来硬件中断上下文的中断处理的部分也通过一个内核线程来处理,启用PREEMPT-RT后(系统抢占配置为Full Real Time Preemption)默认将除代码中明确指明非中断线程化的中断和系统硬件timer中断外的硬件中断均通过中断线程来处理,中断线程默认是一个实时线程,调度类为SCHED_FIFO,实时优先级为50。同时将SoftIRQ全部放到ksoftirqd/CPU线程中执行,ksoftirqd/CPU为普通非实时任务不变。通过ps命令可以看到中断线程。 ubuntu@work-host:~$ ps -ef | grep irq root 14 2 0 12月10 ? 00:02:44 [ksoftirqd/0] root 19 2 0 12月10 ? 00:00:08 [irq_work/0] root 25 2 0 12月10 ? 00:00:00 [irq_work/1] root 28 2 0 12月10 ? 00:00:00 [ksoftirqd/1] root 33 2 0 12月10 ? 00:00:04 [irq_work/2] root 36 2 0 12月10 ? 00:02:47 [ksoftirqd/2] root 41 2 0 12月10 ? 00:00:01 [irq_work/3] root 44 2 0 12月10 ? 00:03:09 [ksoftirqd/3] root 49 2 0 12月10 ? 00:00:04 [irq_work/4] root 52 2 0 12月10 ? 00:02:42 [ksoftirqd/4] root 57 2 0 12月10 ? 00:00:02 [irq_work/5] root 60 2 0 12月10 ? 00:02:28 [ksoftirqd/5] root 121 2 0 12月10 ? 00:00:00 [irq/9-acpi] root 136 2 0 12月10 ? 00:00:00 [irq/120-PCIe PM] root 137 2 0 12月10 ? 00:00:00 [irq/121-PCIe PM] root 138 2 0 12月10 ? 00:00:00 [irq/121-aerdrv] root 139 2 0 12月10 ? 00:00:00 [irq/121-s-aerdr] root 140 2 0 12月10 ? 00:00:00 [irq/122-PCIe PM] root 141 2 0 12月10 ? 00:00:00 [irq/122-aerdrv] root 142 2 0 12月10 ? 00:00:00 [irq/122-s-aerdr] root 143 2 0 12月10 ? 00:00:00 [irq/123-PCIe PM] root 144 2 0 12月10 ? 00:00:00 [irq/123-aerdrv] root 145 2 0 12月10 ? 00:00:00 [irq/123-s-aerdr] root 149 2 0 12月10 ? 00:00:00 [vfio-irqfd-clea] root 150 2 0 12月10 ? 00:00:00 [irq/8-rtc0] root 229 2 0 12月10 ? 00:13:52 [irq/124-xhci_hc] root 230 2 0 12月10 ? 00:00:00 [irq/16-i801_smb] root 281 2 0 12月10 ? 00:00:00 [irq/126-ahci[00] root 746 2 0 12月10 ? 00:00:00 [irq/142-mei_me] root 1089 2 0 12月10 ? 00:00:05 [irq/143-i915] root 1105 2 0 12月10 ? 00:00:00 [irq/144-snd_hda] root 1465 1 0 12月10 ? 00:01:24 /usr/sbin/irqbalance --foreground root 1731 2 0 12月10 ? 00:00:20 [irq/125-eno1] root 1734 2 0 12月10 ? 00:00:00 [irq/127-enp2s0] root 1735 2 0 12月10 ? 00:00:04 [irq/128-enp2s0-] .... 中断线程化通过以下方式提升了Linux系统的实时性: 减少中断禁用时间:由原本整个硬件中断处理过程都需要屏蔽全局中断,变成仅响应中断,唤醒中断线程部分会屏蔽全局中断,这部分只执行最少量的处理,减少了中断禁用的时间,降低了整体延迟。 优先级管理:中断处理程序作为内核线程,基本不会屏蔽全局中断,并且不同优先级的外设中断线程,可以被赋予不同的优先级,从而避免低优先级的外设中断阻塞高优先级外设、任务的执行。 可抢占性:中断线程作为可调度的内核线程,可以被高优先级任务抢占,使系统响应更加灵活和实时。 可预期性增强:中断处理时间变得更加可控和可预见,提高了系统的实时性能。 -系统吞吐量降低:中断线程化后,CPU上下文切换增多,那 CPU 的处理效率就会打一定折扣。 通过这些改进,PREEMPT-RT的中断线程化机制有效地解决了传统中断处理方式在实时性上的不足,使Linux系统能够更好地满足高实时性应用的需求。 二、中断线程优先级配置 所有外设的中断线程默认一个实时线程,调度类为SCHED_FIFO,实时优先级为50,对于实时性要求高的外设应该设置更高的优先级,如何才能修改中断线程的优先级?方式有两种,第一种最直接的办法,驱动代码中添加代码修改,但这样不灵活。另外一种是通过chrt工具来修改。 使用chrt修改中断线程优先级 例如,要将PID为1234的线程的实时优先级设置为50,可以执行以下命令: chrt -f -p 50 1234 这里,-f表示设置实时优先级,-p表示操作对象是进程。同样,只有root用户才能修改线程的实时优先级。 注意:在使用这些命令时,请确保已正确指定PID,以免对其他进程造成不必要的影响。 但直接使用chrt还是太原始,下面我给大家推荐一个工具--rtirq。 查看所有线程(任务)优先级: ps -eo pid,class,rtprio,ni,pri,pcpu,psr,time,stat,comm --sort -rtprio -T "PSR"列显示线程运行在哪个处理器(Core)。 只查看中断软中断线程优先级: ps -eo pid,class,rtprio,ni,pri,pcpu,psr,stat,comm --sort -rtprio | grep -E '(^[ |\t]*PID|irq)' 使用rtirq修改中断线程优先级 安装rtirq 如果你使用的是debian衍生发行版,还可以通过rtirq这个工具来修改中断线程优先级,直接从github拉取 git clone https://github.com/rncbc/rtirq 或者通过apt软件包管理器安装 sudo apt install rtirq-init 注意:rtirq工具依赖完整功能的ps和chrt,所以在一些使用busybox嵌入式系统中由于ps功能简单,rtirq运行不了。 rtirq使用说明 我们来看看rtirq-init是如何工作的,rtirq是一个脚本,位于/etc/init.d/rtirq,位于/etc/init.d目录下的的脚本系统启动后会自动执行,首先读取解析配置文件,然后根据配置文件来修改中断线程的优先级和调度策,最终执行的也是chrt命令,只不过它通过配置文件的方式提供了更多的灵活性配置,你可以自行分析脚本源码。 脚本/etc/init.d/rtirq接收的参数有start、stop、reset、restart(或)force-reload、status。 rtirq脚本执行时使用的配置文件依次为为/etc/sysconfig/rtirq、/etc/default/rtirq、/etc/rtirq.conf,前一个存在后面的就不生效。其中/etc/default/rtirq是安装后自动生成的模板文件,你可以直接修改它或者基于它重新新建一个。 rtirq配置 /etc/default/rtirq内容如下: # 我们要修改的IRQ线程服务名称,按下面的优先级及步长依次配置 # (从优先级高到低,以空格分隔列表)。 # RTIRQ_NAME_LIST="rtc snd usb i8042" #旧版本 RTIRQ_NAME_LIST="snd usb i8042" # 最高的优先级。 RTIRQ_PRIO_HIGH=90 # 优先级递减步长。 RTIRQ_PRIO_DECR=5 # 最低的优先级。 RTIRQ_PRIO_LOW=51 # 是否将所有 IRQ 线程重置为 SCHED_OTHER。 RTIRQ_RESET_ALL=0 # 当内核支持时, # 哪些服务不应被线程化(以空格分隔列表)。 RTIRQ_NON_THREADED="rtc snd" # 将被强制到最高实时优先级范围内的进程名(99-91)(从高到低,以空格分隔列表) # RTIRQ_HIGH_LIST="timer" RTIRQ_NAME_LIST表示我们要修改的中断线程集合,按优先级从RTIRQ_PRIO_HIGH到RTIRQ_PRIO_LOW高顺序排列,示例中的是snd(90)>usb(85)>i8042(80); RTIRQ_PRIO_HIGH表示第一个RTIRQ_NAME_LIST中第一个中断线程的优先级; RTIRQ_PRIO_DECR表示RTIRQ_NAME_LIST中的中断线程优先级从高到低的步长; RTIRQ_PRIO_LOW表示最低优先级; RTIRQ_RESET_ALL是否将RTIRQ_NAME_LIST外的所有 IRQ 线程重置为 SCHED_OTHER 如果我们不知道 IRQ 线程的名称,可以通过执行/etc/init.d/rtirq status来获取,同时能看到系统中中断线程的默认优先级: ubuntu@work-host:~$ /etc/init.d/rtirq status PID CLS RTPRIO NI PRI %CPU STAT COMMAND 1105 FF 90 - 130 0.0 S irq/144-snd_hda 229 FF 85 - 125 0.1 S irq/124-xhci_hc 121 FF 50 - 90 0.0 S irq/9-acpi 136 FF 50 - 90 0.0 S irq/120-PCIe PM 137 FF 50 - 90 0.0 S irq/121-PCIe PM 138 FF 50 - 90 0.0 S irq/121-aerdrv 150 FF 50 - 90 0.0 S irq/8-rtc0 230 FF 50 - 90 0.0 S irq/16-i801_smb 281 FF 50 - 90 0.0 S irq/126-ahci[00 746 FF 50 - 90 0.0 S irq/142-mei_me 1089 FF 50 - 90 0.0 S irq/143-i915 1731 FF 50 - 90 0.0 S irq/125-eno1 .... 1757 FF 50 - 90 0.0 S irq/141-enp4s0- 14 RR 1 - 41 0.0 S ksoftirqd/0 28 TS - 0 19 0.0 S ksoftirqd/1 36 TS - 0 19 0.0 S ksoftirqd/2 44 TS - 0 19 0.0 S ksoftirqd/3 52 TS - 0 19 0.0 S ksoftirqd/4 60 TS - 0 19 0.0 S ksoftirqd/5 .... 示例 先将所有线程优先级配置为0,配置文件如下: .... # Whether to reset all IRQ threads to SCHED_OTHER. RTIRQ_RESET_ALL=1 ... 执行: ubuntu@work-host:~$ sudo /etc/init.d/rtirq reset ubuntu@work-host:~$ sudo /etc/init.d/rtirq status PID CLS RTPRIO NI PRI %CPU STAT COMMAND 14 TS - 0 19 0.0 S ksoftirqd/0 28 TS - 0 19 0.0 S ksoftirqd/1 36 TS - 0 19 0.0 S ksoftirqd/2 44 TS - 0 19 0.0 S ksoftirqd/3 52 TS - 0 19 0.0 S ksoftirqd/4 60 TS - 0 19 0.0 S ksoftirqd/5 121 TS - 0 19 0.0 S irq/9-acpi 137 TS - 0 19 0.0 S irq/120-PCIe PM 138 TS - 0 19 0.0 S irq/121-PCIe PM 139 TS - 0 19 0.0 S irq/121-aerdrv 140 TS - 0 19 0.0 S irq/121-s-aerdr 141 TS - 0 19 0.0 S irq/122-PCIe PM 142 TS - 0 19 0.0 S irq/122-aerdrv 143 TS - 0 19 0.0 S irq/122-s-aerdr 144 TS - 0 19 0.0 S irq/123-PCIe PM 145 TS - 0 19 0.0 S irq/123-aerdrv 146 TS - 0 19 0.0 S irq/123-s-aerdr 151 TS - 0 19 0.0 S irq/8-rtc0 251 TS - 0 19 0.0 S irq/125-xhci_hc 252 TS - 0 19 0.0 S irq/16-i801_smb 299 TS - 0 19 0.0 S irq/126-ahci[00 776 TS - 0 19 0.0 S irq/142-mei_me 1121 TS - 0 19 0.0 S irq/143-i915 1136 TS - 0 19 0.0 S irq/144-snd_hda 1798 TS - 0 19 0.0 S irq/124-eno1 1799 TS - 0 19 0.0 S irq/127-enp2s0 1800 TS - 0 19 0.0 S irq/128-enp2s0- 1801 TS - 0 19 0.0 S irq/129-enp2s0- 1802 TS - 0 19 0.0 S irq/130-enp2s0- 1803 TS - 0 19 0.0 S irq/131-enp2s0- 1808 TS - 0 19 0.0 S irq/132-enp3s0 1809 TS - 0 19 0.0 S irq/133-enp3s0- 1810 TS - 0 19 0.0 S irq/134-enp3s0- 1811 TS - 0 19 0.0 S irq/135-enp3s0- 1812 TS - 0 19 0.0 S irq/136-enp3s0- 1822 TS - 0 19 0.0 S irq/137-enp4s0 1823 TS - 0 19 0.0 S irq/138-enp4s0- 1824 TS - 0 19 0.0 S irq/139-enp4s0- 1825 TS - 0 19 0.0 S irq/140-enp4s0- 1826 TS - 0 19 0.0 S irq/141-enp4s0- 修改rtc0、enp4s0的中断线程优先级,配置文件如下: # IRQ thread service names # (space separated list, from higher to lower priority). # RTIRQ_NAME_LIST="rtc snd usb i8042" # old RTIRQ_NAME_LIST="rtc enp4s0" # Highest priority. RTIRQ_PRIO_HIGH=90 # Priority decrease step. RTIRQ_PRIO_DECR=5 # Lowest priority. RTIRQ_PRIO_LOW=51 # Whether to reset all IRQ threads to SCHED_OTHER. RTIRQ_RESET_ALL=0 # On kernel configurations that support it, # which services should be NOT threaded # (space separated list). #RTIRQ_NON_THREADED="rtc snd" # Process names which will be forced to the # highest realtime priority range (99-91) # (space separated list, from highest to lower priority). # RTIRQ_HIGH_LIST="timer" 执行 ubuntu@work-host:~$ sudo /etc/init.d/rtirq start Setting IRQ priorities: stop [rtc] irq=8 pid=151: OK. Setting IRQ priorities: stop [enp4s0] irq=137 pid=1822: OK. Setting IRQ priorities: stop [enp4s0] irq=138 pid=1823: OK. Setting IRQ priorities: stop [enp4s0] irq=139 pid=1824: OK. Setting IRQ priorities: stop [enp4s0] irq=140 pid=1825: OK. Setting IRQ priorities: stop [enp4s0] irq=141 pid=1826: OK. ubuntu@work-host:~$ sudo /etc/init.d/rtirq status PID CLS RTPRIO NI PRI %CPU STAT COMMAND 151 FF 90 - 130 0.0 S irq/8-rtc0 1822 FF 85 - 125 0.0 S irq/137-enp4s0 1823 FF 84 - 124 0.0 S irq/138-enp4s0- 1824 FF 83 - 123 0.0 S irq/139-enp4s0- 1825 FF 82 - 122 0.0 S irq/140-enp4s0- 1826 FF 81 - 121 0.0 S irq/141-enp4s0- 14 TS - 0 19 0.0 S ksoftirqd/0 28 TS - 0 19 0.0 S ksoftirqd/1 36 TS - 0 19 0.0 S ksoftirqd/2 44 TS - 0 19 0.0 S ksoftirqd/3 52 TS - 0 19 0.0 S ksoftirqd/4 60 TS - 0 19 0.0 S ksoftirqd/5 121 TS - 0 19 0.0 S irq/9-acpi 137 TS - 0 19 0.0 S irq/120-PCIe PM 138 TS - 0 19 0.0 S irq/121-PCIe PM 139 TS - 0 19 0.0 S irq/121-aerdrv 140 TS - 0 19 0.0 S irq/121-s-aerdr 141 TS - 0 19 0.0 S irq/122-PCIe PM 142 TS - 0 19 0.0 S irq/122-aerdrv 143 TS - 0 19 0.0 S irq/122-s-aerdr 144 TS - 0 19 0.0 S irq/123-PCIe PM 145 TS - 0 19 0.0 S irq/123-aerdrv 146 TS - 0 19 0.0 S irq/123-s-aerdr 251 TS - 0 19 0.0 S irq/125-xhci_hc 252 TS - 0 19 0.0 S irq/16-i801_smb 299 TS - 0 19 0.0 S irq/126-ahci[00 776 TS - 0 19 0.0 S irq/142-mei_me 1121 TS - 0 19 0.0 S irq/143-i915 1136 TS - 0 19 0.0 S irq/144-snd_hda 1798 TS - 0 19 0.0 S irq/124-eno1 1799 TS - 0 19 0.0 S irq/127-enp2s0 1800 TS - 0 19 0.0 S irq/128-enp2s0- 1801 TS - 0 19 0.0 S irq/129-enp2s0- 1802 TS - 0 19 0.0 S irq/130-enp2s0- 1803 TS - 0 19 0.0 S irq/131-enp2s0- 1808 TS - 0 19 0.0 S irq/132-enp3s0 1809 TS - 0 19 0.0 S irq/133-enp3s0- 1810 TS - 0 19 0.0 S irq/134-enp3s0- 1811 TS - 0 19 0.0 S irq/135-enp3s0- 1812 TS - 0 19 0.0 S irq/136-enp3s0- 可以看到,irq/8-rtc0实时优先级为90,irq/137-enp4s0实时优先级为85,修改成功。 三、总结 本文主要介绍了中断线程化这一提高linux系统实时性的机制。通过将外设中断处理从屏蔽全局中断的硬件中断上下转移到线程上下文程执行,可以更好地控制中断处理的优先级和响应时间,但牺牲了部分系统的吞吐量。此外,文章还详细讨论了如何配置不同外设的中断线程优先级,以确保关键任务的实时性。这些配置方法对系统设计者在规划和优化实时应用的优先级时具有重要意义。