armclang函数插桩:嵌入式代码执行流程分析实战
直接说结论armclang的-finstrument-functions是我这几年在 Arm 嵌入式开发里用过最“朴素但好用”的函数执行流程分析手段。它不像硬件 Trace 那样需要额外调试器也不像 IDE 的 profiler 那样挑芯片和仿真器只要你用的是 Arm Compiler 6armclang 是它的编译器驱动打开一个编译选项在代码里写上两个回调函数就能把工程里每个函数的“进入”和“退出”事件全部抓下来拿到函数调用顺序、执行耗时、调用次数这些关键数据。这个功能适合谁做嵌入式、RTOS 应用、固件性能优化、还有排查“程序跑飞但不知道跑到哪”这类问题的人。我去年排查过一个跑在 Cortex-M7 上的传感器采集系统任务调度偶尔卡顿用插桩方式直接抓出了是哪几个函数在中断里占用时间过长整个过程没动硬件、没接 JTAG全靠这几个编译选项和回调函数。这篇文章就把我怎么配置、怎么分析、踩过哪些坑全写清楚。1. 先搞明白 -finstrument-functions 的工作原理用工具之前一定要知道它到底干了什么。-finstrument-functions是编译器的插桩选项启用后编译器会在每个函数的入口和出口自动安插两个“钩子调用”分别对应两个约定的回调函数函数入口处会调用__cyg_profile_func_enter(void *this_fn, void *call_site)函数出口处会调用__cyg_profile_func_exit(void *this_fn, void *call_site)这两个回调函数需要你自己实现。this_fn是当前被调用函数的地址call_site是调用点的地址也就是“谁调用了这个函数”的返回地址。记住一个关键事实编译器只是帮你插了两个函数调用真正的数据分析逻辑完全在你手里这也是它灵活的地方。有人可能想问这和直接用-pg做 profiling 有什么区别-pg是 gprof 的老路子armclang 虽然也支持-pg但它需要额外的运行时支持在裸机嵌入式环境里配起来麻烦。-finstrument-functions则非常“裸”只插入两个函数调用剩下的全部由你自己控制这反而更适合嵌入式。从实现原理来看编译器是在函数序言prologue和尾声epilogue阶段插入调用指令。具体位置大概在函数栈帧建立之后、参数准备好之前所以回调函数里你可以安全地访问当前函数的上下文。不过要注意编译器不会自动让这两个回调函数“豁免”插桩——如果你没有给回调函数本身标注__attribute__((no_instrument_function))回调函数会被再次插桩然后回调里调用回调无限递归最终栈溢出。这是初学者最容易踩的第一个坑。还有一个细节值得留意-finstrument-functions对 C 和 C 都生效。C 的成员函数、重载函数、构造析构函数也一样会被插桩但this_fn拿到的是一个代码段地址你需要结合符号表来解析出具体是哪个函数。这引出一个关键概念插桩之后拿到的都是地址不是函数名。所以后续分析流程一定绕不开“地址到符号名”的映射这个我在第 3 节详细讲。总结下来这个机制就是三件事编译器插桩回调、回调函数记录数据、数据分析还原流程。三个环节是串联关系任何一个环节做得粗糙结果都会失真。2. 编译选项配置与回调函数的落地实现2.1 回调函数代码模板官方文档里只给了两个回调函数的约定实际怎么用完全靠实践。这里给出一套我实测可用的模板适配 Arm Compiler 6 的裸机工程用ITM_SendChar或串口输出数据不需要操作系统支持。/* instrument_log.h */ #ifndef INSTRUMENT_LOG_H #define INSTRUMENT_LOG_H #include stdint.h /* 回调函数声明注意不能加 static */ void __cyg_profile_func_enter(void *this_fn, void *call_site) __attribute__((no_instrument_function)); void __cyg_profile_func_exit(void *this_fn, void *call_site) __attribute__((no_instrument_function)); /* 日志缓冲区和控制接口 */ void instrument_log_init(uint32_t *buf, uint32_t size); void instrument_log_start(void); void instrument_log_stop(void); #endif/* instrument_log.c */ #include instrument_log.h #include string.h /* 日志条目结构体避免使用浮点和复杂类型保持结构体紧凑 */ typedef struct { uint32_t magic; /* 魔数校验用固定0x5A5A */ uint32_t this_fn; /* 函数地址 */ uint32_t call_site; /* 调用点地址 */ uint32_t tstamp; /* 时间戳可用 DWT-CYCCNT 获取 */ uint32_t event; /* 0enter, 1exit */ } log_entry_t; static log_entry_t *log_buf 0; static volatile uint32_t log_size 0; static volatile uint32_t log_idx 0; static volatile uint8_t log_enabled 0; void instrument_log_init(uint32_t *buf, uint32_t size) { log_buf (log_entry_t *)buf; log_size size / sizeof(log_entry_t); log_idx 0; log_enabled 0; } void instrument_log_start(void) { log_idx 0; log_enabled 1; } void instrument_log_stop(void) { log_enabled 0; } void __cyg_profile_func_enter(void *this_fn, void *call_site) { (void)call_site; if (!log_enabled || !log_buf) return; DWT-CYCCNT 0; /* 实际应用中不应该清零这里仅演示 */ log_entry_t *e log_buf[log_idx]; if (log_idx log_size) { e-magic 0x5A5A; e-this_fn (uint32_t)this_fn; e-call_site (uint32_t)call_site; e-tstamp DWT-CYCCNT; e-event 0; log_idx; } } void __cyg_profile_func_exit(void *this_fn, void *call_site) { (void)call_site; if (!log_enabled || !log_buf) return; log_entry_t *e log_buf[log_idx]; if (log_idx log_size) { e-magic 0x5A5A; e-this_fn (uint32_t)this_fn; e-call_site (uint32_t)call_site; e-tstamp DWT-CYCCNT; e-event 1; log_idx; } }2.2 注意几个隐蔽的问题第一回调函数自身严禁递归插桩。上面代码里我已经给了__attribute__((no_instrument_function))这非常重要。如果漏掉函数入口被插桩插桩调用又触发入口回调又插桩最终导致栈溢出跑起来就会 HardFault。即使侥幸没挂日志里也会全是回调函数自己的无意义记录。第二日志缓冲区不能放在栈上。裸机工程建议放到一个独立的全局数组或者专门的 RAM 段。因为插桩回调本身也会使用栈函数调用深度越大栈余量越少。如果日志缓冲区在栈上互相挤占直接篡改栈数据。第三时间戳的获取。Cortex-M3/M4/M7 内核有 DWT-CYCCNT 周期计数器读取它非常快几拍就完成不会显著影响插桩原有开销。但要注意在__cyg_profile_func_enter里我演示了清零 CYCCNT这是错误用法——如果你在多处清零时间戳就乱套了。正确的做法是在初始化时清零一次或者干脆不清零每次记录的是绝对计数值做差即可得到单函数耗时。如果芯片没有 DWT退而求其次用SysTick-VAL也可以但注意溢出问题。第四日志写满后的处理。最简单就是不处理直接丢分析最近 N 条记录。要完整跑完再 dump就必须给足缓冲区空间。我通常的做法是先跑一轮分析后按需扩大缓冲区。日志索引log_idx一定要是volatile因为调试器在看这块内存时如果编译器把变量优化成寄存器副本两个回调函数之间就不同步。2.3 armclang 编译指令与链接注意事项开启插桩的编译命令核心就一行armclang -c -finstrument-functions --targetarm-arm-none-eabi -mthumb -mcpucortex-m7 -O1 -g -mapcs -finstrument-functions-exclude-function-list__cyg_profile_func_enter,__cyg_profile_func_exit--targetarm-arm-none-eabi指定目标架构-mcpu按实际芯片选择。这里要强调的是-mapcs这个选项它在 Arm Compiler 6 里用来确保函数调用遵循 AAPCS 标准这样__cyg_profile_func_enter才能拿到正确的入参寄存器。绝大多数情况下 armclang 默认已遵循 AAPCS不需要显式加但如果你把回调函数放到 C 文件里且开了异常处理就必须确认函数签名没有被 name mangling 干扰最好将回调函数声明为extern C。链接时有没有额外要求严格来说没有。但要注意开启-ffunction-sections和链接脚本里的--gc-sections这样没有被调用的函数会被裁剪掉日志只保留真正执行的函数地址分析时更干净。对于裸机工程建议在链接脚本里单独划一块日志 RAM 区域比如.instrument_log (NOLOAD) : { *(.instrument_log) } RAM然后在代码里用__attribute__((section(.instrument_log)))标记日志缓冲区避免被其它模块的变量覆盖。实测下来这种方式在调试时也方便可以直接在 IDE 内存窗口里查看日志数组。2.4 排除列表的使用技巧armclang 支持-finstrument-functions-exclude-function-list...和-finstrument-functions-exclude-file-list...两个附加选项。它们的价值在于你并不需要分析每一个函数。比如中断处理函数、调度器函数、底层驱动函数它们调用频繁且逻辑简短插桩反而会引入额外延迟导致测量结果失真。中优先级的中断如果在 ISR 入口和出口各插桩一次实时性会明显下降。我的习惯是保留所有业务逻辑函数的插桩应用层任务函数、通信协议处理函数排除启动代码和底层库函数Reset_Handler,SystemInit,HardFault_Handler等排除内联概率高的小函数如__NVIC_EnableIRQ这类 CMSIS 函数命令示例armclang -c -finstrument-functions \ -finstrument-functions-exclude-function-listReset_Handler,SystemInit,HardFault_Handler \ -finstrument-functions-exclude-file-list./Drivers/CMSIS/Device/ST/STM32F7xx/Source/Templates/system_stm32f7xx.c \ main.c注意列表里的函数名必须和编译后的符号名完全一致。C 函数需要带修饰名mangled name建议先编译一版用arm-none-eabi-nm查看符号表再决定排除哪些。3. 从日志到调用关系地址映射的完整思路3.1 拿日志里的地址反查符号名__cyg_profile_func_enter拿到的this_fn是代码段地址。如果你链接脚本里定义了LOAD_ADDRESS地址可能还包含偏移。要把地址还原成函数名我一般用两种方法方法一编译时生成 map 文件用armclang -Wl,-Mapoutput.map生成完整符号映射然后写脚本提取函数名和地址范围。这种方法通用但操作繁琐。方法二直接用arm-none-eabi-nm -n导出符号表。在一个小工程里反查几百个函数没问题。举个例子arm-none-eabi-nm -n build/output.elf symbols.txtsymbols.txt格式类似08000110 T main 08000200 t vTaskDelay 08000350 T osDelay 080003a8 t prvIdleTask后面写一个 Python 脚本把地址转成函数名import sys syms [] with open(symbols.txt) as f: for line in f: parts line.split() if len(parts) 3: addr int(parts[0], 16) syms.append((addr, parts[2])) def resolve(addr): best None for a, name in syms: if a addr: best (a, name) else: break return best[1] if best else hex(addr)这个方法的前提是符号地址范围覆盖了日志里出现的所有地址所以编译时-g和-O1保留一定符号信息必须开启。如果开了-O2局部函数可能被内联、符号被裁剪反查准确度下降。3.2 解析日志并还原执行流程拿到日志后我会按this_fn地址做分组统计。核心是还原“哪条调用路径占用了大多数时间”。最简单有效的方法是维护一个 Call Stack遍历日志条目的event字段遇到enter把this_fn压栈遇到exit从栈里弹出并和当前this_fn比较正常应相等如果匹配说明调用关系正确如果不匹配可能发生了长跳转setjmp/longjmp或者中断触发或日志有丢失这时栈状态需要手动修正。执行耗时计算对每个enter和对应exit做时间戳相减得到该函数单次执行时间。注意这里不能简单地把所有耗时全部算在函数本身头上因为函数内部还调用了其他函数。想要得到“纯函数体耗时”要做自下而上的聚合从最内层的叶子函数开始把子函数耗时减掉。具体操作步骤先把日志按时间戳排序如果缓冲区记录顺序就是执行顺序这步可省维护栈同时记录每个函数进入时的时间戳出栈时计算该函数的总耗时并累加到父函数的自耗时间中统计每个函数的调用次数、最大耗时、平均耗时排序输出热点一个典型的输出可能是函数名 调用次数 总耗时(cycles) 自耗(cycles) 平均(cycles) ProcessData 2043 5812340 213200 2844 UART_IRQHandler 8733 1520090 1520090 174 ProtocolParse 198 986300 223400 4981看到ProcessData平均 2844 cycles 但自耗占比不到 4%说明时间几乎都花在它调用的子函数上。顺着调用关系继续往下追就能定位瓶颈。3.3 用脚本可视化执行流程还原调用流程之后如果只是看一眼文字日志很难发现深层的性能问题。我习惯生成两种视图调用树和火焰图。调用树就是缩进式的文本展示main AppInit DriverInit GPIO_Init SPI_Init LoopTask ProcessData SensorRead DataFilter ProtocolParse火焰图我用 Brendan Gregg 的脚本直接生成 SVG。关键是先输出折叠文本格式main;AppInit;DriverInit;GPIO_Init 10 main;AppInit;DriverInit;SPI_Init 15 main;LoopTask;ProcessData;SensorRead 2340每行代表一条调用路径后面的数字是执行次数或耗时。有了这个文件使用 flamegraph.pl 脚本直接生成火焰图每个色块宽度对应耗时占比非常直观地显示热点路径。4. 大型工程里的组合技从插桩到性能数据落地4.1 与 RTOS 任务信息结合如果想看每个任务分别跑了多少时间插桩回调里只能拿到函数地址拿不到当前任务信息。我采用的方案是在回调里读取当前任务的句柄或者利用 RTOS 提供的vTaskSuspendAll/taskENTER_CRITICAL机制保护日志写入过程。但要注意临界区长度如果日志写满或者缓冲区索引更新逻辑太慢临界区时间过长会影响实时性。折中做法是回调里只把函数地址、调用点地址和时间戳写入缓冲区不做任何任务关联在数据分析阶段根据日志中的中断切换、任务切换函数调用如vTaskSwitchContext来推测当时运行的任务这个方案对日志干净整洁重要因为回调函数里做太多事情既会放大观测效应observer effect也可能造成日志自污染。4.2 与硬件 DWT 和 ITM 配合使用Arm Cortex-M 内核有 ITMInstrumentation Trace Macrocell单元支持通过 SWO 引脚输出调试信息。ITM_SendChar可以在不占用 UART 外设的情况下输出字符而-finstrument-functions配合 ITM 输出可以实现在线实时函数跟踪。做法void __cyg_profile_func_enter(void *this_fn, void *call_site) { if (!(ITM-TER 1)) return; /* 检查 ITM 是否使能 */ ITM-PORT[0].u8 (uint8_t)((uint32_t)this_fn 0xFF); ITM-PORT[0].u8 (uint8_t)(((uint32_t)this_fn 8) 0xFF); ... }然后通过调试器的 SWO 接口实时抓取。这个方法的好处是不用缓冲区不受 RAM 限制适合长时间运行观察。劣势是 SWO 带宽有限如果函数调用极其频繁会丢失数据。在 Cortex-M7 上SWO 最大频率一般是 CPU 主频的四分之一所以如果函数每秒调用几万次建议不要用 ITM回落到 RAM 缓冲方案。4.3 低开销模式下只记录函数进出点有种特殊场景你不想记录所有函数只想抓取某个特定函数的执行路径也就是“白名单”效果。-finstrument-functions本身不支持按函数名精确插桩但你可以通过回调函数里的地址判断来过滤void __cyg_profile_func_enter(void *this_fn, void *call_site) { /* 只记录目标函数及其调用者 */ if ((uint32_t)this_fn TARGET_FUNC_START || (uint32_t)this_fn TARGET_FUNC_END) return; ... }这种方式极大降低了记录开销。我做过一个实验对完整工程开插桩日志缓冲 4KB不到 2 秒就满了对单个目标函数做地址过滤后4KB 缓冲区可以跑几十秒。所以遇到“日志一会儿就满”的问题优先考虑做地址过滤而不是盲目加大缓冲区。不过地址过滤依赖链接后地址链接一变动就需要重新计算范围。改进方案是给目标函数加__attribute__((section(.func_monitor)))把这个函数单独放段用链接脚本里段的起始地址和结束地址来判断。这样链接变化了也能自动适配。5. 问题排查实战几次典型的应用案例5.1 案例一中断响应延迟排查某项目中UART 中断服务函数总延迟执行示波器测量中断引脚到响应点有几十微秒延迟。直接怀疑是某个长临界区。用-finstrument-functions全量插桩观察日志发现NVIC_DisableIRQ/NVIC_EnableIRQ之间有大量函数调用其中ProcessData函数中一条memcpy拷贝了 4KB 数据占用了 36 微秒。这段代码正好在临界区中。分析过程是日志里找到NVIC_DisableIRQ的 enter 时间戳和NVIC_EnableIRQ的 enter 时间戳相减再按调用树向下分解秒定位问题。如果没有插桩这个问题只能靠反复检查代码推测非常耗时。5.2 案例二死循环后停留在哪个函数程序跑飞看门狗复位调试器只能看到复位后的状态。我预先开启了插桩并且日志缓冲区不掉电这样复位后读取日志找到最后一条enter事件就是死循环的嫌疑函数。实际案例中最后一条日志显示进入了一个状态机的默认分支该分支里有个for循环条件写反了。这个应用思路的关键是日志缓冲区放在带电池备份的 RAM 区或者复位时不做清空操作保留之前的日志内容。5.3 案例三调度延迟分析FreeRTOS 多任务系统中任务 A 到任务 B 的切换耗时大于预期。插桩可以抓到vTaskSwitchContext到PendSV_Handler之间的耗时。如果发现这段过程中有traceISR_ENTER之类函数占用时间说明调试库本身拖慢了切换。移除部分调试日志后问题消失。6. 常见问题速查与避坑技巧下面这些问题是实际使用中遇到过的高频问题先出一张速查表问题现象可能原因解决方式开启插桩后直接 HardFault回调函数自身被插桩无限递归给回调函数加no_instrument_function属性日志里全是回调函数地址排除列表未生效检查-finstrument-functions-exclude-function-list中符号名是否匹配执行时间异常偏大DWT-CYCCNT 被清零或溢出不要在回调中清零用绝对计数值做差日志丢失或乱序缓冲区太小或中断抢占加大缓冲区或在回调中做临界区保护函数名反查不到编译器优化内联符号裁剪使用-O1配合-g链接脚本--gc-sections排除裁剪回调中调用 printf 挂死printf 不可重入且可能触发新的插桩重定向为串口发送避免调用其他业务函数插桩后代码体积剧增每个函数都增加了额外调用用排除列表缩小插桩范围或降低优化等级6.1 回调函数内禁止调用的东西回调函数在执行时正处于被插桩函数的中间状态。这时候调用 printf、malloc、加锁、调度器等轻则导致重入问题重则死锁或栈溢出。我踩过一次在回调里用 printf 输出调试信息UART 驱动里又调用了任务延时结果在中断上下文直接触发了configASSERT。回调函数里安全做的事只有写内存缓冲区读取 DWT 寄存器简单的 volatile 变量操作如果必须要输出 log只做缓冲区写入由外部任务或调试器定期 dump。6.2 插桩开销的估算很多人担心开启插桩会影响实时性。实测数据是Cortex-M7 主频 400MHz 时每次插桩回调仅记录地址和时间戳大概耗时 20~40 cycles约合 50~100ns。如果一个函数体本身只有几十个 cycle插桩会让它变成两倍甚至三倍这类短小函数要排除掉。如果函数体在几百 cycles 以上插桩引入的误差通常小于 5%可以接受。如果对精确度要求高可以只在性能分析版本中开启插桩发布版本去掉-finstrument-functions重新编译。这样既拿到了分析数据也不影响线上代码。6.3 中断上下文的安全记录ISR 里调用回调函数是安全的但要注意 ISR 的嵌套。如果高优先级中断抢占低优先级中断enter/exit事件在日志里是嵌套交错的按栈方式解析很容易出错。解决方法是回调函数里记录当前中断优先级读NVIC-IP或BASEPRI在分析阶段按照优先级区分是嵌套还是正常调用。采用一个 16 位宽的时间戳字段记录DWT-CYCCNT的低 16 位循环周期在 400MHz 下约 160 微秒一轮。如果单函数执行时间超过这个值时间戳就会产生歧义。6.4 谨慎处理 C 工程的插桩C 异常处理、虚函数调用、构造析构函数都会生成额外代码路径。armclang 在 C 异常展开时会跳过 exit 插桩导致enter/exit不匹配。遇到这种情况先用-fno-exceptions关闭异常支持或者只对.c文件开启插桩C 文件用排除列表关闭。7. 一些实用建议与总结如果你第一次用这个特性不要试图一开始就全量插桩。我建议按这样的节奏推进第一步先在 main 函数前后做最小验证。写一个空工程打开插桩看两个回调是否被调用。第二步加到一个实际模块上。选一个 CPU 占用较高的任务函数观察它的子函数调用路径确认数据格式正确。第三步再做全局插桩和自动化分析脚本。这样每个阶段都有明确的产出问题也好定位。最后分享一个小技巧armclang 的-finstrument-functions和-finstrument-functions-exclude-file-list可以配合通配符比如排除整个驱动目录armclang -finstrument-functions -finstrument-functions-exclude-file-list./Drivers/*.c app.c这样只分析应用层代码日志量大幅降低分析效率更高。实际做性能优化时大多数瓶颈都出在应用层逻辑驱动函数相对固定排除掉它们是合理的取舍。我个人的体会是这个工具虽然原始但恰恰因为“原始”可控性和可定制性极强。比起那些一键生成的性能分析报告搞清楚自己代码的调用路径、时间分布再去优化才是嵌入式优化的正路。踩过几次坑之后我现在每次开新项目都会把插桩相关代码作为调试标配预埋好等遇到疑难问题再去临时加编译选项比事到临头再调整代码要省事得多。

相关新闻

两节串联锂电池主动均衡:基于MP2642GR-Z与PIC单片机的设计

两节串联锂电池主动均衡:基于MP2642GR-Z与PIC单片机的设计

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

2026/10/16 5:19:33 阅读更多 →
Ubuntu离线部署GitLab:程序包下载与快速安装配置指南

Ubuntu离线部署GitLab:程序包下载与快速安装配置指南

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

2026/10/16 5:19:33 阅读更多 →
考研复试AI项目展示:用闭环能力打动老师,而不是模型够新

考研复试AI项目展示:用闭环能力打动老师,而不是模型够新

复试结果出来之前,最常见的问题是:“我该在简历里放一个什么样的项目?”我见过不少人把准备重点放在“模型够不够新”上,也有人宣称自己用了某个高star的开源库,好像项目越重、效果越炫,老师就会多给几分。…

2026/10/16 5:18:33 阅读更多 →

最新新闻

机器视觉:工业相机——从光学传感器到数字图像

机器视觉:工业相机——从光学传感器到数字图像

在讨论各类相机之前,我们首先要互相达成一个共识,即在极度追求成像还原性的工业领域,如果不谈电路、接口和控制系统:相机本质是一种传感器(Sensor)——将采集的被摄物体的颜色/灰度分布特征转换为二维数字特…

2026/10/16 6:05:48 阅读更多 →
【化学实验室|04】HarmonyOS ArkTS 场景选择器实战:让 13 个离线实验的入口、筛选和返回路径保持一致

【化学实验室|04】HarmonyOS ArkTS 场景选择器实战:让 13 个离线实验的入口、筛选和返回路径保持一致

实验目录页、实验模拟页和场景选择页单独看都不复杂:目录页展示卡片,模拟页根据 expId 创建引擎,场景页让用户在标准、加热、低温、高压和自定义环境中选择一个选项。真正容易出问题的是三页之间的契约。在 化学实验室 1.0.5 的实际 ArkTS 源…

2026/10/16 6:05:48 阅读更多 →
负载均衡之(SLB)服务器负载均衡配置

负载均衡之(SLB)服务器负载均衡配置

一 负载均衡配置#sysname L1000E-01&02#interface Reth1 //配置冗余接口和sub地址description TO_VPNWGQ-S7506E-01ip address 192.168.105.28 255.255.255.240ip address 192.168.105.19 255.255.255.240 subip address 192.168.105.20 255.255.255.240 subip address 192…

2026/10/16 6:05:48 阅读更多 →
【化学实验室|05】HarmonyOS ArkTS 每日一题实战:修复提交、统计和下一题推进的状态同步

【化学实验室|05】HarmonyOS ArkTS 每日一题实战:修复提交、统计和下一题推进的状态同步

“每日一题”看起来只有四个选项和一个提交按钮,但它同时跨越日期、页面状态、本地存储、错题本和学习统计。任何一层用自己的布尔值推断结果,都会出现用户能直接感知的矛盾:页面提示“已加入错题本”,实际写入失败;当…

2026/10/16 6:05:48 阅读更多 →
DQN实战避坑指南:设备协同、目标网络与经验回放的工程细节

DQN实战避坑指南:设备协同、目标网络与经验回放的工程细节

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

2026/10/16 6:05:48 阅读更多 →
Linux下vsftpd FTP服务搭建与配置实战:从基础到安全加固

Linux下vsftpd FTP服务搭建与配置实战:从基础到安全加固

搞过Linux运维的人,十有八九都绕不过FTP这关。别看现在云盘、HTTP下载满天飞,企业内部传个文件、给客户开放个下载目录、服务器之间倒腾点数据,FTP依然是最简单直接的那一档方案。尤其是vsftpd,名字就叫“Very Secure FTP Daemon”…

2026/10/16 6:04:47 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

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

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/15 9:49:14 阅读更多 →