读懂 systemd 状态机:从 failed 到 active 的真实含义
简介本资源是一本面向Linux系统管理员与运维工程师的systemd深度实践指南聚焦现代Linux系统服务管理、日志分析、启动优化与跨发行版标准化运维等核心痛点。全书以实战为导向系统讲解.service与.timer单元配置、journalctl日志过滤与故障定位、服务依赖图谱构建、并行启动机制原理等关键能力覆盖从桌面环境到企业服务器的多场景应用。资源为单文件PDF格式共1个文件大小9.58MB内容完整、排版规范适合作为随身查阅手册或系统化学习材料。目前已有966人学习下载读者可直接获取原版英文技术图书David Both著Apress出版的中文知识精要与实操路径掌握systemd作为PID 1进程之外的统一系统管理基石作用显著提升系统稳定性、可维护性与排错效率。1. 为什么改用 systemd 后服务启停像“玄学”——这不是配置写错了是没看懂它的状态机模型某开发者在迁移一个老旧的监控代理服务时遇到典型翻车systemctl start agentd显示active (running)但ps aux | grep agentd找不到进程journalctl -u agentd却满屏fork: Resource temporarily unavailable。他反复重载 unit 文件、加Restartalways、甚至把Type从simple换成forking问题依旧。直到他打开systemctl show agentd -p SubState -p ActiveState -p ExecMainPID才看到SubStatefailed、ActiveStateinactive、ExecMainPID0—— 原来进程早崩了systemd 却卡在“启动中”的假象里。这根本不是配置语法错误而是对 systemd 的状态机本质缺乏理解它不只管“启/停”更严格建模了inactive → activating → active → deactivating → inactive的全生命周期每个状态背后绑定着精确的进程控制逻辑、依赖解析规则和失败回滚策略。本文面向已能写基础.service文件、却常被failed状态卡住、重启不生效、日志查不到关键线索的中级运维与开发人员带你从内核级 cgroup 控制、unit 类型语义差异、到systemctl status输出每一行的真实含义一层层拆开这个 Linux 系统管理黑匣子。你不需要背命令但必须读懂 systemd 在告诉你什么。2. systemd 不是 init 的升级版它是基于 cgroup 的进程生命周期控制器2.1 为什么传统forkexec模式在 systemd 下会集体失效systemd 的核心设计哲学是进程不是孤立运行的而是属于某个资源控制组cgroup的受控实体。这意味着它不信任进程自己“说”自己活得好不好而是通过内核 cgroup 接口直接观测其真实状态。例如一个传统 shell 脚本启动的服务#!/bin/bash # /usr/local/bin/legacy-start.sh nohup /opt/app/bin/server --config /etc/app.conf /var/log/app.log 21 echo $! /var/run/app.pid在 SysV init 下只要脚本返回 0init 就认为服务“启动成功”。但在 systemd 中如果你用Typeforking并设置PIDFile/var/run/app.pidsystemd 会做三件事启动该脚本读取/var/run/app.pid获取主进程 PID立即检查该 PID 是否仍在其所属的 cgroup 中存活通过/proc/pid/cgroup和cgroup.procs验证。如果脚本 fork 出子进程后父进程退出而子进程因权限问题无法写入 cgroup比如未启用Delegateyes或子进程启动后立刻崩溃但 PID 文件已写入systemd 就会判定PIDFile指向的进程“不存在于预期 cgroup”从而将 unit 置为failed即使ps还能看到残留进程。这是最典型的“玄学失败”。提示systemd-run --scope是验证 cgroup 行为的最快方式。执行systemd-run --scope --scope-prefixtest sleep 10然后cat /proc/$(pgrep sleep)/cgroup | grep test你能直观看到进程如何被自动挂入system.slice/test-*.scope。2.2 四种Type的底层行为差异别再无脑抄TypesimpleType决定了 systemd 如何定义“服务已就绪”。它不是启动方式选择而是就绪信号契约。选错类型等于和服务进程签了一份无效合同。Typesystemd 认为“服务就绪”的条件典型适用场景错配后果示例simple主进程exec 启动的第一个进程进入running状态即视为就绪默认值大多数 Go/Python/Rust 编写的单进程服务若服务 fork 后父进程退出systemd 误判为“已就绪”实际子进程可能未初始化完就崩forking主进程 fork 出子进程后退出systemd 读取PIDFile并等待该 PID 进程进入running状态传统 daemon如 nginx、redis-serverPIDFile 路径错误或子进程启动慢systemd 超时start-limit-hitoneshot主进程退出即视为完成常配合RemainAfterExityes实现“仅执行一次”的初始化任务磁盘挂载、防火墙规则加载、数据库 schema 初始化误用于长期服务systemctl status显示inactive却进程仍在跑notify主进程通过sd_notify(3)发送READY1信号systemd 收到后才标记为active需要复杂初始化如加载插件、连接 DB后才真正就绪的服务未链接libsystemd或忘记调用sd_notify(READY1)服务永远卡在activating验证方法写一个最小notify服务测试单元# /etc/systemd/system/test-notify.service [Unit] DescriptionTest notify type [Service] Typenotify ExecStart/bin/bash -c echo \Starting...\; sleep 2; echo \READY1\ | systemd-notify --fd3; sleep 10 # 注意systemd-notify 必须通过 --fd3 使用 sd_notify 协议 [Install] WantedBymulti-user.target启动后执行systemctl status test-notify你会看到状态从activating (start)变为active (running)的精确时间点而非simple类型下“一启动就显示 active”。2.3WantedBy与RequiredBy的依赖图不是线性链而是 DAG有向无环图很多人以为WantedBymulti-user.target就是“开机自启”其实它声明的是当multi-user.target被激活时此 unit 应被包含在激活集合中。但最终是否真的启动取决于整个依赖图的拓扑排序结果。例如若A.serviceWantsB.service而B.service设置了ConditionPathExists/etc/b-disabled且该文件存在则B不会被启动但A仍会正常启动Wants是弱依赖。而若A.serviceRequiresB.service则B启动失败会导致A立即进入failed状态。更关键的是multi-user.target本身并不“启动”任何服务它只是一个同步点synchronization point。所有WantedBymulti-user.target的服务会在multi-user.target的Before和After关系约束下并行启动。这就是为什么有时你发现 20 个服务同时starting但systemctl list-dependencies multi-user.target --reverse却显示它们之间毫无关联——因为它们都直接指向同一个 target而非彼此依赖。提示用systemd-analyze plot deps.svg生成依赖图 SVG用浏览器打开搜索你的 service 名观察它在图中的位置和入边in-edges。你会发现绝大多数服务的直接上游是multi-user.target或network-online.target而非其他具体 service。3.systemctl status每一行都在说真话解码你忽略的 7 个关键字段3.1Loaded:行里的路径和 enable 状态暴露 unit 加载来源Loaded: loaded (/etc/systemd/system/myapp.service; enabled; vendor preset: disabled)这一行包含三个信息层路径/etc/systemd/system/myapp.service表示当前生效的 unit 文件位置。注意/lib/systemd/system/是 vendor 默认路径/etc/systemd/system/是管理员覆盖路径。若两者同名后者优先。enabled表示该 unit 已通过systemctl enable创建了到 target 的软链接如/etc/systemd/system/multi-user.target.wants/myapp.service。但这不保证服务会启动——如果 target 本身未被激活如系统运行在rescue.targetenabled也无效。vendor preset: disabled表示该 unit 在 vendor 包安装时默认被设为disable由/usr/lib/systemd/system-preset/*.preset文件定义。systemctl preset命令可批量恢复 vendor 预设。常见误区systemctl disable myapp只删除软链接不会删除 unit 文件本身。若你修改了/etc/systemd/system/myapp.service后执行disable enable新配置才会生效。否则enable只是重建旧链接。3.2Active:行的substate比state更能定位问题Active: active (running) since Mon 2024-06-10 14:22:33 CST; 2min 15s agoactive (running)是复合状态括号内running即SubState。SubState是诊断黄金指标它比ActiveStateactive/inactive/failed更细粒度SubState触发条件排查重点running主进程 PID 存在且在其 cgroup 中活跃检查ExecMainPID是否为 0ps -o pid,cgroup $(cat /proc/$(systemctl show myapp -p ExecMainPID --value)/cgroup)exitedTypeoneshot服务执行完毕退出且RemainAfterExitno检查RemainAfterExit是否遗漏start-pre正在执行ExecStartPre命令journalctl -u myapp -n 50 --no-pager查看 pre 脚本输出start-post正在执行ExecStartPost命令同上但关注 post 脚本stop-sigtermsystemd 已发送SIGTERM等待进程自行退出默认超时 90ssystemctl show myapp -p TimeoutStopSec查看超时值stop-sigkillSIGTERM超时后systemd 强制发送SIGKILL进程未响应SIGTERM需检查信号处理逻辑或死锁执行systemctl show myapp -p SubState -p ActiveState -p ExecMainPID -p StateChangeTimestamp是快速定位“为什么 status 显示 running 但实际没干活”的标准动作。3.3Main PID:和Control Group:揭示真正的资源归属Main PID: 12345 (myapp) Control Group: /system.slice/myapp.serviceMain PID是 systemd 记录的主进程 PID。若为0说明 systemd 认为主进程已不存在即使ps还能看到那也是孤儿进程。Control Group路径至关重要。它对应/sys/fs/cgroup/systemd/下的真实目录。进入该目录cd /sys/fs/cgroup/systemd/system.slice/myapp.service cat cgroup.procs # 查看当前属于该 cgroup 的所有 PID cat memory.max # 查看内存限制若设置了 MemoryMax cat cpu.weight # 查看 CPU 权重若设置了 CPUWeight若cgroup.procs为空说明进程已脱离 cgroup常见于未正确 daemonize 的程序若memory.max显示max说明未设置内存限制若为数字如524288000即 500MB 限制。注意systemctl status中的Control Group路径是 systemd 内部标识与cgroup2的挂载点路径一致。不要试图cd到system.slice/...而应cd /sys/fs/cgroup/systemd/system.slice/myapp.service。4. 避坑生产环境踩过的 5 个血泪经验每一条都让排查时间缩短 80%4.1 现象systemctl restart myapp后服务短暂启动又立即failedjournalctl显示exit code Exited with codeexited, status203/EXEC原因ExecStart指向的二进制文件权限不足或其动态链接库缺失。status203/EXEC是 systemd 特有错误码表示execve()系统调用失败非进程内部崩溃。常见于二进制文件无x权限二进制是 64 位但系统缺少libc6-amd64使用chroot或容器化环境/usr/lib/x86_64-linux-gnu/路径不可达。解决ls -l /opt/myapp/bin/myapp确认权限ldd /opt/myapp/bin/myapp检查依赖库是否not found若在 chroot确保ldd输出的所有.so路径均在 chroot 根目录下存在。4.2 现象服务在systemctl start后显示active (running)但curl http://localhost:8080/health返回Connection refused原因Typesimple下systemd 在exec返回后即标记为active但应用可能还在初始化网络监听端口。此时active≠ “已监听端口”。解决改用Typenotify并在应用代码中初始化监听器后调用sd_notify(READY1)或使用TypesimpleExecStartPost/bin/sh -c while ! curl -sf http://localhost:8080/health; do sleep 0.1; done仅调试用生产禁用最佳实践在 unit 中添加ExecStartPost/bin/sh -c ss -tln | grep :8080确保端口监听成功后再继续。4.3 现象systemctl stop myapp后ps aux | grep myapp仍显示进程且systemctl status显示deactivating (stop-sigterm)原因进程未正确处理SIGTERM或存在子进程未被 cgroup 继承KillModecontrol-group默认开启但若进程 fork 后未prctl(PR_SET_CHILD_SUBREAPER, 1)子进程可能逃逸出 cgroup。解决在 unit 中显式设置KillModemixed向主进程发SIGTERM向所有子进程发SIGKILL或KillModecontrol-groupSendSIGKILLyes确保子进程也被杀检查进程是否调用setsid()或daemon(3)这可能导致子进程脱离原始 cgroup。4.4 现象systemctl daemon-reload后systemctl status myapp显示配置未更新仍用旧参数原因daemon-reload只重新加载 unit 文件不重启正在运行的服务。已运行的服务仍使用旧配置。解决修改 unit 后必须执行systemctl daemon-reload systemctl restart myapp若只想重载配置而不中断服务如修改EnvironmentFile可systemctl kill --signalSIGHUP myapp前提是应用支持 HUP 重载验证systemctl show myapp -p Environment -p ExecStart对比修改前后输出。4.5 现象服务在systemctl start时触发start-limit-hit后续start直接失败原因systemd 对 unit 启动失败实施速率限制默认StartLimitIntervalSec10s内最多StartLimitBurst5次失败。连续失败 5 次后该 unit 被锁定systemctl start直接返回Job for myapp.service failed because start-limit-hit。解决临时解除systemctl reset-failed myapp永久调整谨慎[Unit] StartLimitIntervalSec60 StartLimitBurst3根本解决用journalctl -u myapp -n 100 --no-pager定位首次失败原因修复后再reset-failed。5. 进阶技巧用systemd-run构建可审计、可回收的临时工作流5.1 为什么systemd-run比nohup 更适合运维脚本nohup script.sh 启动的进程无 cgroup 隔离资源使用不可控无明确生命周期ps查找困难kill易误伤无日志自动归集nohup.out分散各处。而systemd-run创建的 scope unit自动分配唯一 cgroup如/system.slice/run-rf3a2b1c.scope支持--on-failure指定失败回调日志自动打上_SYSTEMD_UNITrun-rf3a2b1c.scope标签journalctl _SYSTEMD_UNITrun-rf3a2b1c.scope一键过滤systemctl stop run-rf3a2b1c.scope可强制终止整个进程树。5.2 构建一个带超时、失败回调、资源限制的备份脚本#!/bin/bash # backup-job.sh BACKUP_ID$(date %Y%m%d-%H%M%S)-$RANDOM LOG_FILE/var/log/backup/${BACKUP_ID}.log # 使用 systemd-run 启动带完整控制 systemd-run \ --scope \ --scope-prefixbackup-${BACKUP_ID} \ --propertyMemoryMax2G \ --propertyCPUQuota50% \ --propertyTasksMax100 \ --on-failurebackup-fail-handler${BACKUP_ID}.service \ --on-successbackup-success-handler${BACKUP_ID}.service \ --timer-propertyAccuracySec1s \ --quiet \ bash -c echo \$(date): Starting backup ${LOG_FILE}; /usr/bin/rsync -av --delete /data/ /backup/ ${LOG_FILE} 21; EXIT_CODE\$?; echo \$(date): Backup finished with code \${EXIT_CODE} ${LOG_FILE}; exit \${EXIT_CODE} # 等待完成最大 2 小时 systemd-run --scope --scope-prefixwait-${BACKUP_ID} \ --on-active2h \ --on-unit-activebackup-${BACKUP_ID}.scope \ --on-unit-inactivebackup-${BACKUP_ID}.scope \ --on-failurebackup-timeout-handler${BACKUP_ID}.service \ /bin/true配套的失败处理 service/etc/systemd/system/backup-fail-handler.service[Unit] DescriptionHandle backup failure for %i [Service] Typeoneshot ExecStart/bin/bash -c echo \$(date): Backup %i FAILED, sending alert\ | mail -s \Backup FAIL\ adminexample.com RemainAfterExityes这样每次备份都有独立 scope、独立日志、独立失败回调且资源被硬性限制彻底告别“备份脚本吃光内存导致数据库 OOM”。5.3 用systemd-cgtop和systemd-cgls实时观测 cgroup 资源水位systemd-cgtop是top的 cgroup 版本实时显示各 slice 的 CPU、内存、IO 使用率# 按内存使用排序 systemd-cgtop -m -P # 按 CPU 使用排序 systemd-cgtop -P # 查看 myapp.service 的完整 cgroup 树 systemd-cgls system.slice/myapp.service输出示例system.slice/myapp.service ├─12345 /opt/myapp/bin/myapp --config /etc/myapp.conf ├─12346 /opt/myapp/bin/watchdog └─12347 /opt/myapp/bin/logger若发现myapp.service下只有主进程但ps aux | grep myapp显示大量子进程说明子进程未被正确继承到该 cgroup —— 这正是KillMode配置不当的铁证。我坚持一个习惯每次上线新服务必先systemd-run --scope --scope-prefixtest-$SERVICE_NAME sleep 30然后systemd-cgls确认进程树结构再systemd-cgtop -m观察内存增长曲线。这 30 秒省去后期 3 小时排查 cgroup 逃逸的痛苦。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

Samba多用户权限配置实战:从共享到ACL的完整指南

Samba多用户权限配置实战:从共享到ACL的完整指南

简介:这份资源聚焦 Linux 环境下 Samba 共享目录的多用户权限配置,面向需要打通 Linux 与 Windows 文件共享的运维人员、系统管理员及有一定 Linux 基础的进阶学习者。内容围绕用户与组管理、smb.conf 全局与共享段配置、目录属主与权限掩码设置、服务重…

2026/10/11 10:30:13 阅读更多 →
绿色版Directory Opus 9.5.0.0实战:配置、批量处理与避坑指南

绿色版Directory Opus 9.5.0.0实战:配置、批量处理与避坑指南

简介:Directory Opus 9.5.0.0 3568.x86 绿色特别版是一款面向 Windows 32 位系统的专业文件管理工具,适合希望替代系统自带资源管理器、追求高效文件操作与便携使用的普通及进阶用户。它支持双面板或多面板布局,集成批量重命名、目录同步、快…

2026/10/11 10:29:12 阅读更多 →
情感分析从模型跑通到业务敢用:技术选型、微调参数与避坑指南

情感分析从模型跑通到业务敢用:技术选型、微调参数与避坑指南

简介:这份资源是一篇系统梳理自然语言处理中情感分析技术的docx文档,面向NLP研究人员、数据科学家及从事情感分析应用的专业人士。内容从研究背景与意义切入,依次探讨基于规则、机器学习与深度学习三类方法的原理与差异,并通过公开…

2026/10/11 10:29:12 阅读更多 →

最新新闻

ImageJ Windows版从闪退到批量出图:内存设置、宏批处理与插件安装全攻略

ImageJ Windows版从闪退到批量出图:内存设置、宏批处理与插件安装全攻略

简介:这款ImageJ Windows版本采用64位Java 8捆绑,开箱即用,适合生物医学、材料科学等领域研究人员进行图像分析与测量。资源共430个文件,压缩包约47.72MB,以ijm宏、dll动态库、jar插件和java源码为主,同时内…

2026/10/11 13:07:48 阅读更多 →
YCBlogs算法笔记:选择排序深度解析——直接选择排序与树形锦标赛排序原理及Java实现

YCBlogs算法笔记:选择排序深度解析——直接选择排序与树形锦标赛排序原理及Java实现

教程技术博客文档 【免费下载链接】YCBlogs 技术博客笔记大汇总,包括Java基础,线程,并发,数据结构;Android技术博客等等;常用设计模式;常见的算法;网络协议知识点;部分fl…

2026/10/11 13:07:48 阅读更多 →
张掖市30米DEM数据处理:坐标系检查与裁剪避坑指南

张掖市30米DEM数据处理:坐标系检查与裁剪避坑指南

简介:这是一套面向地理信息系统学习者与科研人员的区域高程数据包,内含甘肃省张掖市及周边地区三十米分辨率的数字高程模型,并附有行政边界矢量文件,可服务于地形识别、坡向坡度分析、水文模拟和城市规划等任务。数据包共十二个文…

2026/10/11 13:07:48 阅读更多 →
Docker 19.03.9离线部署:本地yum源与镜像导入实战

Docker 19.03.9离线部署:本地yum源与镜像导入实战

简介:面向需要在内网或离线环境部署容器服务的运维与开发人员,这份docker19.03.9离线部署工具提供了完整的安装物料,免去逐台机器联网拉取依赖的麻烦,适合网络受限机房、内网生产环境或需要统一Docker版本的团队使用。压缩包约57.…

2026/10/11 13:07:48 阅读更多 →
32MB系统运维工具箱:从硬件检测到SECS/GEM调试的实战拆解

32MB系统运维工具箱:从硬件检测到SECS/GEM调试的实战拆解

1. 项目概述:32MB 的容量,凭什么装下整个运维工作台做系统运维这行,最怕的不是故障本身,而是故障来了手里没趁手的家伙。经历过那种现场环境:客户机房的机器亮了红灯,你掏出U盘却发现里面只有个大而全的“全…

2026/10/11 13:07:48 阅读更多 →
Personal Agent走到岔路口:对话是起点,还是终点

Personal Agent走到岔路口:对话是起点,还是终点

编辑:前沿在线 编辑部过去一个月,Personal Agent 的热度集中爆发。Meta 的 Muse、OpenAI 的 Dots 先后亮相,前者打通云端邮件、日历、支付全链路,后者连接四千多款应用替人办事。行业里几乎一边倒的声音是:App 的时代结…

2026/10/11 13:06:47 阅读更多 →

日新闻

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

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

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

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

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

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

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

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

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

2026/10/11 0:00:27 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/11 0:00:27 阅读更多 →

月新闻

我发现了一个新思路:用 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/11 10:45:37 阅读更多 →
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/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练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/10 10:38:42 阅读更多 →