- 博客(148)
- 收藏
- 关注
原创 leecode unordered_map与unordered_set常见接口函数
和 的 核心设计思想相似(均基于哈希表实现,提供 O(1) 平均时间复杂度的查找),但它们的 API 并不完全相同,主要区别源于二者用途不同:• 存储键值对(),需通过键访问值。• 仅存储键(),用于快速判断元素是否存在。以下 API 在两者中功能一致:• 提供 和 直接访问值: 没有这些接口,因为它不存储值。• 的迭代器指向 ,需用 和 访问键和值:• 的迭代器直接指向 :(3)初始化与插入• 插入键值对需用 :• 只需插入键:3. 何时选择?• 用 :需要建
2025-04-08 19:21:29
90
原创 静态类型vs动态类型(int i c++和 i python的区别,一个编译时确定,一个运行时确定) 静态绑定vs动态绑定(多态的两种实现方式)
项目静态类型动态类型类型绑定时机编译时运行时类型安全编译器保证程序员保证性能高(可优化)较低灵活性较低高出错时机编译期运行期项目静态绑定动态绑定函数决定时机编译期运行期是否支持多态否✅ 是性能快稍慢(需查 vtable)是否用virtual❌ 否✅ 必须概念是谁的类型?决定时机静态类型变量的类型编译期固定动态类型变量的类型运行期决定静态绑定调用哪个函数编译期决定动态绑定调用哪个函数运行期决定(虚函数)
2025-04-07 15:37:05
443
原创 什么时候必须使用初始化列表?const,&,基类构造需要参数,多继承中虚基类构造等
只要你遇到 const、引用、没有默认构造函数的成员、需要给基类传参 —— 统统必须用初始化列表!
2025-04-07 15:16:16
233
原创 C++ 异常处理的三大核心关键词:`try`、`catch`、`throw`
关键词作用throw抛出异常(出错时执行)try标记一段可能抛异常的代码块catch捕获并处理异常try 是防火墙,throw 是爆炸点,catch 是灭火器。给你讲讲 C++11/17 中异常的优化机制(noexcept替代异常)写一个小型异常封装库(适合实际工程)或者讲讲异常在底层是怎么实现的(栈展开、typeinfo、RTTI)你感兴趣哪个方向?😎。
2025-04-07 14:02:24
244
原创 简单工厂模式
情况建议使用你不想暴露具体类的创建细节✅ 使用工厂类的实例化逻辑比较复杂✅ 使用工厂产品种类会扩展或变化✅ 工厂方法优于简单工厂需要创建产品族(多个相关产品)✅ 抽象工厂模式。
2025-04-07 12:33:01
132
原创 悲观锁和乐观锁
对比项悲观锁乐观锁思维模式默认会冲突默认不会冲突是否加锁是(阻塞)否(非阻塞)实现方式mutexrwlock等CAS, 版本号,时间戳性能特点冲突多时稳定冲突少时快,冲突多性能下降成功概率一般都能成功可能失败需重试使用场景高冲突区,如数据库写操作高并发低冲突,如计数器、自旋队列。
2025-04-02 09:44:33
317
原创 printf的实现,va_list读取参数,vsprintf进行将%s,%d这些参数的值转化为字符串并一起输出到缓存区
【代码】printf的实现,va_list读取参数,vsprintf进行将%s,%d这些参数的值转化为字符串并一起输出到缓存区。
2025-04-01 17:58:42
66
原创 在 C++ 中,“自动化内存管理” ,类(RALL)+智能指针+STL
用类封装FILE*socketmutexGPU buffer等资源析构里做清理,自动释放技术功能RAII作用域控制资源释放智能指针管理堆内存,无需手动 deleteSTL 容器自动管理内部数据结构的内存如果你告诉我你在哪个场景想要“自动释放”内存(比如网络、图像处理、游戏对象),我可以给你写个适配的封装类或使用策略,要不要?
2025-04-01 10:33:13
152
原创 对象的内存:对象大小 = 成员变量大小之和 + padding
每个成员变量的地址必须是它对齐大小的整数倍整个结构体的大小必须是最大对齐单位的整数倍编译器可能在结构体末尾也加 padding 以满足数组连续性概念说明成员变量内存之和是理论最小值,但通常不等于实际大小实际对象大小= 成员内存之和 + 对齐 padding查看偏移用什么?offsetof()查看字段偏移怎么调小对象大小?调整成员顺序、pack 指令、位域/union 等。
2025-04-01 10:31:24
372
原创 一个多线程程序,优化程序性能,考虑些什么
优化目标方法减少调度开销线程池、控制线程数避免资源竞争拆分数据、使用原子操作减少锁带宽原子变量、读写锁、无锁数据结构提高缓存效率避免伪共享、顺序访问平衡负载动态分配、工作窃取易于调试与监控工具分析、打印线程 ID、日志安全。
2025-04-01 10:13:57
410
原创 堆,堆排序原理
堆是一棵特殊的完全二叉树(Complete Binary Tree)大顶堆(Max-Heap):父节点 ≥ 子节点小顶堆(Min-Heap):父节点 ≤ 子节点💡 完全二叉树 → 除最后一层外都是满的,最后一层从左到右填满堆是一种满足“父节点 ≥ 子节点(或 ≤)”的完全二叉树,通常用数组实现,支持高效地插入元素和获取最大/最小值,时间复杂度是 O(log n)。堆在 C++ 中广泛应用于优先队列、TopK 问题、任务调度等场景,堆排序基于最大堆实现,整体时间复杂度为 O(n log n)。
2025-04-01 09:54:50
211
原创 系统调用execve()
execve会用一个新的程序替换当前进程的用户空间。内核会创建新的页表、加载 ELF 文件、重建用户栈和参数区,并在系统调用返回时,从新程序的入口地址开始运行,而不是返回到原来的函数调用。整个过程是“旧壳换新魂”,PID 不变,但一切都变了。
2025-04-01 09:23:49
200
原创 所有排序算法,稳定,实现,时间复杂度,示例
稳定排序你要排序的对象推荐算法小数据、教学冒泡 / 插入大数据、稳定归并排序速度优先快速排序不要求稳定、原地堆排序整数范围小计数 / 桶排序整数位数不大基数排序当然可以!这里我给你列出每个常用排序算法的示例排序前数组排序后数组排序过程简单说明(便于理解)原始数组:{5, 2, 9, 1, 5, 6}
2025-03-31 00:17:49
648
原创 想看一个进程的 堆栈(stack)信息
命令作用bt最详细,调试视角快速查看所有线程堆栈查看主线程内核栈看是否卡在系统调用查看栈地址空间查看指定线程内核栈如果你能告诉我你是要分析什么样的程序(比如阻塞了?挂死了?死锁了?),我可以一步一步带你排查,还可以帮你写个一键堆栈收集脚本。要不要试试?
2025-03-30 23:43:05
343
原创 内存碎片:外部碎片(1+2+3,但你需要5,使用分页,分段使得不连续映射),内部碎片(分的页大于实际使用,移动成大块)
内存中存在足够总量的空闲空间,但这些空闲空间是不连续的。导致无法满足较大块内存的分配请求。👉 举例:有 1KB + 2KB + 3KB 的空闲,但你申请 5KB,就失败了。碎片类型解决机制外部碎片分页、伙伴系统、压缩(Compaction)内部碎片小块分配器、Slab/Slub 分配器、内存池程序层碎片合理使用、避免频繁小块分配如果你对某个系统(比如 Linux、FreeRTOS 或 Windows)具体的内存碎片处理机制感兴趣,我可以更深入分析它的实现逻辑,要不要来个实战分析?
2025-03-30 23:20:45
810
原创 core dump(程序崩溃,记录上下文),生成core文件,在编译后,gdb ./my_app core
core dump 是程序崩溃时生成的内存快照,可以用 gdb 等工具分析,帮助定位崩溃原因。可以查看调用栈、变量值和崩溃行,常用于调试空指针、越界、内存访问错误等问题。,它记录了程序崩溃时的全部上下文(栈、寄存器、变量等),用于帮助开发者。启用 core 文件后,结合。这就清楚定位到了崩溃点!
2025-03-30 13:28:19
239
原创 提高 C++ 编译速度的 8 大常用方法(头文件少include别的,多声明,使用内联函数,并行编译,使用一个共同的头文件)
使用前向声明、预编译头、ccache 和并行编译等手段来优化编译速度。同时会避免在头文件中使用复杂模板逻辑,提升整体编译效率。C++ 编译慢的核心原因在于头文件滥用和模板展开。我通常通过减少不必要的。比如把所有常用 STL 头、系统头统一写进一个。宏是文本替换,预处理阶段会导致很多无效编译。C++ 编译慢最主要原因:头文件爆炸!,然后生成 PCH。
2025-03-30 13:25:35
242
原创 指针和引用的区别
对比项指针(Pointer)引用(Reference)是否可为空✅ 可以为nullptr❌ 引用必须绑定有效对象是否能修改指向✅ 可以重新指向别的对象❌ 引用一旦绑定不可更改语法使用使用解引用,取地址像别名一样直接用,无需是否可嵌套✅ 指针的指针(T**❌ 引用的引用不存在是否可空判断✅ 可以if (ptr)❌ 引用本身不能判空本质保存地址的变量(内存中一块值)变量的别名(本质是编译器处理)
2025-03-30 13:16:57
360
原创 面试:用过c++测试用例/单元测试嘛?答:gtest
我平时写完关键函数会加一些 gtest 测试,比如边界值、负数输入、异常处理等,一方面保证正确性,另一方面也方便 CI 自动验证。,你想知道它是否在各种输入下返回正确值,你可以用 gtest 写多个“测试用例”。就是你要测试的函数。你写算法、类、模块,最后都要测试一下它对不对。引入 gtest 框架的头文件,里面定义了测试宏、断言等。如果不是,测试就会失败,控制台输出错误信息。里的测试用例,告诉你是否通过。函数处理正数输入的情况。
2025-03-30 13:12:25
344
原创 进程fork和多线程的本质区别,一个是不同物理页,同虚拟地址,一个是同虚拟,物理地址,但不同栈,esp,ebp
线程属于同一进程,多个线程共享相同的虚拟地址空间(代码段、数据段、堆等),只有栈是独立的,这样线程之间可以轻松共享数据,适合并发模型。
2025-03-28 15:57:58
307
原创 系统调用 fork()
用户态:| 用户程序调用 fork() || ↓ || 封装后执行 int 0x80 触发中断 |内核态:| 中断处理器进入 sys_call || ↓ || 找到 syscall table 中 fork 对应函数 sys_fork() || ↓ || 执行:复制PCB、内存、文件、寄存器等 || ↓ || 设置返回值 eax=0 / eax=child_pid || ↓ || 用 iret 恢复到用户态 |用户态:| 两个进程继续执行,父子分流 |
2025-03-28 15:40:39
214
原创 TSS(Task State Segment)和 GDT(Global Descriptor Table)之间的关系 ,TSS必须独占一段(抽象)
TSS 必须占用一个 GDT 条目;GDT 可以包含多个普通段、TSS 段、LDT 段等;是用于释放 GDT 中的某个条目(比如释放一个 TSS 所在的段)。
2025-03-28 15:20:45
398
原创 lcall(调用门)和0x80(中断门)触发系统调用区别
位置原 lcall 方式新 int 方式用户态调用int $0x80参数传递压栈eax寄存器(可扩展用其他寄存器)内核入口调用门目标函数IDT 中断向量 0x80权限设置GDT 调用门 DPL=3IDT 中断门 DPL=3返回方式lretiret。
2025-03-28 10:38:50
226
原创 调用门之系统调用例如“msleep()”具体实现
你的系统调用流程长这样:函数sys_call()构造参数;执行lcall调用门。查找调用门,进入 GDT 中设置的目标(切换特权级;自动压栈 CS/EIP/EFLAGS;跳到偏移 0,即执行。保存上下文;设置段寄存器;调用真正处理系统调用。这段代码是一个自定义的系统调用包装函数,使用了调用门(call gate)来从用户空间进入内核态执行系统调用。这是调用门的“伪指针”,lcall使用的格式是段选择子(selector)+ 偏移地址(offset)。
2025-03-26 20:51:59
803
原创 调用门:调用门描述符(GDT)获得目标地址,并同时从特权级3中压入用户上下文信息,
在调用门触发跨权限调用(如从用户态进入内核)时,CPU 会自动根据调用门描述符切换栈、压入原用户态返回地址(CS/EIP/SS/ESP),然后直接从门中加载新的 CS:EIP 执行目标函数。整个过程是受限的、自动的、不可绕过的安全跳转。要不要我帮你画一张图,展示调用门触发 → 自动切栈 → 执行目标代码 → lret 回用户态的完整流程图?📊。
2025-03-26 15:16:42
498
原创 中断底层,exception_hand,手动保存寄存器入栈 esp、eflags、cs、eip、error code 最先自动入栈,和函数调用入栈稍有区别
参数含义name异常名,比如dividepage_faultnum异常号,比如014是否有 error code(1 = 有,0 = 没有)[中断进入]↓push 自动保存(eip, cs, eflags, esp, ss...)↓pusha;保存通用寄存器手动填补push $num;异常号push 段寄存器push %esp;传给 C 函数pop %esppop 段寄存器pop 通用寄存器跳过异常号+错误码iret。
2025-03-25 15:21:38
997
原创 simple_switch 操作系统/线程库中实现用户级线程上下文切换
阶段说明🧠 保存当前线程把当前线程的esp保存到from_stack指向的地址中⏩ 切换栈指针把目标线程的栈顶地址赋给esp,让 CPU 栈切换到新线程♻️ 恢复目标线程从新线程栈中弹出保存的寄存器值(pop),恢复上下文🚀 ret 跳转ret相当于,跳到目标线程上次挂起的位置步骤做了什么① 取参数从栈中取fromto(cdecl 调用规则)② 保存现场push四个通用寄存器③ 保存栈顶→ 存 from 栈指针④ 切换栈顶→ 加载 to 的栈指针⑤ 恢复现场pop恢复寄存器。
2025-03-25 14:56:44
881
原创 Mid360 Fast-lio抖动与PX4关闭磁力计后无法解锁
由于隧道矿道环境导致gps磁力计问题,由此我们需要做的是ekf2_mag_type和ekf2_mag_,一个是进行磁力计检查,一个是打开几个磁力计。Fast-lio本来没问题,后面漂了,请检查螺旋桨,电机。
2025-03-25 13:59:53
95
原创 用户态,内核态用链接脚本分离(lds),将进程放进用户态
目的实现方式保留内核低端物理空间按模块组织段.text.data.bss.rodata排除用户进程代码用户任务运行于高虚拟地址支持虚实地址分离加载导出关键地址符号供内核使用这份链接脚本通过和虚拟地址定位,清晰地区分了内核段与用户进程段,并通过AT()实现虚拟地址和加载地址分离,确保用户任务可以在高地址运行,同时保留了内核的低端物理布局。要不要我给你画个完整的“内核加载段 vs 用户任务段(虚拟地址映射)”的对比图?📊 非常适合做注释笔记或面试展示~
2025-03-24 19:53:33
899
原创 选择子是什么(数组的下标量),GDT(段),TSS(上下文,任务状态段,结构中包含各种 CPU 任务切换需要的寄存器状态)
TSS(任务状态段)中的选择子用于指定CPU 执行任务时用的段寄存器值;而 GDT(全局描述符表)保存了这些段选择子的具体描述信息(段基址、段限长、段属性)。TSS结构│ ss0 = 0x10 │ → GDT[2] = 内核数据段│ cs = 0x08 │ → GDT[1] = 内核代码段│ cr3 = 页目录地址 │↓↓↓GDT结构(全局描述符表)│索引│ 段描述符 ││ 1 │ 内核代码段 (0x08) ││ 2 │ 内核数据段 (0x10) ││ 3 │ 用户代码段 │。
2025-03-24 13:29:16
803
原创 进程如何分配内存?是按劳分配!只有触发page fault才分配,只有进行写才分配
一个进程在启动或申请内存时,并不会立即获得对应的物理内存页。操作系统采用**按需分配(lazy allocation)**策略,等进程真正访问某个虚拟页时,才通过页错误机制分配实际的物理页帧。这一机制极大提高了内存利用率,也方便了内存共享与延迟优化。需要的话我可以画一张图:「虚拟地址 → page fault → 物理页分配 → 页表更新」的流程图,或者对比“分配 vs 实际使用”的图示。要来一张吗?📊。
2025-03-23 23:52:53
371
原创 虚拟内存管理具体有哪些?
虚拟内存管理是操作系统提供的内存抽象,既提升了内存使用效率,又隔离了进程地址空间。它通过页表完成地址映射,通过置换算法管理内存回收,并利用权限位进行访问控制,是现代系统运行的基础保障之一。如果你想,我可以画出“地址转换流程图”或者“多级页表示意图”,让你面试/复习/笔记更直观!📊。
2025-03-23 23:51:38
222
原创 页面置换算法
算法性能实用性复杂度FIFO中下✅ 简单,入门常考低LRU高❌ 难实现硬件支持中高OPT最优❌ 不可实现,仅分析用高CLOCK接近 LRU✅ 常用于实际系统中LFU受限❌ 不常用高Random视场景✅ 非常轻量极低页面置换算法用于决定内存不够时淘汰哪一页。常见算法包括 FIFO、LRU、CLOCK、OPT 等。其中 CLOCK 是实际系统中常用的,因为它在效率和实现复杂度之间取得了良好平衡。
2025-03-23 23:34:08
748
原创 OOM,用score(占用内存高,不影响内核,用户不怎么保护)
OOM = 内存耗尽,系统无法为进程/内核分配更多内存,就触发了 OOM。当系统可用内存不足,且尝试分配内存失败(例如malloc()或fork()系统会触发一个机制 →OOM Killer(“内存杀手”)启动它会强制杀死某些进程来释放内存,避免整个系统崩溃OOM 是系统内存耗尽时由内核触发的一种保护机制,Linux 下通过 OOM Killer 杀掉内存占用过大的进程来回收资源,从而防止系统完全崩溃。它底层依赖内核内存分配失败检测、cgroup 限制、OOM 分数算法等机制。
2025-03-23 23:21:08
885
原创 原子操作底层真正实现 lock+锁总线/锁缓存(MESI)修改为M,别的CPU修改为I
MESI 是一种CPU 缓存一致性协议状态含义缓存中数据被改了,还没写回内存独占状态,数据和内存一致S (Shared)多个 CPU 共享,数据和内存一致缓存无效,需重新加载在老式 CPU 中,原子性是通过锁总线来实现的,代价高;而现代多核处理器通过缓存一致性协议(MESI)实现原子操作,锁的是缓存行,不仅高效,还能避免总线阻塞,是原子指令实现的主流方式。太棒了!你真的在深入理解系统底层了 🔥下面我就来给你深入具体讲讲 MESI 协议。
2025-03-23 23:12:35
924
原创 信号量【N-1】+互斥锁【N】,N个哲学家吃饭,避免死锁(所有人拿起左叉,信号量N-1,就可以解决有一个人不会拿起左叉),所有的叉子都是互斥锁
哲学家进餐问题考察死锁的 4 个条件:互斥、占有等待、不剥夺、循环等待。打破循环等待或占有等待条件。信号量限制进入人数 + 互斥锁控制资源线程std::mutex互斥锁(叉子)(C++20 支持)限制最大吃饭人数,避免死锁。
2025-03-23 19:23:07
357
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人