- 博客(83)
- 收藏
- 关注
原创 C++20(jthread)
本章参考和 MSVC 示例。类表示。它与具有相同的通用行为,除了jthread在析构时会自动加入,并且可以在某些情况下被取消/停止。与传统的 std::thread 的不同。
2026-03-01 13:34:44
986
原创 C++20(范围)
标准库的容器,可是拥有和非拥有数据(stringview....)自定的对象内实现了 begin/end原生数组生成器生成的序列简单说:任何包含 begin/end 支持范围 for 的对象都是一个范围。
2026-02-09 16:38:53
945
原创 C++20(三路比较运算符)
>= <= 这些比较符,而 C++ 20 提供了 <=> 三路比较运算符,又称 宇宙飞船运算符,他同时兼具了上述的所有比较符的逻辑,统一了接口,不必写冗余的代码。如果自己写了 <=> 或者强制编译器生成默认的 <=> ,该类就可以使用传统的比较方式了,但 <=> 生成默认的要求所有的成员变量都支持 <=>,并按声明顺序进行比较,既可写成类成员函数也可写成非成员函数。weak_ordering:弱序比较,不严格的比较,如按自己的规则进行比较,string 忽略大小写比较等。unordered:不可比较。
2026-02-05 11:03:39
595
原创 C++20(协程)
先看看线程:线程是 CPU 调度的基本单位,属于内核,保存上下文需要切换到内核态,成本较高,线程是抢占式的,任何一个线程都可能会被随时切换,线程阻塞会一直等直到唤醒。协程:粒度比线程更细,协程属于用户态,上下文的保存在用户态完成,成本较低不需要状态转换,协程是协作式的,协程的挂起由另一个协程恢复,协程阻塞不会影响其他的协程,多个协程可以跑在一个线程上。
2026-02-04 15:36:01
1032
原创 C++20(模块化)
先看一张图片:main1.cc main2.cc main3.cc 包含 fun.h 在预处理的时候都会展开一次,增加编译时间,假设头文件有 m 份,源文件有 n 份,总共编译时间为 m*n ,这也是传统的C++包含头文件带来的效率问题,右边 main1.cc 同时包含 2 个头文件,里面都有同一变量,导致冲突。为了解决上述重复包含头文件和冲突等问题,在 C++ 20 引入了模块化。
2026-01-30 15:06:51
458
原创 C++20(概念和约束)
类模板、函数模板(包括泛型lambda)和其他模板化函数(通常是类模板的成员)可能与一个约束相关联,该约束指定了对模板参数的要求,可用于选择最合适的函数重载和模板特化。这种需求的命名集合称为概念。每个概念都是一个谓词,在编译时计算,并成为模板接口的一部分,用作约束:说人话,就是给模板添加一种约束,让不符合的条件的错误信息更直观看懂,且编码方式统一。类型萃取:模板特化等....这些方式千奇百怪,有了概念之后遵守统一的语法格式更方便编码和调试。
2026-01-28 11:28:05
758
原创 C++17(新特性)
C++为了区分同名变量通过域作用限定符来访问不同的对象,也就是对象被封到命名空间李了,如果命名空间嵌套的太深了,太冗余了,C++ 17 给了一套新的定义方式。// C++ 17 之前{}// C++ 17int a = 10;
2025-10-31 18:45:35
1076
原创 TCP(滑动窗口/拥塞窗口补充)
方案,如果对端已经收到所有的报文或者刚连接什么也没发,也就是发送的报文全部都 ACK 了,则无论多大的包都直接发送,否则如果还有的报文没有 ACK,后续写的数据会积攒起来,如果积攒超过 MSS 则会打包发过去,或者前面发的所有包都 ACK 了也会直接发出去。对端通告窗口大小为 1,2,3.... 这种很小的窗口,发送端就只能发送很小的数据,但基于网络协议栈进行封包,TCP/IP最少报头都是 20 + 20,不携带选项,在携带这么少的数据,也就是 MSS,开销比较大。1. ACK丢包:发送 1,2,3。
2025-10-23 23:40:12
1059
原创 C++11新特性补充
1. 先来谈谈 volatile:防止编译器对代码进行优化(代码编译后生成指令,编译器可能因为效率的原因对指令进行重新排序,也就是调换位置),并保证他修饰的对象在内存中是可见的,因为 CPU 高速缓存(L1 L2 L3)会通过预加载机制读取一定范围的物理地址,如缓存数据和指令,如果某个对象用 const 修饰,编译器则会认为这个对象不会被改,后续访问就去缓存直接读取,volatile 则就保证了禁止去缓存,转而去内存读取数据。
2025-10-09 22:08:41
799
原创 Redis(基础数据类型/String)
比如:0 ~ 30 秒内只能获取一次,通过 setnx ex,存在则设置,不存在则出错,并设置过期时间。比如:用户身份校验,浏览器传入 cookie/sessionid,Redis 缓存并查询。通过上述的 incr/incrby 等来实现。使用类型必须匹配,且保证不会溢出最大范围。
2025-09-17 21:26:27
456
原创 Redis(哨兵)
前章讲到主从结构下,主节点挂了是需要手动让某个从节点变成主节点,或者修复原来的主节点,人为干涉肯定是不现实的,所以 Redis 引入了哨兵机制。顾名思义:建立在主从结构之上,用来管理主从节点由于自身的的异常情况而避免人为干涉,变成了自动解决。当这些哨兵发现主节点挂了会统计个数,并判断 达没达到 认为主节点真正挂了的阈值,防止误判。然后调整从节点和挂了的主节点和客户端连接的 Redis 服务之间的关系即可。
2025-09-11 18:44:38
354
原创 Redis(集群)
都知道 Redis 是跑在内存中的,既然在内存,能存储的数据是有限的,如果存储的数据太大,就必须引入更多的 Redis 服务来进行分摊数据,所以 Redis 中集群模式就是来解决这类问题的。
2025-09-11 18:44:05
598
原创 Redis(缓存)
1. 生活上的例子比如有一个行李箱和一个手机,每次把手机放到行李箱在拿出来肯定很麻烦,如果放到裤兜里就会方便很多,所以裤兜算作行李箱的一个缓存,不仅仅是裤兜,甚至可以一直拿在手上等其他有存储介质的东西。说白了缓存就是用来提高效率的,比如 内存 作为 CPU 和 外设 的缓存,和接下来要讲的 Redis 作为 MySQL 的缓存,MySQL 和磁盘打交道,磁盘是外设 I/O 效率慢,并且是随机寻址就更慢了,而 Redis 工作在内存,用来缓存 MySQL 的部分数据来分摊压力再合适不过了。
2025-09-09 22:40:05
899
原创 Redis(主从复制)
顾名思义:部署多台 Redis 服务器,并选择一个为主 Redis 服务器,其他为从 Redis 服务器。从节点的数据必须从主节点同步过来从节点只能读,主节点可读可写某个从节点挂了,不影响从其他节点读取数据和写数据主节点挂了,不能写数据,从节点读的数据可能是旧的引入主从模式在读的场景下能提高硬件资源数量和可用性,比如某个服务挂了,依旧能从其他节点读数据,但主节点挂了就不会更新新的数据了。
2025-09-09 20:42:18
1021
原创 图(遍历/最小生成树/单/多源最短路径)
Dijkstra 是很快的算法,但不能针对负权值,这个算法则可以针对,因为采用的是暴力遍历的方式求最短路径,从源点出发,每次选择任意连接的顶点即可,不需要选择从源点到某个点的最小路径,当所有的点选完之后则再次选择所有的边,因为可能带负权,执行 n - 1次,但如果出现了负权回路则会永远选不出最短路径。比如:A 出 B D C,在出 B,此时 B 不能再出 A ,A已经出过要标记一下,也不能再出 C,所以 C入的时候就要标记,所以不管是出过的顶点还是待出的顶点都要标记为不能再次入或者出。
2025-08-02 16:40:12
985
原创 ptmalloc(glibc-2.12.1)源码解析2
bins[NBINS * 2 - 2]:bins 数组,前面说的逻辑结构是 128 个,实际物理是 128 * 2 - 2,前面说过一个 bin 的哨兵节点需要 2 个指针,这里把 2 个指针拆开放到下标里,也就是两两下标为一组。stat_lock_direct/stat_lock_loop/stat_lock_wait:表示线程的直接获取锁的数量/自选等待锁的数量/阻塞等待锁的数量。max_n_mmaps:表示当前进程已经调用 mmap 且取最大值,不能超过 n_mmaps_max。
2025-07-25 17:50:15
873
原创 ptmalloc(glibc-2.12.1)源码解析1
chunk2mem(p):根据已分配的 chunk 的起始地址得到用户实际用的其实地址,本质是跳过前 2 个固定字段的大小,后4个字段在已分配的 chunk 中无效,直接给用户复用,也就是给用户返回的是 fd 字段的起始地址。MINSIZE:进行按最小的 chunk 对齐,比如传入 1 字节,至少对齐到 1+16(最小的 chunk 大小) -> 32(按最小的 chunk 对齐),大于最小的一样按照最小的 chunk 对齐。
2025-07-24 14:31:52
875
原创 Linux(内存分配算法)
最小分配出去的内存块是1页,即4k,所以如果申请小于1页的需求,也至少分配1页出去,可能导致大量的内存浪费用不上,如果分配的是完整的页,即计算后的大小命中某个下标,则直接分配出去,否则如果没能命中,则尝试找更大的连续的页,然后进行切割,剩余挂到对应的下标里,或者一个也没有则尝试合并小的页成一个大的页进行分配然后尝试切割,否则说明该数组不能满足需求则直接分配失败。首先伙伴算法管理的内存块是以页为单位的和物理内存一样,每个下标管理的是以 2^n 个页的内存块,比如:1页,2页,4页,8页.....。
2025-07-21 11:03:39
873
原创 ptmalloc(glibc-2.12.1)整体结构
先说说生活上的例子:比如在学校,每个月又或者每个星期或者每天家里人都会给生活费来维持在学校的生活开销。那么一次要多少,隔多少天要一次?这些情况都会带来不一样的效果和影响。比如一天要一次和一个星期要一次:每天都要一点,每天都会给家里人通信,想必时间久了家里人都会觉得太麻烦了。每隔星期要一点,一次要多点,这样就会家里人就不会觉得太麻烦了。家里人可能很忙,不能每天抽空按时给生活费。
2025-07-20 14:12:52
1036
原创 C++(STL源码刨析/stack/queue/priority_queue)
【代码】C++(STL源码刨析/stack/queue/priority_queue)
2025-07-15 23:29:22
302
原创 C++(STL源码刨析/deque)
和前面 vector,list的接口不同,他是由一个数组,每个下标储存的是类型指针,在该指针上开辟空间,该指针的空间是连续的,但下标之间不是连续的,可能是连续的,这里不考虑,看起来像是vector 和 list 的结合,因为这样的结构,只需要从中间开始开辟空间,并向2边扩散,这样头尾操作都是O(1),完美的解决了 vector 和 list 的缺点,头操作,增加只需要往左边的缓冲区放入资源,删除则删除即可,不需要挪数据,每个下标之间的缓冲区无关系,尾操作也是一样的。一 deque的结构。
2025-07-15 00:26:33
347
原创 C++(STL源码刨析/list)
由于 链表 不是连续的内存块,所以对每一个申请到的内存块要进行统一组织,也就是封装成一个类,添加前后指针来关联申请到的内存块,在由 List 统一管理起来。由于 List 不像vector那样有一段连续的空间,所以不能直接用裸指针作为 List 的迭代器,但可以为这个指针封装一层,让他模拟指针的行为即可。list的无参构造表示没有节点,但要有个哨兵节点,因为list是双向带头循环链表,为了处理边界情况,所以有一个领头羊。那么怎么访问/遍历/修改 List 中的节点,一个个的访问吗?2. List本身字段。
2025-07-11 23:01:26
538
原创 C++(STL源码刨析/vector)
对象类型:对象指针:对象迭代器:对象引用:对象的个数: 用来计算对象的个数可以看出来指针和迭代器是一样的,因为vector是线性的,直接用裸指针就能模拟迭代器的行为,这里不需要过多考虑。
2025-07-08 22:58:00
887
原创 C++(STL源码刨析/空间配置器)
空间配置器:字面意思就是管理空间,也就是内存,但这个空间不仅仅指的是内存,比如磁盘等可以进行存储介质的硬件都可以有空间。都知道C++申请内存的方式是new,但STL容器走的都是空间配置器,为什么不走new?1. new把申请对象和对象的构造强绑定在一起。2. new申请内存必须指定类型。3. new如果在构造的时候抛异常,对象还没初始化完成,需要手动进行释放操作。4. new只能申请的是堆/共享区的内存,比如 gilic ptmalloc 管理小块内存块调用brk,大块内存块调用mmap。
2025-07-07 20:18:22
763
原创 C++(STL源码刨析/std::sort)
_introsort_loop():首先判断区间元素个数是否小于16,是就结束执行__introsort_loop()下一个函数__final_insertion_sort()插入排序,否则,先判断深度是否为0,是就转堆排,否则,减减深度,然后__unguarded_partition_pivot(),进行三数取中选取key,并划分左右区间,然后__introsort_loop(),递归右区间,然后让右迭代器=key,让他作为左区间的右开区间,即左区间最后一个元素+1的位置,然后执行下一次循环。
2025-07-03 22:36:50
1078
原创 Linux(生产消费者模型/线程池)
这里的效率指的是生产者生产数据和消费者消费数据都需要时间,仅放/拿数据是互斥的,放/拿数据和他们生产任务/处理任务不和交易场所直接关联,也就是通过交易场所进行解耦,互相不冲突达到并行效果,所以总体来说效率高,并且上面是把交易场所当整体使用了,也可以比如交易场所有10个数据,同时放10个消费者取数据,维护他们之间的互斥和同步,也能达到同时消费,进而提高效率,所以下面引入信号量。工厂和工厂的关系:如果超市还差一个货物满了,工厂与工厂之间同时出货物,超市只能处理一个,剩下处理不了,所以工厂和工厂是互斥关系。
2025-06-07 23:06:06
823
原创 Linux(线程控制)
第一:内存中的值没变,其他线程可以继续申请,如果申请到,内存中的值变了,切走的线程再回来,恢复自己的寄存器的里的值:0,交换内存中的值,此时假设内存中原来的值为1,因切走被其他线程交换,变为0,此时0和0交换,在进行if()判断,走else 阻塞等待。第二:执行第二行代码被切走,此时内存中的值由初始1被交换到寄存器,变为0,线程切换保存寄存器的值,后续线程的寄存器初始化为0,和内存中被交换后的值:0,0和0交换,if()不成立,else阻塞。那么用户级线程是如何封装的?用户级线程包含哪些属性?
2025-06-06 16:18:55
1414
原创 Linux(线程概念)
当创建一个进程,加载可执行文件代码和数据,创建内核数据结构:PCB,mm_struct,文件描述符表....等对象,都需要在内存中申请空间,被CPU调度,切换保护进程上下文数据,重新填充pc指针,MMU,TLB,CR3,等一系列的寄存器,每个进程新创建/被切换,带来的开销势必是很大的,这也就是为什么进程是资源分配的基本单位,要什么资源直接给进程分配物理内存。进程调度,当时间片到了切换到下一个进程时,保存上下文更新,CR2指向的页表,TLB缓存内容,全部都失效重新加载新的进程,重新进行TLB缓存预热。
2025-06-03 23:25:08
1122
原创 Linux(信号)
1. 生活中的信号:红绿灯,闹钟,上课铃声....都称为信号。2. 当得知了这些信号,是执行默认动作?还是不管直接忽略掉?还是做你想做的动作?还是等会在做?都取决于自己。3. 当你正在做某件事情的时候,信号可能随时产生,也就是说信号的产生是异步的,比如3个人吃饭,突然有一个人干别的事情去了,无需等待他,继续吃饭称为异步,如果等他把事情做完了再回来在继续吃饭称为同步。// 返回值为 void 参数为 int 的函数指针类型int signum, // 信号编号。
2025-06-03 00:00:19
1081
原创 进程间通信(信号量)
2. 还是和之前的共享内存/消息队列一样,通过形成的key_t并创建一个信号量集,一个信号量集里面可以有多个信号量,并初始化信号量集合,后续对集合中某个特定的元素进行操作。3. 信号量主要用来计数器,信号量++,--操作是原子的,自带同步机制,当某个信号量元素为0则阻塞/其他时间,阻塞有资源则就唤醒等。1. 和共享内存/消息队列一样都属于system v版本的进程间通信,接口大同小异。三 二元信号量的demo代码。
2025-05-31 23:35:43
338
原创 进程间通信(消息队列)
责任链属于行为类设计模式,也就是程序在运行的时候每个模块之间都有任务和优先级,哪些任务完成或者不完成的结果要交给下一个节点(处理点),也可以不启动这个任务,就好比食堂打饭打完这个菜,后面的菜可打可不打。
2025-05-31 18:39:51
1328
原创 进程间通信(共享内存)
key是用户形成的只是用来获取和创建共享内存的,并不能对已经存在的共享内存进行字段修改/结构修改等,只有shmid能操作,本质就是 shmid 属于内核提供的,key是有用户提供的,他们的功能侧重点不同,用户管用户,内核管内核,变相的解耦。1. 传入一个路径和id并通过算法形成一个key_t结构,本质是int类型,算法是固定的,所以不同的进程传入相同的字符串和id得到的key_t都是一样的,这是用户传入数据生成的。key: 用户形成的随机值,用来获取,创建访问共享内存。bytes: 共享内存多大。
2025-05-29 23:44:50
1030
原创 进程间通信(命名管道)
2. 命名管道通过特殊的管道文件路径来标识唯一性,不同的进程的文件描述符表是独立的,但如果都对同一个文件进行操作,该文件的属性,缓冲区,方法级,都是一样的,所以不通过的进程只需要让自己的文件描述符指向同一个文件的相关属性即可,怎么知道不同的进程打开的文件是一样的?1. 前一章说了匿名管道是通过借鉴文件系统的属性方法缓冲区等,操作系统实现的一套具有血液关系的进程通信机制,本质是让不同的进程看到同一份资源(通过子进程继承父进程的文件描述符表),那么命名管道如何标记同一份资源呢?
2025-05-27 18:09:09
409
原创 进程间通信(匿名管道)
字面意思来看管道是单向流动的,只许一方读一方写,或者一方写一方读,也就是特殊的半双工,除非创建2个管道通信,也就是全双工。
2025-05-27 15:34:18
1076
原创 动静态库--
1. 每个函数内部的地址都是相对于该函数的起始位置的偏移量,比如 1168 -> 1 ,-> 2 , -> 3,1加上1168就能访问到具体的语句,被称为相对编址,而上图没有采用,而是采用的绝对地址,1168 -> 116c,直接访问116c不需加上偏移量,被称为绝对编址,这种采用绝对编制范围 全0 ~ 全F,也可以说是逻辑地址(虚拟地址),这个文件里面的地址都是采用ELF格式进行编制的,有自己的固定格式。3. 动态库加载到内存,他的地址是不变的,不管虚拟地址是否变化,最终映射到的库的起始地址是不变的。
2025-05-25 00:00:52
1174
原创 软硬链接--
他们的Inode号是一样的,Link也和上面的软连接不一样,Link就是硬链接数,标识有多少文件与Inode的映射关系,也就是说Inode只有一个,但文件可以有很多个并同时指向这个Inode,类似引用计数,删除任意一个不会影响Inode,只要硬链接数为0,Inode也就会释放掉了。如果被链接的文件被删除或者移动到别的目录下,软连接也就失效了,不会动态随着目标文件而改变,但如果目标文件不在了,但又创建一个和目标文件同名的文件,且路劲和软连接文件的路劲是一样的,就不会失效了。
2025-05-23 00:06:26
896
原创 K个一组链表翻转
很好解决让new一个指针ans,定义一个指针tmp=ans指向他,那怎么让反转后的最后一个节点1指向下一个节点?反转后的状态直接让tmp指向他,也就是 ans的next 指向 prev 所在的位置并让 tmp进行指向(ans后续返回答案要用)。总共5个节点,5/2,总共反转2次,结束让最后的指针指向剩余的节点。然后让tmp指向之前标记的cur也就是pos,也就是反转之前的头部。只要最开始记录1和2,2个节点,并让1指向空,在根据2得到3节点。结束,让tmp指向没反转的节点,节点就是cur指向的节点。
2025-05-21 23:16:21
408
原创 Socket echo server
因为他们的第一个字段是一样的,所以可以用 sockaddr_in和sockaddr_un强转成sockaddr结构,并提取第一个字段判断是AF_INET还是AF_UNIX选择强转成sockaddr_in还是sockaddr_un结构,也就是C语言版的继承和多态。知名端口号比如:HTTP(80),HTTPS(443),DNS(53)。2. 当server启动的时候,就需要accpet获取连接,也就是有连接来了,获取到的连接分配一个文件描述符来进行读写,所以要起多线程/多进程,不然会阻塞后续的accpet。
2025-05-16 19:19:23
698
原创 NAT/代理服务器/内网穿透
当某个内网想访问公网,就必须先找到处于同一个网段的出入口路由器,而内网又不能暴露在公网上,所以当IP地址到达路由器时,路由器会把源IP转换成路由器的WAN口IP,并和源IP进行映射,这里他们互为 key <=> val,当服务器回显时,填充目的服务器IP地址,也就是之前经过转换后的IP地址,源地址填服务器的IP地址,在路由到目的IP所在的路由器,并由该路由器进行查映射表,查到转换之前的私有IP,在进行转发。路由器进行查表:202.244.174.37,查到得到:10.0.0.10,并交付给最初的主机。
2025-05-14 21:45:23
1089
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅