操作系统实验进阶:从OSTEP模拟器到xv6内核动手改代码
简介操作系统实验HNU_ostep资源包面向高校操作系统课程与自学人群以OSTEP教材为理论基础涵盖进程管理、内存管理、文件系统、I/O系统等关键实验场景。压缩包共2000个文件、合计42.51MB其中包含910个C源文件、760个头文件、61个Python脚本、55个Markdown文档以及shell/cmake/make等构建辅助文件代码与文档结构清晰便于按模块查阅。ostep_code目录提供可直接修改运行的实验源代码说明文档明确实验环境及提交要求附带的教材解读和讲义则补充了底层原理帮助理解调度算法、页面置换、文件分配等实现细节。已有164人学习下载适合需要从理论跨越到实践的本科生、研究生及准备系统软件方向的开发者。通过动手调整调度策略、内存分配或文件系统代码读者能直观观察不同设计对性能的影响从而提升系统级调试、分析与设计能力。1. 先搞清楚它在做什么一门把《操作系统导论》课后题变成动手实验的课很多人听到“操作系统实验”第一反应是“又要从零写一个操作系统”。但拿到这套实验项目之后你会发现它的主线不是从 bootloader 开始造轮子而是沿着《操作系统导论OSTEP》这本书的章节把每一章末尾的作业变成可以运行、可以评分、可以对照答案的实验。实验材料分两类一类是 Python 写的模拟器用来复现调度算法、内存分配、地址翻译、并发调度这些“看不见的内核行为”另一类是 xv6 教学操作系统用来真正动手改内核代码。它能解决的核心问题是“上课听懂了但不会用”的断层。这套实验项目适合正在跟这门课的学生、想自学 OSTEP 的开发者以及准备被拉去当助教的人。2. 模拟器实验怎么做三类经典实验的复现路径与参数调整模拟器实验是整套 ostep 操作系统实验里门槛最低、但信息量最大的一类。说它门槛低是因为你只需要一个 Python 解释器没有任何内核环境依赖说它信息量大是因为调度、内存、并发这三块最难讲清楚的概念都能被拆成一行命令和一个可对比的输出。这一章按“为什么做、怎么做、怎么算会了”的顺序把三类模拟器实验的完整路径走一遍。2.1 为什么先跑模拟器把黑匣子变成可观察的命令行OSTEP 每一章末尾的作业分成两类模拟器和真实代码。模拟器是一个几百行的 Python 脚本它内部实现了一个迷你调度器、一个内存分配器、一张页表或一个线程调度器然后通过命令行参数让你改变策略、负载和随机种子观察不同情况下系统行为如何变化。选型理由很实在直接改内核编译一次要几十秒跑起来还要处理 qemu 环境出错后还不容易定位模拟器则是零依赖、秒级出结果、输出完全确定适合把一个概念反复“折腾”。关键认知模拟器实验不是让你“跑出输出然后粘贴”。它的设计和评分方式默认你会在运行之前先自己推导结果再用-c参数打开参考答案对自己。这个“先猜后验”的节奏才是这类实验真正的训练目标。很多同学把模拟器当计算器用跑完就交结果到了 xv6 改内核的部分连“预期行为是什么”都描述不出来。2.2 调度模拟器scheduler.py 的最小运行与必调参数调度实验对应 OSTEP 里 CPU 虚拟化那一章核心是搞清楚不同调度策略下进程的周转时间、响应时间和完成时间分别怎么变化。常见做法是用scheduler.py这个模拟器最小命令是这样# 先不开 -c自己推导完成时间和周转时间 python3 scheduler.py -p FIFO -l 3,2,1 -s 100 # 打开 -c 对答案看每个进程的实际完成时刻 python3 scheduler.py -p MLFQ -l 3,2,1 -s 100 -c第一行命令用 FIFO 策略处理三个进程负载分别是 3、2、1 个时间单位随机种子是 100第二行换成 MLFQ 策略并打开-c让模拟器打印计算过程。逻辑上-p决定用哪种调度策略-l给出每个进程需要的 CPU 时间-s是随机种子它决定进程的到达顺序和可能的优先级初值。-c是“计算并显示答案”的开关不开它模拟器只输出进程列表和最终统计逼你自己算。调度模拟器里最值得调的参数是这几个参数作用建议实验-p指定调度策略FIFO、SJF、RR、MLFQ同一组负载下换策略对比完成时间-l设置每个进程的负载多个值用逗号分隔让负载分布从“均匀”改成“两极分化”-s随机种子决定到达序列固定种子才能复现和对比输出-q时间片长度RR 和 MLFQ 专用从 10 改成 1观察响应时间变化-j进程数量配合随机负载生成和-l二选一用我一般会做的第一个实验不是调策略而是把同一个负载分别用 FIFO、SJF、RR 跑一遍先不开-c把每个进程的完成时间写在纸上再打开-c对比。对不上的地方九成不是算错而是把“周转时间 完成时间 - 到达时间”和“响应时间 第一次被调度时间 - 到达时间”这两个定义记混了。2.3 内存与并发模拟器malloc.py、paging.py、threads.py 的用法对比内存部分有三个模拟器覆盖不同场景用户态内存分配、地址翻译、页表相关逻辑。malloc.py模拟的是堆内存分配策略核心参数是-p指定分配策略、-A指定分配和释放序列# BEST 适配先分配 100 字节再释放前 50 字节观察碎片情况 python3 malloc.py -p BEST -A 100,-50 # 地址翻译模拟地址空间 64KB物理内存 32KB页大小 4KB python3 paging.py -a 64 -p 32 -P 4 -c第二条命令里-a是地址空间大小-p是物理内存大小-P是页大小单位都是 KB。paging.py会生成一组虚拟地址让你手动完成页号、偏移量的拆分和页表查询-c打印翻译结果。这里最容易忽略的是页大小-P一变页表项数量呈反比变化。实验时应固定地址空间、只改页大小观察页表膨胀和碎片的变化趋势。并发模拟器threads.py对应并发那一章参数少但语义容易误解-t是线程数-l是每个线程循环次数-i是中断频率。中断频率越大数值越小线程越容易被切换竞态条件复现概率越高。很多人以为-l越大越容易出问题实际上关键在-i把-i从 100 改成 1几乎每次跑都能看到中间状态被切走。2.4 怎么确认自己真的会了先猜答案再开 -c 对答案这个项目里模拟器实验的提交物一般不是运行输出而是你的一份推导记录对每个用例先写明你预测的完成时间、分配到的内存块位置或页表项内容再贴命令和-c后的真实输出。两者一致的地方不用多看不一致的地方才是得分点。所以实际操作时我会把每个实验拆成三步第一步闭卷推导用书里的公式手算第二步把推导结果写成注释放在命令上方第三步跑命令对答案。# 预测FIFO 下 P1 完成于 3P2 完成于 5P3 完成于 6 python3 scheduler.py -p FIFO -l 3,2,1 -s 100 -c如果预测和实际不一致回书里找到对应公式而不是调参数“凑出”正确答案。这一步养成的习惯到 xv6 实验时价值极大。提示模拟器输出里的“答案”是参考答案不是实验现象。提交时不要只粘输出作业要求里一般明确写了要附推导过程哪怕没写附上也能在批改时少很多争议。3. xv6 实验怎么做从读内核源码到实现系统调用的完整链路模拟器解决的是“看懂概念”xv6 部分解决的是“会改代码”。xv6 是一套专门为教学设计的操作系统代码量不大但进程、调度、内存、文件系统都有是目前高校操作系统实验里最常见的载体。一套 ostep 实验项目里的 xv6 环节通常由几个递进的任务组成加一个系统调用、改调度策略、处理页表相关问题。这一章按“认路、动手、验证”三个环节展开。3.1 先认识 xv6 的代码结构只看最关键的 4 个目录拿到 xv6 源码先别从头读到尾。按实验要动的范围有 4 个位置必须认清楚目录/文件内容实验相关点kernel/内核核心代码proc.c、syscall.c、trap.c、vm.c等系统调用实现、调度器、页表都在这里user/用户态程序包括 shell以及你自己写的测试程序测试程序要放这里并在 Makefile 注册Makefile构建脚本包含 qemu 启动参数和 UPROGS 列表新增用户程序必须改它mkfs/或相关工具制作文件系统镜像改内核启动参数时偶尔需要阅读顺序建议是先读kernel/proc.h里的struct proc知道一个进程有哪些字段再读kernel/proc.c里的allocproc()和scheduler()知道进程如何创建、如何被调度最后读kernel/syscall.c的分发表知道用户态请求如何到达内核。按实验点读而不是按目录顺序通读是这套 xv6 实验里最省时间的方法。3.2 系统调用实验从用户态到内核态的完整链路实验的入门任务通常是“新增一个系统调用”。以最常见的任务“获取当前进程的父进程 PID”为例需要动 5 处代码链路从用户态到内核态逐一打通// 1. kernel/syscall.h分配系统调用号在现有最大号之后顺延 #define SYS_getppid 23 // 2. kernel/syscall.c声明并注册分发函数 extern uint64 sys_getppid(void); [SYS_getppid] sys_getppid, // 3. kernel/sysproc.c实现内核函数本体 uint64 sys_getppid(void) { struct proc *p myproc(); if (p-parent 0) return -1; return p-parent-pid; }// 4. user/usys.S添加用户态入口汇编跳板 SYSCALL(getppid) // 5. user/user.h声明用户态可见的函数 int getppid(void);这 5 处分别对应系统调用号的全局约定、内核分发表、内核实现函数、用户态汇编入口、用户态头文件声明。漏掉第 4 处用户程序能编译但链接不到入口漏掉第 2 处内核收到调用号会找不到分发函数漏掉第 1 处整个表项都会错位。检查顺序就按这 5 处从上到下 grep基本能定位 90% 的问题。注意系统调用号的分配必须从当前最大值往后顺延不要使用已有编号函数名约定是sys_前缀大小写不能错。3.3 调度与页表实验改一个函数和改一张表的不同难度系统调用做完下一个任务通常是改调度器。xv6 默认的scheduler()是一个最简单的轮转遍历进程表找到第一个RUNNABLE的进程就切换过去。改成优先级调度或彩票调度核心改动集中在一个函数// kernel/proc.c 中 scheduler() 的骨架示意 for (;;) { // 关中断释放内核锁开始选择下一个进程 for (p proc; p proc[NPROC]; p) { if (p-state RUNNABLE) { // 你的选择逻辑 // 默认是“遇到第一个就选” // 做作业时通常改成“遍历完所有 RUNNABLE按优先级字段挑一个” } } }改动面积不大但连带动作很多在struct proc里加优先级字段、在fork()或allocproc()里初始化、在yield()的时机上判断是否抢占。这个任务最多的翻车点不在调度逻辑本身而在“加了字段但没初始化”导致新进程的优先级是内存里残留的垃圾值调度行为随机出现。页表类实验则完全不是一个量级它要求你改vm.c里的页表创建和映射逻辑如果做写时复制还要改trap.c的缺页处理属于横跨内存管理和陷阱处理的综合题。做这类任务前把walk()和mappages()这两个函数读透比急着改代码有用得多。这类实验在 ostep 操作系统实验项目里通常被放在最后作为区分“会写代码”和“理解系统”的分水岭。3.4 实验验证qemu 运行、测试程序与 gdb 断点xv6 实验跑在 qemu 模拟器上验证路径有三条。第一条是启动系统并手动操作make qemu # 编译并启动 xv6在 xv6 的 shell 里运行你写的测试程序观察输出是否符合预期。第二条是自动评分课程实验如果配置了评分脚本通常执行make grade它会一次性跑完所有用例并逐项打分。第三条是内核调试启动时用make qemu-gdb会拉起一个 gdb 等待远程连接可以在内核函数上打断点、查变量适合定位“一跑就 panic”这类问题。手动验证时要注意测试程序得放在user/目录并且在Makefile的UPROGS列表里加一行否则编译出的镜像里没有这个程序。很多同学改完代码在 xv6 的 shell 里说“找不到命令”根因就是漏了这一步。4. 实验避坑5 个让代码“看起来对了但就是拿不到分”的典型问题这套实验项目里最常见的一类挫败感是代码逻辑看着没问题输出也“差不多”但评分就是不过。这一章把模拟器和 xv6 两侧的高频问题各挑几个按“现象、原因、解决”写清楚。4.1 模拟器输出对不上与推导全错这两个入门坑最隐蔽现象一自己跑scheduler.py的输出和别人贴出来的答案完全对不上连进程到达顺序都不一样以为是实验包装错了。原因随机种子-s不一致或两个环境下默认 Python 版本不同导致随机序列不同。老版本的模拟器作业说明里到处是-s 0、-s 1这类种子但并没有强制要求所有对比都固定同一个种子。解决统一用python3执行并且对比前确认双方命令里的-s完全一致否则没有任何可比性。这是模拟器实验里最浪费时间的一个坑十有八九是参数没对齐。现象二推导时觉得自己算得没错一开-c发现完成时间全部错位。原因把进程“到达时间”默认成了 0但模拟器生成随机负载时不同进程的到达时间可能不同另外“周转时间 完成 - 到达”和“响应时间 首次调度 - 到达”这两个公式混用也会导致整列数据对不上。解决先看模拟器打印的进程列表里有没有到达时间字段有的话把到达时间标在草稿上再算-c输出里每一列都有公式提示对着差异列反推是哪个公式错了。4.2 xv6 的编译、入口与返回值这三个翻车现场是扣分重灾区现象三照着文档加了 5 处代码make报undefined reference to sys_xxx。原因往往是syscall.h的编号加了但syscall.c里的函数声明拼写不一致或者extern漏了还有人是把实现写进了syscall.c但分发表里注册的函数名和实现函数名大小写不一致——这类问题编译器会给一个看起来吓人、实际原因很简单的链接错误。解决按前面 3.2 的 5 个位置逐一 grep先查syscall.c的声明再查syscall.h的编号最后查实现文件里的函数名大小写。这三处一致链接一定过。现象四用户态程序调用新系统调用后返回值是 -1或者直接触发 panic。原因xv6 的系统调用返回值约定是a0寄存器里的值就是用户态拿到的值默认“成功返回 0失败返回 -1”。很多实现把这条约定搞反或者内核函数里访问了空指针比如拿p-parent时没判断父进程是否存在就解引用。解决先在sysproc.c里写一个固定返回值比如return 0;跑通链路后再加真实逻辑涉及指针访问前先做空值判断尤其是进程关系相关的字段宁可返回 -1 也不要 panic。现象五手动跑测试程序一切正常make grade却挂在同一个用例上。原因评分脚本对标准输出格式敏感。你在调试时往printf里打的额外信息手动运行看不出来但评分脚本会把它和期望输出做逐字符比较多一点换行或少一个空格都算失败。解决调试信息不要走printf改用条件编译或专门的环境变量开关控制提交前把调试输出全部清掉确认输出与预期完全一致再交。注意评分脚本比较的是“行为”而不是“意图”。代码里把算法写对了但输出格式不对依然会扣分。这类问题在 xv6 实验中比算法错误本身更常见。5. 把实验做成能力三个进阶验证习惯做完这一整套 ostep 操作系统实验收获最大的往往不是某个算法的实现而是三套“怎么验证自己写对了”的习惯。这三套习惯可以直接带走用在你以后任何涉及系统编程的工作里。第一个习惯先写预测再运行。模拟器实验里是手算完成时间xv6 实验里是在注释里写“这行改动应该让哪个测试用例从 fail 变 pass”。预测错了不要紧预测和结果不一致的位置就是你理解有偏差的位置比闷头调代码有价值得多。第二个习惯改完一处跑全量回归。很多人做 xv6 实验只跑自己新增的用例结果改了scheduler()之后原来已经通过的系统调用测试挂了。每次改动后用make grade把全部用例跑一遍能在提交前就把副作用找出来这比手动一个个敲命令可靠得多。第三个习惯用调试器代替 printf。页表类和陷阱类实验printf 大法会让人疯掉——你根本不知道哪一行触发了缺页中断。我自己做页表相关任务时就是在trap.c里加了一堆打印最后靠 gdb 的 watch 命令监视某个变量在被谁改写才定位到问题。xv6 的make qemu-gdb已经把调试链路做好了值得花一小时学会后面所有内核实验都会用它收尾。这三条习惯本质上是把“写代码”这件事变成了“写代码加设计验证”。操作系统实验的评分是自动的但工作中没有自动化的裁判自己当裁判的能力就是从这种“先猜后验”的日常训练里长出来的。希望你做完这套实验之后记住的不是那几个算法而是这一点。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

虚拟线程在Spring Boot 4中的高并发实战:秒杀与批量处理

虚拟线程在Spring Boot 4中的高并发实战:秒杀与批量处理

1. 先把结论放前面:虚拟线程到底解决了什么虚拟线程是我做后端这些年觉得最值得跟进的一个 JVM 特性,不是因为它性能比线程池高多少,而是它彻底改变了写高并发代码的姿势。以前一个 Tomcat 线程默认处理一个请求,线程数一多&#…

2026/10/17 16:50:51 阅读更多 →
具身智能破壁入世:VLA视觉-语言-动作模型的技术全貌与落地实践

具身智能破壁入世:VLA视觉-语言-动作模型的技术全貌与落地实践

1. 从“十一”这个编号说起:这套系列到底在讲什么我陆陆续续写了十篇关于具身智能行业观察的文章,从硬件本体聊到操作算法,从仿真环境聊到数据策略,内容比较零散,不成体系。这篇是第十一篇,标题定成了“具身…

2026/10/17 16:50:51 阅读更多 →
MySQL面试题不是背答案:一张知识点地图串起索引、日志与主从复制

MySQL面试题不是背答案:一张知识点地图串起索引、日志与主从复制

简介:一份MySQL面试题知识点总结文档,面向后端开发与求职人群,目标是系统归纳面试高频考点,帮助读者清晰掌握关系型与非关系型数据库差异、SQL语句执行全流程、索引底层数据结构与设计原理等核心内容。资源包包含1个docx文档&…

2026/10/17 16:50:51 阅读更多 →

最新新闻

数据指标多重应用:从口径定义到业务决策的实战指南

数据指标多重应用:从口径定义到业务决策的实战指南

看一眼标题,很多人第一反应是“哪个指标?”,然后就没有然后了。做数据和运营这些年,最明显的一个感受是:大家天天在看日活、看留存、看转化率,但这些“熟悉得不能再熟悉”的指标,绝大多数时候只…

2026/10/17 17:51:34 阅读更多 →
Windows 下 GDAL FileGDB 驱动编译与读写实战:从源码到可运行

Windows 下 GDAL FileGDB 驱动编译与读写实战:从源码到可运行

简介:这份资源面向在 Windows 平台从事地理数据处理与转换的开发者,尤其是需要在脱离 ArcGIS 环境的前提下读写 .gdb 文件地理数据库的工程师。针对 GDAL 3.5 及以下版本默认不支持写入 gdb 的问题,资源给出了基于 FileGDB 驱动的轻量级配置方…

2026/10/17 17:51:34 阅读更多 →
Hive动态分区为何必走Shuffle?揭秘Reduce强制触发机制

Hive动态分区为何必走Shuffle?揭秘Reduce强制触发机制

1. 动态分区不是“开个开关”就能跑得快:为什么Hive里一用动态分区就卡在Shuffle阶段你有没有遇到过这样的场景:写好一个带INSERT OVERWRITE TABLE ... PARTITION (dt, region)的SQL,本地小数据测试飞快,一上生产集群,…

2026/10/17 17:51:34 阅读更多 →
Teamcenter 2406部署避坑指南:四层客户端组件冲突与BMIDE环境变量配置

Teamcenter 2406部署避坑指南:四层客户端组件冲突与BMIDE环境变量配置

简介:这份文档面向Teamcenter实施与运维人员,聚焦PLM系统部署中客户端与开发工具的安装配置问题。内容围绕Deployment Center工具展开,覆盖TC24062、两层与四层客户端以及BMIDE的完整部署流程,适合具备一定PLM基础、需要独立完成环…

2026/10/17 17:51:34 阅读更多 →
基于Autoencoder的反欺诈异常检测实践:从原理到工程落地

基于Autoencoder的反欺诈异常检测实践:从原理到工程落地

做反欺诈相关算法的人,天天面对同一个矛盾:欺诈样本太少,而且永远在变。别的分类模型再强,喂进去的标签说白了都是历史旧账,而你要拦的是明天可能出现的新变种。所以我第一次在某个风控原型项目里尝试用 Autoencoder 做…

2026/10/17 17:51:34 阅读更多 →
MTK/高通/展锐刷机工具全解析:从协议到救砖实战

MTK/高通/展锐刷机工具全解析:从协议到救砖实战

简介:面向MTK、高通、展锐三大移动平台的版本升级烧录工具合集,适合设备厂商、开发者与售后维修人员使用,覆盖固件全量升级、增量升级、底层加载、分区写入、驱动识别与故障恢复等常见场景。资源包共1031个文件,压缩后181.59MB&am…

2026/10/17 17:50:33 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 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/16 21:54:38 阅读更多 →
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/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练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/16 14:23:36 阅读更多 →