新闻详情

新闻详情

首页 / 资讯中心 / 详情

Linux下半部机制详解:软中断、tasklet与线程化中断的选型

发布时间:2026/9/10 17:14:30来源:尧图网络
Linux下半部机制详解:软中断、tasklet与线程化中断的选型
作为一个在内核驱动里摸爬滚打多年的开发者我越来越觉得“linux 下半部Bottom Half机制”是理解整个中断处理体系的钥匙。很多人写驱动时不管三七二十一所有逻辑全塞进中断处理函数结果系统一跑起来就卡顿、丢中断甚至死锁最后排查半天才发现是中断上下文里干了不该干的事。搞懂下半部本质上就是搞懂“中断处理程序里到底能做什么、不能做什么以及那些不能做的活该往哪儿放”。这篇文章我想从底层逻辑讲到实战选型把软中断、tasklet、工作队列、线程化中断这几种下半部机制掰开揉碎。内容会覆盖它们的设计动机、执行流程、相互差异以及我在真实项目里踩过的坑和总结的排查方法。不管你是刚开始看内核的新手还是已经被中断问题折磨过的驱动老手这篇文章应该都能给你一些参考。1. 中断响应为什么非要拆成两半上半部/下半部的分工逻辑1.1 中断处理程序面临的两个硬约束要理解下半部得先理解中断处理程序本身有多“憋屈”。当一个中断信号到达CPU时处理器会立即跳转到对应的中断处理入口。这段代码必须满足两个铁律第一执行速度要极快。中断处理期间当前CPU核心上的其他任务全部暂停。如果是多核系统其他核心还能继续跑但触发中断的那个核心就“钉”在原地直到中断处理完。如果中断处理花了10毫秒那这个核心上的所有用户进程、内核线程、其他低优先级中断全部都得等10毫秒。这种延迟对于实时性要求高的场景是不可接受的。第二不能睡眠。中断处理程序运行在中断上下文atomic context不是任何进程的上下文。这意味着它没有所属的进程结构没有用户态地址空间也不能被调度器换出。一旦调用了一个可能睡眠的函数比如mutex_lock、kmalloc(GFP_KERNEL)、copy_from_user内核就会在运行时抛出“BUG: scheduling while atomic”或者直接死锁因为没人能唤醒它。这两条铁律决定了凡是耗时长、可能需要等待、涉及进程上下文的操作统统不能放在中断处理程序里。于是就有了拆分的思路——中断处理程序只做最紧急、最简短的事情比如读取硬件状态寄存器、清除中断标志、把数据搬运到内存缓冲区然后立即返回。剩下的那些“不急但必须做”的活挂到一个延后执行的机制上等系统空闲了再处理。1.2 拆分带来的设计收益从“一个硬实时任务”变成“两个软实时任务”把中断处理拆成上半部top half和下半部bottom half收益非常明显。上半部也就是我们常说的中断处理程序request_irq注册的那个handler追求的是“短平快”。它运行在关中断或屏蔽中断的上下文里必须迅速完成硬件交互避免其他中断被长时间阻塞。这里的核心操作通常包括读状态寄存器确认中断源、写寄存器清除中断标志、把硬件FIFO里的数据拷贝到内存中的ring buffer、更新一些简单的统计计数、然后触发对应的下半部。下半部则是上半部的“善后处理”。它不在中断上下文里那么严格受限可以在更宽松的环境下慢慢处理数据、解析协议、唤醒等待队列、调用内核API完成更复杂的逻辑。这种拆分最大的收益是中断延迟被压到最低系统响应性大幅提升。你用网卡收包时如果每来一个包都在中断处理程序里做完整的TCP/IP协议栈解析那网卡中断会被占死丢包率飙升整个网络基本瘫痪。但如果只是把包从硬件缓冲区搬到内存skb里然后标记一下软中断再立即返回那CPU很快就能继续处理其他事情协议栈解析交给软中断慢慢做。1.3 一个非常直观的例子网卡收包路径我们以网卡收包为例把整个过程串起来看。数据帧到达网卡硬件网卡通过DMA把数据写入内存中的接收环形队列RX ring。然后网卡触发一个中断。CPU进入网卡的中断处理程序这个处理程序做以下几件事读取中断状态寄存器确认是接收中断。写寄存器清除中断标志避免重复触发。把RX ring里的数据包封装成sk_buff结构挂到CPU的软中断队列上。调用napi_schedule触发NET_RX_SOFTIRQ软中断。中断处理程序返回。真正的协议栈处理比如IP解析、TCP校验、路由查找、把数据交给socket接收队列全部发生在NET_RX_SOFTIRQ这个软中断里。这样中断处理程序本身的执行时间被压缩到几微秒网卡在高负载下也能及时响应新的中断。我在一个嵌入式项目里有次深刻的体会设备用的是千兆网卡但CPU主频只有800MHz。刚开始我把数据包处理逻辑全放在中断里结果网络一跑流量整个系统就“假死”ping都ping不通。后来改成NAPI软中断的经典路径CPU占用率从接近100%降到了30%左右。这就是上半部/下半部拆分的威力。2. 下半部的三代演进从BH、tasklet到线程化中断2.1 远古的BH机制一个全局锁串起的脆弱方案Linux内核最早期引入的下半部机制就叫BHBottom Half这也是“下半部”这个名词最早的来源。它的实现非常简单粗暴内核定义了一个包含32个BH函数的数组每个BH函数对应一个编号。中断处理程序可以通过mark_bh(n)来标记第n个BH函数需要执行内核在合适的时候会遍历这个数组执行所有被标记的BH函数。你可能会问这有什么问题吗问题大了。任何一个时刻全局只有一个CPU能执行BH函数。如果CPU0正在执行某个BH函数CPU1想执行另一个BH函数它必须等CPU0执行完才行。这意味着什么意味着在多核系统上BH机制完全无法利用多核并行能力。而且BH函数的执行方式类似于“一次全跑完”没有优先级区分没有抢占控制一个耗时的BH函数会阻塞其他所有BH函数。我当时第一次看老版本内核代码时一度以为这种东西早就被淘汰了。2.6内核以后BH机制确实被删除了但它在历史发展中的地位无法抹去——它第一次让内核开发者形成了一个共识中断处理必须拆分延后执行的部分需要一套独立的管理机制。后面的tasklet和软中断本质上都是对BH机制的改良。2.2 tasklet的黄金时代串行化保证与驱动开发者的宠儿tasklet是紧接着BH出现的下一代下半部机制它解决了BH两个核心痛点多核并行和动态管理。tasklet在内核里的实现非常巧妙。每个tasklet绑定到一个CPU上运行可以设置TASKLET_SET_CPUS最关键的是同一个tasklet在同一时刻只可能在一个CPU上执行。如果你在CPU0上提交了一个taskletCPU0正在执行它此时CPU1也想执行这个taskletCPU1会被告知“已被调度执行但还没执行完”于是它不会重复执行。这个保证大大降低了驱动开发者的心智负担——你不用额外加锁去保护tasklet内部的数据因为同一个tasklet天然串行。用法上也非常简单void my_tasklet_func(unsigned long data); DECLARE_TASKLET(my_tasklet, my_tasklet_func, 0); // 中断上半部中调度 tasklet_schedule(my_tasklet); // 或者用动态初始化的方式 struct tasklet_struct t; tasklet_init(t, my_tasklet_func, 0); tasklet_schedule(t);当tasklet被调度后内核会在合适的时机执行它。tasklet的实现依赖于一种软中断——TASKLET_SOFTIRQ这个后面细说。在驱动开发的黄金时代tasklet几乎是下半部的默认选择因为它简单、安全、够用。2.3 线程化中断threaded IRQ的补位把中断变成“线程的活”tasklet虽好但有一个硬伤它依然运行在中断上下文依然不能睡眠。很多驱动场景下中断处理的下半部需要访问I2C总线、读写SD卡、等待DMA完成这些操作都可能导致阻塞。在tasklet里没法做那就只能退回到中断处理程序里做但那样上半部就变“胖”了违背了设计的初衷。线程化中断threaded IRQ的引入解决了这个问题。它的核心思想是为每个中断请求创建一个内核线程中断触发时内核唤醒这个线程中断处理逻辑在线程的上下文中执行可以睡眠、可以被调度、可以和其他线程一样参与优先级管理。用法上通过request_threaded_irq注册static irqreturn_t irq_handler_fn(int irq, void *dev_id) { // 这个函数运行在线程上下文可以睡眠 msleep(10); return IRQ_HANDLED; } int ret request_threaded_irq(irq, NULL, irq_handler_fn, IRQF_TRIGGER_RISING, my_device, dev);注意第二个参数传的是NULL这意味着中断处理程序直接使用线程化的handler不区分上半部和下半部。你也可以同时指定一个上半部的handler和下半部的线程handler实现“上半部快速响应下半部线程慢慢处理”的混合模式。线程化中断还有一个额外的好处它天然支持实时调度策略。对实时性有要求的中断可以把对应的线程设置为SCHED_FIFO优先级拉到很高这样即使系统负载很高中断处理也能及时得到CPU时间。这一点在Linux RT补丁和PREEMPT_RT内核中尤其重要。从历史演进来看下半部机制从BH到tasklet再到线程化中断方向非常清晰越来越灵活越来越允许阻塞操作越来越贴近进程上下文的语义。但每种机制都没完全取代前一种因为它们各有各的性能优势和适用场景。接下来我们就得把软中断这个“底层舞台”彻底讲清楚因为你理解了软中断才能真正理解tasklet为什么快、workqueue为什么慢、线程化中断为什么灵活。3. 软中断所有下半部的底层舞台3.1 软中断的注册与触发一种“轻量级中断”软中断softirq是内核里最接近硬件中断的一种软件机制。它不是由一个真实的中断号触发的而是由内核代码主动标记一个“待处理标志”然后在适当的时机统一执行。内核中的软中断类型定义在linux/interrupt.h里常见的有这么几种软中断类型编号用途HI_SOFTIRQ0高优先级taskletTIMER_SOFTIRQ1定时器NET_TX_SOFTIRQ2网络发送NET_RX_SOFTIRQ3网络接收BLOCK_SOFTIRQ4块设备IRQ_POLL_SOFTIRQ5IRQ轮询TASKLET_SOFTIRQ6普通taskletSCHED_SOFTIRQ7调度器HRTIMER_SOFTIRQ8高精度定时器RCU_SOFTIRQ9RCU机制每个软中断有一个对应的处理函数比如NET_RX_SOFTIRQ对应的net_rx_action。内核维护了一个softirq_vec数组数组下标就是软中断编号数组元素包含处理函数指针。触发软中断的API是raise_softirq或raise_softirq_irqoff。调用这个API时内核会把当前CPU的__softirq_pending位图中的对应位置1表示“这个软中断需要被处理”。3.2 执行时机与ksoftirqd限流软中断到底在什么时候跑软中断被标记为“待处理”之后并不会立即执行。它会等待内核选择一个合适的时机统一处理。这个时机主要有三个第一硬件中断处理程序返回前。在irq_exit()函数中内核会检查当前CPU是否有待处理的软中断。如果有就调用invoke_softirq()去处理。这是软中断最常见的执行时机——紧随硬件中断之后趁热打铁把下半部干了。第二内核线程ksoftirqd被唤醒。如果软中断触发特别频繁比如网络高负载下NET_RX_SOFTIRQ被反复触发内核会启用限流机制。当__do_softirq检测到连续处理一段时间后仍有大量软中断待处理它会“劫持”当前的执行路径唤醒每CPU的ksoftirqd内核线程让这个线程在较低的优先级下继续处理软中断。这本质是一种防饿死机制保证用户态进程和其他内核任务不会被软中断彻底饿死。第三显式调用do_softirq的地方。内核中有一些代码路径会主动检查并处理软中断比如网络栈的某些收包路径。执行时机最值得注意的一点是软中断处理函数是被“借用”当前进程的上下文来执行的但它仍然运行在中断上下文中。也就是说当CPU从中断处理程序返回到被中断的进程之前它先跳到软中断处理函数去执行。这个被执行的过程对那个“被中断的进程”来说是无感的它只是突然凭空多耗了一些时间。有意思的是软中断处理函数里不允许睡眠的原因除了本身处于原子上下文之外还因为它的执行方式它压根就不是一个可以被调度器管理的主体它是一个“借壳执行”的函数。你让一个没有“身体”的函数去睡眠跟让一个幽灵去吃饭一样逻辑上就说不通。3.3 为什么说tasklet是软中断的“挂件”很多人分不清tasklet和软中断的区别其实很简单tasklet是构建在软中断之上的一个“上层封装”。内核预留了两个软中断编号给tasklet使用HI_SOFTIRQ和TASKLET_SOFTIRQ前者用于高优先级tasklet后者用于普通tasklet。当代码调用tasklet_schedule时内核内部的实现其实是static inline void tasklet_schedule(struct tasklet_struct *t) { if (!test_and_set_bit(TASKLET_STATE_SCHED, t-state)) __raise_softirq_irqoff(TASKLET_SOFTIRQ); }先设置tasklet的TASKLET_STATE_SCHED状态位防止重复调度然后触发TASKLET_SOFTIRQ软中断。当软中断执行到TASKLET_SOFTIRQ对应的tasklet_action函数时tasklet_action会从当前CPU的tasklet链表中取出所有被调度的tasklet逐个执行它们的回调函数。这个设计给tasklet带来了两个重要特性tasklet的执行是串行的。同一个tasklet不会在两个CPU上同时执行这是通过TASKLET_STATE_RUN状态位保证的。但是要注意不同的tasklet是可以在不同CPU上并行执行的。如果你的两个tasklet之间共享数据该加锁还是得加锁。tasklet的优先级低于网络和定时器软中断。因为NET_RX_SOFTIRQ排在TASKLET_SOFTIRQ前面网络软中断会优先于tasklet执行。这在大流量网络场景下可能导致tasklet饿死需要特别注意。我曾经遇到过一个诡异的现象一个基于tasklet的LED驱动在系统跑满网络流量时LED闪烁频率明显变慢像“半死不活”一样。后来查了内核文档才明白就是因为NET_RX_SOFTIRQ一直有活干tasklet的软中断被无限延后了。这个案例让我深刻理解了软中断优先级编排对上层任务的影响。4. 到底该用哪种下半部选型对比与适用场景4.1 四张牌摊开看软中断、tasklet、工作队列、线程化中断的差异做驱动开发选对下半部机制比写对代码更重要。我见过太多人拿着workqueue去处理网卡收包结果性能一塌糊涂也见过有人非要在tasklet里调用usb_control_msg导致内核panic。选型的核心是先搞清这四种机制的底牌。维度软中断softirqtasklet工作队列workqueue线程化中断threaded IRQ执行上下文中断上下文中断上下文进程上下文内核线程上下文能否睡眠不能不能可以可以执行优先级高先于进程调度中依赖软中断编号低作为普通内核线程参与调度可配置支持RT调度策略是否可抢占不可抢占不可抢占可被更高优先级任务抢占可被更高优先级任务抢占同实例并发约束同一软中断类型可在多CPU并行同一tasklet仅在一个CPU执行同一work可在多CPU并行也可绑定每个IRQ一个线程适合场景网络收发、块设备、高性能路径驱动下半部、需要简单延迟处理的场景复杂驱动逻辑、涉及文件系统/设备I/O多数驱动中断处理的“保守选择”这个表格值得贴在你的终端上。每次写驱动前先对着这个表做排除法能省掉后面一大半调bug的时间。我个人的习惯是站在“性能”和“开发效率”两个角度权衡追求极致性能、处理逻辑短平快选软中断或tasklet。网络栈、块设备这类子系统走的都是软中断路径因为它们在数据面延迟敏感不能忍受进程调度的开销。处理逻辑复杂、需要访问I2C/SPI/文件系统选工作队列或线程化中断。比如一个触摸屏驱动中断来了之后要去读I2C寄存器获取坐标I2C读写本身就可能睡眠这时候只能用进程上下文的下半部。想省事、不想手动创建工作队列选线程化中断。request_threaded_irq几乎是一步到位逻辑都写在同一个回调函数里不需要额外管理tasklet/workqueue的生命周期。代价是每个中断都要创建一个内核线程中断频繁时线程唤醒调度的开销会比tasklet大。4.2 驱动开发实战选型建议一张决策流程图选型时可以按这套逻辑走先问自己下半部的处理函数里有没有可能调用任何会睡眠的API比如mutex_lock、kmalloc(GFP_KERNEL)、msleep、i2c_transfer、wait_for_completion。有直接排除软中断和tasklet只能在workqueue和threaded IRQ里选。没有继续看第二步。再问处理的实时性要求高不高是不是数据面路径丢一个中断可能导致数据包丢失或吞吐骤降高选用软中断或tasklet优先tasklet开发简单。不高workqueue或threaded IRQ都可以。最后问下半部处理逻辑和中断的耦合度强不强是不是每个中断都需要立即触发一次下半部强耦合threaded IRQ最合适逻辑集中在一个函数里代码可读性好。弱耦合workqueue更灵活可以合并多个处理请求批量处理。在大多数普通外设驱动中threaded IRQ几乎是最稳妥的选择。它既解决了睡眠问题又不需要你额外管理一个workqueue的生命周期代码清晰度也高。我自己写GPIO按键驱动、传感器驱动时优先都是threaded IRQ。5. 下半部开发中的高频坑与排查经验5.1 坑一在tasklet或软中断里调用可睡眠API这是下半部开发中最经典的坑。我在一个项目里见过同事这么写void my_tasklet_func(unsigned long data) { struct my_device *dev (struct my_device *)data; dev-buffer kmalloc(1024, GFP_KERNEL); // 问题所在 // ... }GFP_KERNEL是允许睡眠的分配标志内核会尝试用非原子方式分配内存必要时会睡眠等待内存回收。但tasklet运行在中断上下文不允许睡眠。结果是什么当系统内存紧张时kmalloc(GFP_KERNEL)尝试唤起内存回收机制内核直接抛出了“BUG: scheduling while atomic”整个系统可能陷入不稳定状态。正确做法是用GFP_ATOMICdev-buffer kmalloc(1024, GFP_ATOMIC);GFP_ATOMIC使用原子内存池不会睡眠代价是分配成功率低、不能使用紧急预留内存之外的部分。所以你在中断上下文中申请内存时要时刻想着“万一分配失败我得有兜底方案”比如在初始化时预分配好内存下半部只做数据的拷贝。5.2 坑二tasklet的串行性不等于数据安全性前面说过同一个tasklet不会在两个CPU上并发执行。很多人就误以为“既然是串行的那我就不用加锁了”。这忽略了一个重要前提tasklet的串行性只保证它自身不会并发但不能保证它和调用它的其他代码路径也不会并发。举个例子。你的驱动里有一个全局变量int count中断处理程序上半部里会counttasklet里会读取count并做处理。上半部运行在中断上下文tasklet运行在软中断上下文它们在同一个CPU上看起来是“先后执行”的但如果在不同的CPU上呢CPU0正在执行tasklet读取countCPU1触发中断中断处理程序里count。这两个操作就在两个CPU上并发访问了同一个变量没有锁保护count的值可能错乱。所以任务之间的并发关系取决于它们是否可能在两个CPU上同时执行而不是取决于它们是不是“同一个tasklet”。只要有一条执行路径可能和tasklet并行该加的锁就得加。我一般会用spin_lock_irqsave去保护上半部和下半部共享的数据因为它能同时屏蔽中断和获取自旋锁保证临界区安全。5.3 坑三下半部耗时过长导致系统交互卡顿这是个“慢性病”不容易一眼看出来但对用户体验影响巨大。softirq在irq_exit()中执行时是借用了被中断进程的执行流。如果某个CPU上的软中断处理函数耗时过长比如网卡驱动在NET_RX_SOFTIRQ里做了大量数据包解析那被中断的用户进程就会一直被“向后挤”表现为系统卡顿、鼠标拖影、视频掉帧。排查这个问题我推荐用两个工具top命令按CPU占用排序观察sisoftirq占用率。如果si长期超过30%说明软中断处理负担过重。perf top查看热点函数确认是不是某个驱动下半部函数占了大头。定位到问题后常用的优化手段有如果下半部是tasklet可以升级为专用的内核线程处理。内核线程可以被调度器管理被打断的进程能获得调度机会系统卡顿会明显缓解。如果半部中做了多步骤的硬件操作可以考虑使用hrtimer进行“分片处理”把一个长任务拆成多个短任务每个短任务挂到定时器上依次执行。对于网络类软中断确认是否使用了NAPI。NAPI允许网卡驱动在中断处理中切换为轮询模式批量收包大幅降低软中断触发频率。没开NAPI的网卡驱动在高负载下会频繁触发中断软中断占用率很容易飙到80%以上。5.4 实测调优把tasklet改造成自定义内核线程这里分享一个我实际做过的优化案例。有一款USB转串口设备驱动里用tasklet处理接收数据。问题是在高速波特率下数据量大时tasklet执行时间过长导致系统响应明显变慢而且用户态读取串口时延迟能达到几十毫秒。排查后发现tasklet处理数据时涉及用户空间通知调用了kill_fasync触发信号这个信号处理过程中可能和用户态产生竞争进一步拉长处理时间。我最终把tasklet替换成了自定义内核线程用kthread_create创建一个专用线程用waitqueue和标志位来触发处理逻辑。static struct task_struct *my_thread; static wait_queue_head_t my_wq; static atomic_t my_flag; static int my_thread_func(void *data) { while (!kthread_should_stop()) { wait_event_interruptible(my_wq, atomic_read(my_flag)); atomic_set(my_flag, 0); // 在这里处理数据可以放心地调用会睡眠的函数 } return 0; } // 中断上半部中触发 static irqreturn_t my_irq_handler(int irq, void *dev_id) { // 拷贝数据到缓冲区 atomic_set(my_flag, 1); wake_up_interruptible(my_wq); return IRQ_HANDLED; }改造后的效果非常明显串口读取延迟从几十毫秒降到了2毫秒以内系统在高速数据流下也不再卡顿。这个案例给我的启发是——tasklet适合“轻量级善后”一旦你的下半部处理逻辑开始变得复杂、可能阻塞、需要和用户态交互就应该尽早迁移到内核线程或workqueue上。别为了省几行代码让系统的整体响应性买单。5.5 调试下半部的几个实用技巧最后说几个调试下半部问题的经验这些是看文档很难学到的技巧一打开内核的原子上下文检测。在内核配置中开启CONFIG_DEBUG_ATOMIC_SLEEP内核会在开发阶段主动检查中断上下文中的睡眠调用直接打出警告栈帮你提前发现坑一中的问题。开发阶段一定开着上线前再关掉。技巧二利用/proc/softirqs观察软中断分布。这个文件会统计每个CPU上各类软中断的触发次数。如果某个CPU的NET_RX_SOFTIRQ计数异常偏高说明网卡中断在这个CPU上过于集中可能需要调整中断亲和性irqbalance或手动设置smp_affinity。技巧三用ftrace跟踪tasklet和workqueue的执行时序。trace-cmd record -e softirq:softirq_entry -e softirq:softirq_exit可以记录软中断的进入和退出配合trace-cmd report能看到每个软中断执行的耗时定位是哪个CPU上哪个软中断卡顿最严重。技巧四注意local_bh_disable和local_bh_enable的使用。这对函数用于在进程上下文中临时禁用本CPU软中断防止下半部和当前执行的代码发生数据竞争。典型场景是socket层收包时的bh_lock_sock。但如果驱动写得不规范在持有自旋锁时调用local_bh_disable可能会和tasklet产生死锁——因为tasklet被禁用后可能正好持有你等待的另一把锁。这个死锁问题极难排查因为它只在特定时序下才会触发。我建议驱动开发中尽量少用local_bh_disable改用更明确的锁机制来保护共享数据。6. 对下半部机制的一点个人总结与选型心法做了这么多年内核驱动我越来越觉得下半部机制是整个中断子系统里最需要“设计感”的部分。它不是简单地“把中断处理拆成两半”就完了而是要在实时性、吞吐量、开发复杂度之间做权衡。拿我自己的项目来说凡是量小的外设中断按键、传感器、低速通信我几乎全部用threaded IRQ。理由很简单代码逻辑集中在一个函数里不会涉及tasklet/workqueue的生命周期管理出了问题也好排查。而量大的数据面网卡、高速串口、DMA搬运我倾向于用tasklet或者直接定制软中断路径因为这里每一步的延迟都值钱。另外我想特别强调一点选型不是一劳永逸的。同样的一个驱动在内核版本升级后可能下半部的推荐写法就变了。比如老版本里netif_rx的收包路径依赖NET_RX_SOFTIRQ新版本引入了NAPI之后很多场景已经不再依赖传统软中断。你要保持对内核邮件列表和文档的关注别抱着十年前的经验不放。还有一个容易被忽略的点下半部机制和实时性要求的冲突。在PREEMPT_RT内核中几乎所有硬中断处理都被强制线程化传统软中断的执行时机也发生了很大变化。如果你的项目要跑RT内核一定要重新审视下半部的选型别把普通内核下的性能经验照搬过去。如果你刚开始接触这块我建议你从一个小驱动的改造练起找一个现有的GPIO按键驱动先跑通threaded IRQ版本然后改写为tasklet版本最后改写为workqueue版本对比三者行为差异。亲手改一遍比看十篇分析文章都管用。等你把几种机制的脾气都摸清了再回到数据手册和内核源码里对照着看很多之前不理解的设计决策会瞬间豁然开朗。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

基于Spring Boot+Vue的影评管理系统设计与实现 2026/9/10 17:53:41

基于Spring Boot+Vue的影评管理系统设计与实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Java日期比较全攻略:从Date到LocalDateTime的完整实践 2026/9/10 17:53:41

Java日期比较全攻略:从Date到LocalDateTime的完整实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
NVFuser 融合代码生成器:PyTorch TorchScript 的 GPU 算子融合后端 2026/9/10 17:53:41

NVFuser 融合代码生成器:PyTorch TorchScript 的 GPU 算子融合后端

NVFuser 融合代码生成器:PyTorch TorchScript 的 GPU 算子融合后端 【免费下载链接】pytorch Tensors and Dynamic neural networks in Python with strong GPU acceleration 项目地址: https://gitcode.com/GitHub_Trending/py/pytorch NVFuser 是 PyTorch …

阅读更多 →
随机森林回归实战:从数据预处理到模型调优全解析 2026/9/10 17:53:41

随机森林回归实战:从数据预处理到模型调优全解析

简介:随机森林回归模型项目实战资料包,面向Python机器学习初学者及需要项目练习的开发者,完整演示了从问题定义、数据获取、数据清洗与预处理、EDA探索性分析、特征工程,到随机森林建模、模型评估与实际应用的标准化流程。压缩包共…

阅读更多 →
Qwen-Agent 是怎么把 100 页 PDF 读给 AI 的?文档分块与存储 3 个机制全解析 2026/9/10 17:53:41

Qwen-Agent 是怎么把 100 页 PDF 读给 AI 的?文档分块与存储 3 个机制全解析

Qwen-Agent 是怎么把 100 页 PDF 读给 AI 的?文档分块与存储 3 个机制全解析 【免费下载链接】Qwen-Agent Agent framework and applications built upon Qwen>3.0, featuring Function Calling, MCP, Code Interpreter, RAG, Chrome extension, etc. 项目地址…

阅读更多 →
JPEG-LS V2.2工业级无损压缩实战指南 2026/9/10 17:50:40

JPEG-LS V2.2工业级无损压缩实战指南

简介:本资源是JPEG-LS无损图像压缩标准(ISO/IEC 14495-1 / ITU-T T.87)的V2.2版本完整实现源码包,面向图像处理开发者、嵌入式算法工程师及多媒体编码学习者,解决无损压缩算法集成、编解码流程理解与底层优化实践等核心…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞