首页/新闻资讯/正文详情

操作系统核心边界解析:线程、分页、系统调用与并发

发布时间:2026/11/6 7:09:26 来源:云帆数科 栏目:资讯中心
操作系统核心边界解析:线程、分页、系统调用与并发
简介西安电子科技大学《操作系统》期末试题及答案解析面向准备操作系统课程考试的高校学生、考研复试复习者及需要检验知识掌握程度的自学者帮助梳理闭卷考试中的选择题、填空题等常见题型与核心考点。资源包共1个PDF文件大小约118KB试卷标注考试时间120分钟、四大题、满分100分选择题与填空题各20分题干为中英混合表述内容覆盖进程与线程、CPU调度、分页与虚拟内存、局部性原理、i-node、磁盘访问时间、Linux权限与文件元数据、死锁、信号量及DMA等主题并配有答案解析便于对照题目定位概念盲区、理解出题方式与答题思路。现有207人学习适合在考前进行限时模拟、按章节查漏补缺也可作为课堂复习后的自测材料快速建立操作系统课程的知识框架与题型意识。1. 边界比定义更重要操作系统这门课最反直觉的一点是几乎所有考点都不在定义上而在边界上。进程和线程的边界、用户态和内核态的边界、虚拟内存里虚拟地址和物理地址的边界、调度器在哪些时刻才真正插手——只要边界画错选择题看着每项都像对的大题写满了也未必拿分。这份某高校《操作系统》期末试题资料的价值不在答案字母而在后面那几行解释为什么线程既能实现在用户空间也能实现在内核、为什么 31 位地址拆出来正好是 1K 页、为什么 DMA 能让 CPU 完全袖手旁观。把这些线串成一张从硬件到系统调用的地图「操作系统原理」就不再是需要背的东西。2. 进程与线程内核视角下到底共享了什么2.1 用户级线程与内核级线程差在一次陷入题目里那句「Thread can be implemented in ___」问的其实是线程表由谁维护。答案之所以是「用户空间或内核都可以」是因为线程这个概念本身不绑定实现位置绑定的是「谁来做调度决策」。放到用户空间实现运行时库自己维护一张线程表切换线程就是保存几个 callee-saved 寄存器、换一下栈指针连特权指令都不需要执行/* 用户级线程的上下文切换全程在用户态完成 */ typedef struct uctx { void *sp; /* 保存的栈指针指向该线程自己的栈 */ void (*entry)(void *); void *arg; } uctx_t; extern void ctx_switch(void **old_sp, void *new_sp); /* 汇编实现压 callee-saved 寄存器后换栈 */ void thread_yield(uctx_t *from, uctx_t *to) { ctx_switch(from-sp, to-sp); /* 没有 syscall没有 CR3 切换没有内核栈 */ }这段代码里最关键的信息是「没有」什么没有 CR3 写入页表基址不变没有内核栈切换没有 trapframe 保存。所以用户级线程切换便宜到几十纳秒同一进程内的线程切换几乎免费。代价同样明确内核根本不知道这些线程存在它眼里的这个进程是单线程的。于是任何一个线程执行阻塞式read内核只会把整个进程挂起同进程里其他就绪线程全被连坐。多核也救不了——内核只看到一个可调度实体同一进程的用户级线程永远只能挤在一个核上。反过来在内核里实现线程每个线程在调度器里有一条记录、一个内核栈和一个 TCB创建要走系统调用、切换要陷入内核但阻塞互不影响、能真正并行。混合模型则是把 n 个用户级线程复用到 m 个内核级线程上各取一半好处。2.2 一句话回答「线程不共享什么」「进程内所有线程共享哪些资源」这道题背答案很容易但真正记住要看数据结构长什么样。常见的实现思路是把「属于地址空间的东西」放进进程结构把「属于执行流的东西」放进线程结构struct thread { struct trapframe *tf; /* 私有rax..r15、rip、rsp、rflags 的现场 */ void *kstack; /* 私有内核栈系统调用时用 */ void *ustack; /* 私有用户栈线程之间不能共用 */ int tid; struct proc *proc; /* 指回所属进程 */ struct thread *next; /* 挂调度队列用 */ }; struct proc { pagetable_t *pgdir; /* 共享页表切线程时完全不换 */ struct file *ofile[NOFILE]; /* 共享打开文件表 */ struct inode *cwd; /* 共享当前工作目录 */ struct sigaction sigact[NSIG];/* 共享信号处理方式 */ int pid; };判断依据非常机械pgdir在proc里所以两个线程访问同一个虚拟地址会落到同一个物理页堆上的全局变量天然互相可见ofile在proc里所以一个线程close掉的文件另一个线程再读就是EBADF。而寄存器现场必须在thread里因为 PC 决定下一条执行哪条指令、SP 决定往哪个栈压数据这两样一旦共享两个线程就会跑同一条指令流、压同一个栈直接崩。顺着这个结构看就明白题目问的是哪个字段的归属地址空间、打开文件、记账信息都在进程结构里只有寄存器是线程私有的。2.3 三级调度各管哪一段短期调度short-term schedule就是 CPU 调度从就绪队列里挑一个进程上处理器频率最高毫秒甚至微秒级一次它的决策必须极快所以只看优先级和时间片不做任何重量级判断。这也是为什么题目把 short term schedule 对应到 CPU scheduling——它决定的是「此刻谁用 CPU」不是「谁被创建」也不是「谁被换出」。长期调度long-term schedule作业调度决定哪个作业被选中、创建成进程进入内存它控制的是多道程序度。批处理系统里这个度调得太高每个进程分到的时间片就碎交互体验崩调得太低CPU 又闲。中期调度medium-term schedule内存调度管的是换出换入内存紧张时挑一个进程挂起到磁盘等内存宽裕再换回来对应进程状态图里那个「挂起」态。三级调度的分工本质是三种不同时间尺度的资源CPU 是微秒级的内存是毫秒到秒级的作业吞吐是分钟级的。用同一套逻辑管三种尺度必然有一头顾不好。2.4 系统调用到底走了哪几步Part 3 第一题问系统调用标准答法是「内核提供给用户的一组接口」但真正体现理解的是那条路径。用户态把系统调用号放进rax参数依次放rdi、rsi、rdx、r10、r8、r9然后执行syscall指令CPU 依据 MSR 里预先登记的入口地址跳到内核同时完成栈切换和rip/rflags保存内核拿rax去查分派表。/* 系统调用分派表用户态那条 syscall 指令最终落到这里 */ static long (*sys_call_table[])(struct trapframe *) { [SYS_read] sys_read, [SYS_write] sys_write, [SYS_fork] sys_fork, [SYS_open] sys_open, }; void syscall_entry(struct trapframe *tf) { if (tf-rax NR_SYSCALLS || !sys_call_table[tf-rax]) tf-rax -ENOSYS; else tf-rax sys_call_table[tf-rax](tf); /* 返回值回填 rax由 sysret 带回用户态 */ }tf-rax在这里身兼两职进来时是调用号出去时是返回值。这也是「一次系统调用两次特权级切换」说法的由来——一次陷入、一次返回中间那次查表分派才是真正的业务。需要额外注意的是参数里的用户态指针在进入内核后必须校验并拷贝copyin否则用户传一个内核地址进来内核就替它读了不该读的内存。3. 分页与地址翻译31 位地址是怎么被切开的3.1 位宽是唯一的信息源题目给了一张图31 到 10 位是页号9 到 0 位是页内偏移。它没告诉你页大小也没告诉你页数全靠位宽反推。偏移占 10 位意味着页内能寻址 2^10 1024 字节页号占 22 位意味着最多 2^22 4M 个页。选 B。def decode(addr_bits, offset_bits): page_size 1 offset_bits # 页大小 2^偏移位数 pages 1 (addr_bits - offset_bits) # 页数 2^页号位数 return page_size, pages print(decode(32, 10)) # (1024, 4194304) - 1K 页、4M 页 print(decode(32, 11)) # (2048, 2097152) - 2K 页、2M 页两个参数的含义必须说清addr_bits是虚拟地址总位宽offset_bits是低位偏移占的位数。分页系统里页大小永远是 2 的幂正是为了让「除法和取余」退化成「移位和掩码」——硬件不做除法右移 10 位得页号与0x3FF得偏移。很多人在这里翻车是因为看到「2M pages at most」就以为是页大小的暗示其实 4M 页对应的是 32 位地址减 10 位偏移加减法算错一位四个选项全像对的。3.2 TLB 打在地址翻译路径上的补丁分页的代价藏得很深页表本身在内存里一次虚拟地址翻译要先读一次页表项再读一次目标数据。也就是说一条普通的mov在开了分页之后变成两次访存性能直接腰斩。TLBTranslation Lookaside Buffer就是针对这条路径的缓存它放在 MMU 里是一组小容量的相联存储器表项结构大致是struct tlb_entry { unsigned long vpn : 40; /* 虚页号命中比较时当 tag 用 */ unsigned long ppn : 24; /* 物理页框号命中后直接拼出物理地址 */ unsigned long asid : 16; /* 地址空间标识避免切换进程就全表刷掉 */ unsigned int valid : 1; unsigned int dirty : 1; /* 该页被写过换出时要回写磁盘 */ unsigned int prot : 3; /* 读 / 写 / 执行权限 */ };命中时MMU 用虚页号做一次并行比较直接拿到物理页框号省掉那次页表访存未命中才走页表遍历并把结果回填。asid字段是后加的补丁早期 x86 没有 PCID一写 CR3 切页表整个 TLB 就得刷掉切换进程后前几百条指令全是冷启动的缺页式延迟。加上地址空间标识之后不同进程的翻译结果可以共存切换只换一个标识。为什么几十条表项就能覆盖工作集因为页访问的局部性比指令级局部性更粗一个程序在一个时间窗口里只会碰少数几个页TLB 装得下。这也是虚拟内存和 TLB 能互相成全的原因。3.3 局部性原理不是「顺序执行」选择题里「程序按顺序执行代码」是个漂亮的干扰项。局部性讲的是两件事时间局部性——刚被访问的指令或数据很可能马上再被访问循环体、计数器变量空间局部性——访问了地址 xx 附近的地址很快也会被访问数组遍历、顺序取指。它描述的是一种「访问概率分布不均」不是「执行流线性」。虚拟内存整套机制建立在这个前提上进程可以声明 8 页的地址空间物理内存只给 4 个帧靠换入换出把当前工作集留在内存里。如果程序真的均匀随机访问整个地址空间命中率会低到每次访问都缺页系统直接颠簸到不可用。Denning 提出的工作集模型就是把这件事量化给一个时间窗口 τ窗口内被访问的页集合就是工作集操作系统只要保证工作集在内存里缺页率就压得住。3.4 页故障与地址范围把除法做对就行题目给的条件是页大小 2K、物理内存 4 个帧、进程需要 8 页页表里第 0、1、3、5 页无效。虚拟页 i 覆盖的地址范围就是[i*2048, (i1)*2048 - 1]虚页有效位物理帧虚拟地址范围0No—0 – 20471No—2048 – 40952Yes14096 – 61433No—6144 – 81914Yes38192 – 102395No—10240 – 122876Yes012288 – 143357Yes214336 – 16383翻译一个具体地址只做两步整除得页号取余得偏移。8500 除以 2048 得 4 余 308页 4 有效且映射到帧 3物理地址就是 3×2048308 64521400 落在页 0 的区间页 0 无效直接缺页5000 是页 2 偏移 904帧 1物理地址 29522100 是页 1 偏移 52缺页。PAGE_SIZE 2048 page_table {0: None, 1: None, 2: 1, 3: None, 4: 3, 5: None, 6: 0, 7: 2} def translate(va): vpn, off divmod(va, PAGE_SIZE) # 硬件等价于 va 11 与 va 0x7FF frame page_table[vpn] if frame is None: return fpage fault vpn{vpn} return fpa{frame * PAGE_SIZE off} print(translate(8500), translate(1400), translate(5000), translate(2100))真正的缺页处理比这个函数长得多陷入内核、查页表确认确实无效、挑一个牺牲帧、脏页回写、从磁盘读入目标页、更新页表、把有效位置 1、重新执行那条触发缺页的指令。注意最后一步是「重新执行」而不是「继续执行下一条」——因为陷入时那条指令的副作用还没发生必须原样重放这也是缺页处理必须可重入的原因。4. 并发与死锁P/V、管程和安全序列的实现细节4.1 信号量的原子性靠哪条指令信号量 S 是个整数但只能被 P 和 V 两个原语碰。P 的语义是「减一如果减完小于零就阻塞自己」V 是「加一如果加完小于等于零就唤醒一个等待者」。注意那个「小于零」而不是「小于等于零」S 为负时绝对值恰好等于等待队列长度这个设计让一个整数同时承载了「剩余资源数」和「等待者数量」两个信息。void P(sem_t *s) { disable_interrupts(); /* 单核上用关中断拿到最便宜的原子性 */ s-value--; if (s-value 0) { enqueue(s-waitq, current); /* 挂到等待队列状态置为阻塞 */ enable_interrupts(); schedule(); /* 让出 CPU被 V 唤醒后从这里返回 */ } else { enable_interrupts(); } } void V(sem_t *s) { disable_interrupts(); s-value; if (s-value 0) { wakeup(dequeue(s-waitq)); /* 只唤醒不立刻切换过去 */ } enable_interrupts(); }单核上关中断够了多核上不够——disable_interrupts只屏蔽当前核心别的核心照样能同时执行 P。所以现代实现靠硬件原子指令x86 的lock xadd、lock cmpxchgARM 的ldrex/strex或 LSE 的ldadd用一条不可被打断的读改写完成「减一并判断」。这也是为什么信号量这类原语必须和内核配合原子性归硬件阻塞和唤醒归内核两者缺一不可。4.2 管程把互斥藏进了语法管程是一组过程、共享变量和初始化代码打包成的模块外加一个隐含的互斥锁。进程可以随时调用管程里的过程但同一个管程里任意时刻只能有一个进程在活动这个互斥由编译器或运行时自动加上不需要调用者写一行 P/V。对写并发代码的人来说这是从「自己管锁」到「结构上保证互斥」的跨越。条件变量是管程里的等待队列只有wait和signal两个操作。wait的关键在于「挂起自己」和「释放管程互斥锁」必须是原子的——如果先放锁再挂起中间那段窗口里别的进程可能已经signal过来信号就丢了自己再也醒不过来。所以pthread_cond_wait(cond, mutex)的第二个参数必须传管程锁由运行时保证这两步不可分割。signal的语义有两种流派Hoare 语义是唤醒者立刻把管程让给被唤醒者被唤醒者马上运行Mesa 语义是唤醒者继续跑完被唤醒者只进就绪队列。pthread 和 Java 都是 Mesa 语义所以被唤醒后条件可能又被别人改掉了——这就是必须用while重新检查条件的根本原因它跟「虚假唤醒」是同一件事的两面。4.3 生产者消费者那六个空这段代码用的是单槽缓冲区buffer 0表示空非 0 表示满并存着数据。把六个空还原出来void *producer(void *arg) { for (int i 1; i MAX; i) { pthread_mutex_lock(the_mutex); while (buffer ! 0) /* ① 槽满生产者等 */ pthread_cond_wait(condp, the_mutex); buffer i; pthread_cond_signal(condc); /* ② 有货了通知消费者 */ pthread_mutex_unlock(the_mutex); /* ③ 放锁 */ } return NULL; } void *consumer(void *arg) { for (int i 1; i MAX; i) { pthread_mutex_lock(the_mutex); while (buffer 0) /* ④ 槽空消费者等 */ pthread_cond_wait(condc, the_mutex);/* ⑤ 等在生产者的对立条件上 */ int res buffer; buffer 0; pthread_cond_signal(condp); /* 通知生产者可以填了 */ pthread_mutex_unlock(the_mutex); /* ⑥ 放锁 */ printf(buffer%d\n, res); } return NULL; }⑤ 填condc而不是condp是最容易错的一处消费者必须等在自己「可能被满足」的那个条件变量上生产者 signal 的也必须是这个变量两边对不上就是永久阻塞。② 和 ③ 的顺序值得多说一句。理论上先 signal 再 unlock 是安全的pthread 允许持锁唤醒但如果先 unlock 再 signal中间会出现一个窗口另一个生产者抢进来把 buffer 填满然后才 signal被唤醒的消费者醒来发现条件已经不成立。while能兜住这种情况代价是一次空转。稳妥写法是持锁 signal、解锁收尾。4.4 银行家算法安全性检查就是一次模拟Need Max - Allocation这一步只是减法真正的逻辑在安全性检查假设所有进程此刻都停住手里资源都不还只有 Available 可用然后找一个 Need 不超过 Available 的进程让它跑完把它手里的 Allocation 全部归还Work 变大再找下一个。找不到可满足的进程而还有进程没完成就判定不安全。int safe p a hrefhttps://download.csdn.net/download/m0_51765271/86698951 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p

相关推荐

发票核验API对接实战:验真、状态复检与幂等限流
发票核验API对接实战:验真、状态复检与幂等限流

发票核验这件事,我前后做过三轮:最早是给一家做企业报销的团队写对接层,后来是给一个电商结算系统补验票环节,最近一次是把验票能力抽成独立服务给多条业务线复用。三轮下来最大的感受是——发票核验API看着只是一个"传发票号… · 2026/11/6 7:09:26

LeetCode数组加1问题:反转数组与进位处理技巧
LeetCode数组加1问题:反转数组与进位处理技巧

1. 问题背景与核心挑战这道LeetCode题目看似简单,却隐藏着几个关键的技术难点。给定一个表示非负整数的数组,其中每个元素对应整数的一位数字(按高位到低位顺序排列),我们需要实现对这个整数加1的操作。比如输入[1,2,3… · 2026/11/6 7:09:26

图论入门:从快递调度到社交推荐的关系建模
图论入门:从快递调度到社交推荐的关系建模

简介:本资源是哈尔滨工业大学《集合论与图论(下)》慕课MOOC课程的配套习题答案整理,面向计算机、数学及相关专业本科生及自学者,助力系统掌握图论核心理论与解题方法。内容覆盖图的基本类型(简单图、有向/无… · 2026/11/6 7:08:26

Windows XP/2K/NT密码重置原理与chntpw实操指南
Windows XP/2K/NT密码重置原理与chntpw实操指南

1. 这不是“破解”,而是系统级凭证恢复的底层逻辑 “2K/NT/XP中找回丢失的管理员密码”——这个标题乍看像老电影里的黑客桥段,实则指向一个早已被现代安全体系淘汰、却仍在特定工业控制、老旧医疗设备、嵌入式终端中真实存在的运维痛点。我接触过某高校… · 2026/11/6 8:12:36

多门店小程序平台:零售数字化转型的核心架构与实践
多门店小程序平台:零售数字化转型的核心架构与实践

1. 多门店小程序平台:零售企业数字化转型的核心引擎 零售行业正经历着前所未有的数字化变革。作为一名深耕零售科技领域多年的从业者,我亲眼见证了从单店独立运营到全渠道一体化管理的演进过程。多门店小程序平台的出现,彻底改变了传统连锁零… · 2026/11/6 8:12:36

CAD多重插入块无法分解?AutoLISP改组码恢复图纸
CAD多重插入块无法分解?AutoLISP改组码恢复图纸

简介:面向CAD制图人员与图纸接收方的实用操作资料,针对多重插入引用导致的图纸无法编辑、难以分解问题,整理加密图纸的炸开与还原思路。包内仅含1个doc文档,压缩包约18KB,篇幅紧凑,适合按步骤查阅和随查随用… · 2026/11/6 8:12:36

软件建模中质量需求与组织愿景的区分与实践
软件建模中质量需求与组织愿景的区分与实践

1. 从质量需求到组织愿景:软件建模中的常见误区解析在软件建模和需求分析过程中,我见过太多团队陷入一个典型误区——将系统质量需求直接等同于组织愿景。这种现象在敏捷开发普及的今天尤为常见,许多产品经理和技术人员习惯于从系统功能和技术… · 2026/11/6 8:12:36

网页木马检测与遗留脚本宿主加固:静态动态结合实践
网页木马检测与遗留脚本宿主加固:静态动态结合实践

这个标题我没办法照着写成博文。 网马 (网页木马)的核心用途是通过网页加载恶意载荷、下载执行程序或窃取凭据,写一篇"用 VB6.0 做出来"的教程,等于提供可直接复用的攻击工具制作方法,这类内容我不能产出&… · 2026/11/6 8:12:36

Baklib构建高效用户手册体系的核心功能与实践
Baklib构建高效用户手册体系的核心功能与实践

1. 产品文档的价值与挑战 在数字化产品大行其道的今天,用户手册早已不再是传统印象中那本被束之高阁的纸质说明书。作为连接产品与用户的核心纽带,一份优秀的在线文档能够显著降低用户的学习成本,提升产品体验,甚至直接影响用户的… · 2026/11/6 8:11:36

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

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

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

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

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

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

三菱PLC数据采集到数据库的时序设计:Python+MC协议实战
三菱PLC数据采集到数据库的时序设计:Python+MC协议实战

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

ASIC主模式RTL设计:状态机与三态驱动的流片级实现
ASIC主模式RTL设计:状态机与三态驱动的流片级实现

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

STM32嵌入式C++开发:GDB调试与Renode仿真实战指南
STM32嵌入式C++开发:GDB调试与Renode仿真实战指南

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

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

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

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

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

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

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

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码