news 2026/9/27 1:51:44

5G NR寻呼机制详解:PF/PO计算、P-RNTI与参数配置避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
5G NR寻呼机制详解:PF/PO计算、P-RNTI与参数配置避坑指南

简介:寻呼(Paging)是5G(NR)网络中的关键机制,这份资源面向5G网络优化工程师与通信网规网优人员,系统讲解NR寻呼的触发原因与消息结构,并对比4G LTE的差异。文档从LTE三种寻呼触发(RRC建立、系统消息更新、PWS/ETWS通知)入手,过渡到5G新增的DCI 1_0携带P_RNTI的PWS/ETWS通知及PDSCH应答,揭示5G如何更灵活地唤醒UE。内容重点剖析寻呼消息PagingRecordList、PagingRecord、PagingUE-Identity(5G-S-TMSI/I-RNTI)及accessType等字段,并说明非3GPP接入类型,便于读者从ASN.1层面理解寻呼过程。资源包内含1个docx文档,压缩包总大小15KB,内容紧凑、结构清晰,适合作为5G信令流程学习的速查笔记。已有988人学习,对需要掌握NR空口寻呼机制、开展网络优化与参数调优的工程师具有实用参考价值,理解寻呼直接影响UE唤醒效率与系统资源利用,对网络性能优化至关重要。

1. 5G(NR)网络中的寻呼(Paging):空闲态UE怎么被精准找到

一台5G手机在高铁上挂着RRC_IDLE态,没有专用资源,用户突然收到一条微信。基站侧只知道这个UE最近驻留过的tracking area,并不清楚它此刻在哪个小区。要在几毫秒内找到它,靠的不是“持续上报”,而是一套设计精妙的寻呼(Paging)广播机制:核心网只在可能的区域范围发一个寻呼请求,UE则按约定好的周期在某个无线帧的某个时机醒来监听,双方靠一个从TMSI算出来的“编号”默契地碰头。这正是5G NR寻呼要解决的核心问题。这篇笔记适合三类人:刚接触5G协议栈、把寻呼当“一堆计算公式”但不知道怎么落地的开发新手;在做OAI或商用设备联调、被PF/PO计算坑过的测试工程师;以及做网络优化、需要解读寻呼参数和排查漏呼问题的运维同学。下文会从原理、公式、消息结构、参数配置一路写到常见翻车现场,最后给出验证寻呼是否正常的实操手段。

2. 寻呼时机PF/PO怎么算:一个Python脚本把公式落地

2.1 先理解两个关键概念:寻呼帧PF和寻呼时机PO

5G NR把时间划成10ms的无线帧(SFN)。为了不让UE在整个DRX周期里一直醒来,协议让基站只在某些帧上发送寻呼,这些帧叫寻呼帧PF。每个PF内部又可能安排多个寻呼时机PO,UE只去监听属于自己的那个PO上的PDCCH。所以网络和UE要从同一个参数集合算出同一个PF、PO,才能对上暗号。

3GPP TS 38.304给出的核心公式是:

  • PF满足:(SFN + PF_offset) mod T = (T div N) * (UE_ID mod N)
  • PO索引:i_s = floor(UE_ID / N) mod Ns

其中:

  • T是寻呼周期,单位是无线帧,由SIB1中的defaultPagingCycle决定,常见值是32、64、128、256。
  • N = min(T, nB),nB由SIB1里的nAndPagingFrameOffset配合T解析得到。比如nB=T、T/4、T/8这类值。N实际决定了把UE_ID分成的“组数”。
  • Ns是每个PF里PO的个数,SIB1中的ns枚举直接给出,常见1、2、4。
  • PF_offset是帧偏移,同样来自nAndPagingFrameOffset,用于把一个T内的PF在时间上错开。
  • UE_ID = 5G-S-TMSI mod 1024,注意是低10位,不是完整IMSI。

为什么要取mod 1024?因为5G-S-TMSI会被压缩成低10位参与计算,这样网络和UE算出来的结果一致,且能控制碰撞概率。这里有个容易被忽视的边界:如果核心网下发的5G-S-TMSI中临时身份分配不当,可能出现两个UE算到同一组PF/PO,它们会在同一PO收到对方的寻呼记录导致额外的唤醒,这个坑在第五章再展开。

2.2 把公式变成可运行的Python脚本

下面这段代码可以直接跑,输入四个参数就能得到某个UE在一个寻呼周期内所有可用的PF,以及它应该监听的PO索引,用于联调时核对网络侧配置和UE行为是否一致。注意代码里入参N用的是nB经过min(T, nB)处理后的实际值,也就是协议里的N。如果你手头只有SIB1原始IE,需要先按TS 38.331解析出nB和PF_offset。

# paging_calc.py # 用途:根据TS 38.304计算5G NR寻呼帧PF和寻呼时机PO索引 def calc_paging(T: int, nB: int, pf_offset: int, ns: int, tmsi: int): """ T : defaultPagingCycle, 无线帧数(如128) nB : 由nAndPagingFrameOffset解析出的值, 单位为无线帧数(如64) pf_offset: Paging Frame Offset, 0 ~ T-1 ns : 每个PF内的PO数量, 取1/2/4 tmsi : 5G-S-TMSI十进制值 """ N = min(T, nB) # 分组数 UE_ID = tmsi % 1024 # 低10位,协议规定 print(f"分组数N={N}, UE_ID={UE_ID}") # 公式条件: (SFN + pf_offset) mod T == (T // N) * (UE_ID % N) target = (T // N) * (UE_ID % N) pfs = [sfn for sfn in range(pf_offset, pf_offset + T) if (sfn + pf_offset) % T == target] i_s = (UE_ID // N) % ns # PO索引 return pfs, i_s if __name__ == "__main__": # 示例:defaultPagingCycle=128, nB=64, PF_offset=0, ns=2, TMSI=0x1234567 pf, po = calc_paging(T=128, nB=64, pf_offset=0, ns=2, tmsi=0x1234567) print(f"本周期内PF: {pf}, PO索引: {po}")

代码逻辑说明:第4行到第7行只是把协议公式翻译成Python。关键第14行的target,对应公式右边(T div N)*(UE_ID mod N);第15行遍历一个T周期的候选帧,满足同余关系的帧就是PF。第17行算PO索引。注意第15行的起点取pf_offset而不是0,这样打印出来的PF天然带上偏移,更直观。

参数说明里最容易搞反的是pf_offset和T的“加”关系。有时候配置面板上让工程师填“寻呼偏移占比”,协议栈解析出来是一个绝对值,如果两个单位没对齐,结果会差一个周期。我在脚本注释里把单位明确为无线帧,联调时能少一半沟通成本。

2.3 从SIB1读到配置后怎么手工验算

拿到一条实际的SIB1日志,里面会有类似如下的寻呼配置字段(不同厂商字段名略有差异,含义相同):

参数示例值说明
defaultPagingCyclerf128寻呼周期128帧,即1.28s
nAndPagingFrameOffsetn64 offset 0nB=64帧,PF_offset=0
nstwo每个PF里2个PO
firstPDCCH-MonitoringOccasionOfPO0第一个PO对应的PDCCH监听时机偏移
actualPOlCount5实际PO数(按SSB波束分布)

拿到这些值,假设UE的5G-S-TMSI换算成十进制是0x1234567,用上面脚本算出PF和PO索引。然后你需要确认:UE真的在算出来的那个时机去监听PDCCH了。在仿真链路或测试仪表里,把UE日志里唤醒时间点对准SFN,和脚本输出比对,这一步做对了,寻呼联调就成功了一半。

注意:脚本算出的PO索引只是“第几个PO”,真正监听的是哪个时隙,还要看firstPDCCH-MonitoringOccasionOfPO以及该小区实际发送的SSB波束数。波束很多时,同一个PO会被映射到多个PDCCH监听时机上,UE要在每个波束对应的时机盲检。这是新手最容易以为“公式算完就完了”的地方。

3. 寻呼消息在空口怎么传:P-RNTI、DCI 1_0与SIB1里的寻呼配置

3.1 一次寻呼接收,空口上发生了三件事

UE在PO醒来后,并不是直接听PDSCH,而是先在PDCCH上盲检。网络使用P-RNTI加扰DCI格式1_0,这个P-RNTI是固定的0xFFFE。UE一旦解出CRC匹配的DCI,就知道接下来有寻呼,然后根据DCI里调度的频域资源去解PDSCH,得到一条RRC消息:Paging。

整个链路可以简化成四步:

  1. PDCCH上用P-RNTI加扰发送DCI 1_0,指示PDSCH调度信息。
  2. PDSCH上承载Paging消息,用RRC的透明传输方式发送。
  3. UE解出Paging后,检查里面有没有自己的pagingRecord,或者是不是系统消息变更通知。
  4. 如果是被寻呼,UE马上发起RRCSetupRequest,携带原因值mt-Access(移动终止接入)。

这里有个所有协议栈实现都必须注意的细节:P-RNTI是所有UE共享的,所以每个PO上只有一个PDCCH搜索空间。有的开发者在写盲检逻辑时把P-RNTI当成C-RNTI那种独占分配,用“每个UE一个RNTI”的思路去处理,导致在PO上扫了多个搜索空间仍然找不到自己的DCI,这就是没有吃透共享RNTI的原生机制。

3.2 Paging消息里到底有什么:pagingRecordList与shortMessage

打开一条解析好的Paging消息(RRC层),核心字段如下表:

IE作用常见取值
pagingRecordList被寻呼UE的完整标识列表每条记录含ue-Identity
ue-Identity5G-S-TMSI或I-RNTI32比特
pagingCause寻呼原因mt-Access / emergency
shortMessage短消息,用于SI变更和告警systemInfoModification, etwsAndCmasIndication
lateNonCriticalExtension兼容扩展可选

当核心网因为下行数据到达发起寻呼时,pagingRecordList里放的是UE的5G-S-TMSI(32位)。当要通知所有UE“系统消息要改了”时,网络可以不带pagingRecordList,而只用shortMessage里的systemInfoModification标记。这两种寻呼的接收行为完全不同:前者只有匹配的UE才发起RRC连接,后者所有UE都要去读取新的SIB1。调试时如果只盯着pagingRecordList,而忽略了shortMessage,会漏掉系统消息变更场景的用例。

3.3 怎么用日志或抓包确认寻呼链路的每一环

常见做法是在终端侧和无线侧同时抓包。核心网侧看NGAP接口的Paging消息,gNB侧看MAC/PHY调度日志,UE侧看RRC层。三条日志能对上同一个5G-S-TMSI,基本就能证明寻呼从核心网到终端的端到端是通的。

在Wireshark里打开NGAP抓包后,可以直接在显示过滤器里输入ngap和paging去过滤,如果实际抓包中Paging过程没有单独字段解出来,用frame contains paging也能兜底。对应F1AP接口的RAN寻呼过滤思路类似。千万不要只凭一个过滤表达式就下结论,我遇到过gNB侧把NGAP Paging消息收到了,但在空口侧没有调度的现象,这时候要回到gNB的寻呼配置去查,而不是在核心网反复抓包。

# 示例:用tshark读取NGAP抓包,把带Paging过程的消息列出来 tshark -r ngap.pcapng -Y "frame contains paging" -T fields -e ngap.UEIdentityIndexValue

这条命令里的ngap.UEIdentityIndexValue字段名在不同Wireshark版本里可能略有差异,如果没有解出来,就只保留-Y过滤条件看整体消息,再手动展开协议树确认UE身份字段。工具只是辅助,重点是对着消息里的TAI list和UE身份逐条核验。

4. 一次寻呼从哪来:CN寻呼、RAN寻呼与系统消息变更的区别

4.1 三种寻呼触发源,三种截然不同的接收行为

5G NR把寻呼消息当“通用通知”,但它承载的意图分三类。第一类是CN触发的寻呼,UE处于RRC_IDLE,AMF收到下行数据或下行信令,在UE注册的TA list内发起。第二类是RAN触发的寻呼,UE处于RRC_INACTIVE,gNB在RNA范围内发起,不经过核心网。第三类是纯广播通知,比如系统消息变更、ETWS/CMAS灾害预警,用shortMessage实现。

这三类寻呼在空口上使用的P-RNTI和PO机制完全相同,但UE的响应策略完全不同。比如CN寻呼要求UE从IDLE进入连接态,发起RRCSetupRequest,原因是mt-Access;而RAN寻呼要求UE发起RRCResumeRequest,原因是mt-Access;如果是系统消息变更,UE不发起任何连接,只是按需重新读取SIB。很多同学在调RRC状态机时,只做了一类寻呼到达的响应,结果INACTIVE态UE收到RAN寻呼后直接RRCSetup而不是Resume,导致网络侧状态错乱。

4.2 CN寻呼流程:从AMF到gNB再到UE的消息链

用消息链视角看CN寻呼(这是UE被叫场景):

  1. UPF收到下行数据包,通知AMF。
  2. AMF根据UE上下文中的TA list,向这些TA覆盖下的所有gNB发送NGAP Paging消息。
  3. gNB收到后,结合本小区SIB1里的寻呼配置,算出该UE在本小区的PF/PO,生成RRC Paging消息调度出去。
  4. UE收到后回复RRCSetupRequest,gNB转发Initial UEMessage给AMF,AMF触发Service Request建立流程。

这条链上最容易出现的问题是:AMF下发的Paging里带的是5G-S-TMSI,而gNB在算PF/PO时需要UE_ID = 5G-S-TMSI mod 1024。如果核心网在某种流程下把临时身份的高位也塞进来,或者gNB错误地取了全量TMSI做mod,两边算出的PO就不一致,UE必然漏接。这种故障在商用网的仪表联调里非常常见,通常表现为“同一TA内部分小区寻呼正常、部分异常”。

4.3 RAN寻呼流程:RRC_INACTIVE态专用的“区域内找人”

RRC_INACTIVE是5G新增状态。UE在这个状态下保留完整无线上下文,但RRC连接被挂起。此时产生下行数据时,核心网并不知道UE在哪,只能靠gNB在RNA(RAN Notification Area)内发起RAN寻呼。RAN寻呼的消息格式和CN寻呼完全一样,区别在于gNB不需要等待AMF的NGAP消息,而是自己直接在RNA内所有小区把Paging调度出去。

RAN寻呼配置一般由基站侧的ran-PagingConfig决定,包括寻呼周期、最大重传次数等。实际联调OAI或商用网时,我遇到的一个共性问题:RNA范围配置得比TA list小很多,UE在注册的TA内进行了小区重选但仍在RNA内,基站侧却只在原来的RNA小区发寻呼,导致UE收不到。解决方法是把RAN寻呼的覆盖范围与TA list对齐,或者把RNA范围按重选边界外扩几个小区。

4.4 系统消息变更与灾害预警寻呼:只带shortMessage的寻呼

当SIB1里的一部分SIB需要更新时,网络在寻呼实例上发送shortMessage,置位systemInfoModification。UE收到后立刻在当前修改周期内重新读取SIB1,再按需读变更的SIB。这就是为什么在运营商的周期变更窗口,所有UE都会短暂唤醒一下——并不是被叫,而是被“通知更新”。

在5G实训室里如果想演示这个过程,可以在OAI的gNB上修改一个SIB参数,再触发修改周期,观察手机是否会周期唤醒。需要注意:系统消息修改只能发生在修改周期边界,修改周期长度由SIB1里的modificationPeriodCoeff乘以默认寻呼周期算出。有的初学者把这个参数配置得和defaultPagingCycle一样甚至更小,结果系统消息永远等不到修改周期,出现“改了参数但UE就是不重新读”的翻车现场。

5. 寻呼参数配置避坑:5个让人翻车的现场与排查清单

5.1 收不到寻呼但信令正常?先核对TAC和TA List

现象:NGAP接口上Paging消息已经发到gNB,空口却没有调度PDCCH,UE完全不知道被寻呼。

原因:gNB收到Paging后,会判断本小区TAC是否在AMF下发的Paging TA list内。如果TAC不匹配,gNB会把消息丢弃,不进入寻呼调度。常见于核心网配置了多个TA但gNB侧TAC配错,或者一个gNB多个小区对应不同TAC,核心网只往其中一个发。

解决:用日志确认gNB收到的Paging消息里的TAI list,逐项与本小区TAC比对。绝对不能只比对“看起来同一个地市就算同一个TA”。在做5G实训室组网时,建议先做一张TAC对照表,把AMF配置、gNB配置、核心网订阅数据三处统一。

5.2 待机电流异常高?可能是defaultPagingCycle太小

现象:UE待机功耗测试,电流比同类设备高一倍,且周期性唤醒频繁。

原因:SIB1里的defaultPagingCycle配成了32帧(320ms),UE每320ms就要醒来监听一次PDCCH,功耗自然高。商用网通常配置128或256帧,兼顾被叫时延。

解决:把defaultPagingCycle调大,同时关注Paging消息的DCI检测结果是否正常。如果调到256后出现被叫时延超标,再考虑用PO数量Ns或firstPDCCH-MonitoringOccasionOfPO来错峰,而不是盲目调小周期。

5.3 多波束下有UE在某个SSB方向永远漏呼

现象:同一小区内,大部分终端能收到寻呼,但个别在特定方向上的终端反复被叫失败。

原因:PO内部的PDCCH监听时机与SSB波束是一一映射的。如果gNB实际发送8个SSB波束,但firstPDCCH-MonitoringOccasionOfPO配置的起始偏移只覆盖了4个波束的监听位置,那后4个波束上的UE在整个PO周期内没有可监听的PDCCH时机,自然漏呼。

解决:核对SSB数量和PDCCH监听时机数量。在5G基站的波束配置里,通常要求PO内监听时机数大于等于SSB数,并且firstPDCCH-MonitoringOccasionOfPO按实际同步信号块位置设置。如果实在不好改,可以增加PO内的监听时机,也就是把Ns调大,但要注意增加UE功耗。

5.4 系统消息变更通知发了,UE却迟迟不更新SIB

现象:修改了SIB2里的某个参数,用网管触发系统消息更新,但部分手机还是用旧的系统参数接入,导致切换失败。

原因:系统消息变更要发生在修改周期的边界上。如果寻呼配置的modificationPeriodCoeff为2、defaultPagingCycle为32,修改周期只有64帧(640ms),网络侧在周期中间发生变更,UE虽然在周期内醒过一次,但没有恰好监听变更标记,错过之后要等下一个修改周期。

解决:修改系统参数时,统一在修改周期边界前将新值准备好,并在边界时刻触发。如果UE始终不更新,重点看UE是否真的在PO上解出了shortMessage里的systemInfoModification,而不是看网络侧有没有发。

5.5 NSA/EN-DC组网里NR侧寻呼一直为空

现象:NSA组网下,UE在LTE侧待机,NR逻辑下仅添加了辅节点,但测试人员想验证NR寻呼,发现空口完全没有NR寻呼调度。

原因:NSA组网中,UE没有在NR侧进入RRC_IDLE或RRC_INACTIVE,真正的寻呼监听发生在MCG(LTE侧),NR侧根本没有寻呼时机。这不是配置错误,而是协议流程决定的结果。

解决:要验证NR寻呼,应使用SA组网,或者在NSA中让UE进入NR侧的RRC_INACTIVE且由gNB发起RAN寻呼。在5G实训室建议直接搭SA的OAI或用商用SA核心网,否则会陷入“NR侧收不到寻呼”的伪故障。

5.6 补充一条:5G-S-TMSI低10位碰撞导致两个UE互相干扰

现象:两个UE在同一个PO上频繁同时醒来,且其中一个收到了另一个的寻呼记录,协议栈解析时丢弃陌生记录,但被叫成功率下降。

原因:5G-S-TMSI分配时低10位碰撞,导致两个UE的PF/PO相同。这是概率事件,但如果核心网临时身份分配策略不当,碰撞概率会显著升高。

解决:在核心网侧检查TMSI的低10位可用池,必要时用哈希规则打散。正常商用网中,唯一性由AMF保证,偶尔碰撞通过UE收到不匹配的ue-Identity直接丢弃来兜底,不会误响应。

6. 验证寻呼是否正常的三种手段:从空口日志到小区级统计

6.1 用测试终端的协议日志确认UE侧接收

最直接的手段是拿一台支持工程模式的5G测试手机或软件狗,在收到被叫时抓取PHY/PDCCH日志。确认三件事:PO时间点是否和脚本计算的PF/PO对上;PDCCH上是否解出P-RNTI加扰的DCI 1_0;PDSCH里Paging消息是否包含自己的5G-S-TMSI。只要这三个对上了,UE侧行为就是完整的。

6.2 用NGAP/F1AP抓包确认网络侧发送

在gNB和AMF之间的NGAP接口挂一个分光或抓包,看Paging请求里的TAI list和5G-S-TMSI。再在gNB的MAC日志里看这个Paging是否被触发调度。我常用的判断方法是:如果NGAP有Paging但MAC无调度,问题在gNB的寻呼参数;如果MAC有调度但UE端没解出,问题在空口波束或PO计算。

6.3 用小区级统计看寻呼成功率

商用网管里一般有寻呼成功率、寻呼响应时延等计数器。可以按小区维度看长期漏呼分布。如果某个小区成功率明显低于周边,优先怀疑该小区TAC配置或波束覆盖问题。如果全网都低,核心网侧下发的TA list疑似过大,导致寻呼广播在错误区域扩散,拉低了响应率。

至于寻呼参数调得值不值得做,我的看法很明确:寻呼是所有被叫业务和系统消息更新的底座,参数调一次可以管很多年,但前提是先能复现一次完整寻呼。我在实训室复现时被PF_offset符号坑过整整一个下午——手工算出来SFN是对的,但日志里提示PO前移一帧,最后才知道核心网下发的Paging里的DRX值被基站覆盖了。那之后养成的习惯是:所有寻呼参数改完,先看SIB1里实际广播出来的值,再跑一遍计算脚本,而不是相信配置文件里的注释。这个习惯后来帮团队排查过多次漏呼,希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/27 1:51:28

北京天通苑网站建设新手入门:3套方案实测避坑

北京天通苑网站建设新手入门:3套方案实测避坑 网站做好了没人访问,这是北京天通苑很多创业团队负责人最头疼的事。刚花几万块把站弄上线,百度搜不到,微信打不开,客户来了也留不住。别怪自己运气差,大概率是技术选型踩了坑,SEO底层架构没搭对。 针对 北京天通苑网站建设 ,特别是面向 新手入门…

作者头像 李华
网站建设 2026/9/27 1:51:26

事业单位网站备案流程全解:怎么选对服务商才不踩坑

事业单位网站备案流程全解:怎么选对服务商才不踩坑 你的网站刚上线不久,突然收到用户反馈打不开,或者页面弹出一堆奇怪的广告链接?这时候别慌,先别急着重启服务器,很多事业单位和国企的新手站长都遇到过这种情况:网站被黑挂马,后台日志里全是陌生的IP在疯狂请求,甚至首页被替换成了赌博页面。面对这种“网站被黑…

作者头像 李华
网站建设 2026/9/27 1:51:00

搞懂做网站后台指的那5个核心坑,避开性能优化陷阱

搞懂做网站后台指的那5个核心坑,避开性能优化陷阱 找建站公司最怕被坑高价,尤其是当对方信誓旦旦说“后台很强大”时,你往往看不懂门道,只能掏钱。其实,“做网站后台指的那”些东西,核心就两点:能不能让你自己改内容,以及服务器跑得快不快。很多低价建站公司忽悠你说后台功能多,结果上线后页面打开要半分钟,用户…

作者头像 李华
网站建设 2026/9/27 1:50:56

嵌入式C++实战:STM32下用类封装GPIO与定时器点亮LED

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

作者头像 李华
网站建设 2026/9/27 1:50:45

3步找回wordpress管理面板忘记密码,附5大费用与注意事项

3步找回wordpress管理面板忘记密码,附5大费用与注意事项 刚接手一个北京客户的旧站,后台登录页死活进不去。客户急得直拍大腿,说域名和服务器都在自己手里,但就是登不进WordPress后台,数据全在里面,急得想把服务器直接拔了重做。…

作者头像 李华
网站建设 2026/9/27 1:50:18

FPGA入门到实战:Verilog与Vivado开发全流程避坑指南

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

作者头像 李华