- 博客(41)
- 收藏
- 关注
原创 网络基础概念
网络是指将多个计算机或设备通过通信线路、传输协议和网络设备连接起来,形成一个相互通信和共享资源的系统。TCP/IP 协议的本质是一种解决方案TCP/IP 协议能分层,前提是因为问题们本身能分层截止到目前,我们还没接触过任何协议,但是如何朴素的理解协议,我们已经可以试试了。OS 源代码一般都是用 C/C++语言写的。下面,仔细看看下面的图在计算机网络通信中,报头(Header)是一部分数据包的固定结构,它包含了关于该数据包的元信息和控制信息。报头位于数据包的前部,用于标识和管理数据包的传输。
2024-12-16 20:19:55
971
原创 加餐:读者写者问题与读写锁+自旋锁
自旋锁是一种多线程同步机制,用于保护共享资源免受并发访问的影响。在多个线程尝试获取锁时,它们会持续自旋(即在一个循环中不断检查锁是否可用)而不是立即进入休眠状态等待锁的释放。这种机制减少了线程切换的开销,适用于短时间内锁的竞争情况。但是不合理的使用,可能会造成 CPU 的浪费。
2024-12-16 20:18:29
886
原创 线程池+线程安全+常见锁
IT行业这么火,涌入的人很多,俗话说林子大了啥鸟都有,大佬和菜鸡们两极分化的越来越严重.为了让菜鸡们不太拖大佬的后腿,于是大佬们针对一些经典的常见的场景,给定了一些对应的解决方案,这个就是。
2024-12-13 20:12:17
688
原创 线程互斥与同步
例如,现在有两个线程访问一块临界区,一个线程往临界区写入数据,另一个线程从临界区读取数据,但负责数据写入的线程的竞争力特别强,该线程每次都能竞争到锁,那么此时该线程就一直在执行写入操作,直到临界区被写满,此后该线程就一直在进行申请锁和释放锁。临界区内的线程完全可能进行线程切换,但即便该线程被切走,其他线程也无法进入临界区进行资源访问,因为此时该线程是拿着锁被切走的,锁没有被释放也就意味着其他线程无法申请到锁,也就无法进入临界区进行资源访问了,所以是串行的,所以效率低。所以解锁和等待必须是一个原子操作。
2024-12-13 20:06:51
858
原创 线程概念与控制
在一个程序里的一个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序列”。一切进程至少都有一个执行线程。线程在进程内部运行,本质是在进程地址空间内运行。在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更轻量化。透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流。
2024-12-04 17:50:05
614
原创 进程间通信--详解
如何理解进程间通信?进程具有独立性,所以进程想要通信难度是比较大的,成本高。在日常生活中,通信的本质是传递信息,但站在程序员角度来看,进程间通信的本质:让不同的进程看到同一份资源(内存空间)。进程间通信就是进程之间互相传递数据,那么进程间能直接相互传递数据吗?不能,因为进程具有独立性,所有的数据操作都会发生写时拷贝,父子进程都不能传递,更不要说两个进程毫无关系还想直接相互传递数据。所以两个进程如果想要通信就一定要通过中间媒介的方式来进行通信,那么就必须先想办法让不同的进程看到同一份公共的资源。
2024-11-23 18:36:05
1177
原创 基础 IO(动静态库)-- 详解
这也就是为什么大部分库在提供的时候,一般是提供库文件 + 头文件,也就是为什么在使用 C 语言的时候永远都是 #include <stdio.h>,然后在写 printf 的时候直接调用,最后链接库,本质上就是因为系统在装的时候就把库文件和头文件给装了,而 C 语言的源代码就不需要在系统中了。静态库是程序在编译链接的时候把库的代码复制到可执行文件当中的,生成的可执行程序在运行的时候将不再需要静态库,因此使用静态库生成的可执行程序的大小一般比较大。今天不再是之前的说法了。3.如果你只提供静态库。
2024-11-11 23:54:24
807
1
原创 基础 IO(文件系统 & inode & 软硬链接)-- 详解
我们一直都在说打开的文件,磁盘中包含了上百万个文件,肯定不可能都是以打开的方式存在。其实文件包含打开的文件和普通的未打开的文件,下面重点谈谈未打开的文件。我们知道打开的文件是通过操作系统被进程打开,一旦打开,操作系统就要维护多个文件,所以它是需要被操作系统管理的。也就是说这种方式,磁盘上和内存上都有这个文件,它们不是完全一样的,内存中的文件更强调的是属性和方法,磁盘中的文件更强调的是数据,它们是通过缓冲区关联的;而普通的未打开的文件在磁盘上,未被加载到内存中,它当然也要被管理;
2024-11-06 17:58:14
684
原创 基础 IO(文件描述符)-- 详解
文件在哪呢?从广义上理解,键盘、显示器、网卡、声卡、显卡、磁盘等几乎所有的外设都可以称之为文件,因为“Linux 下,一切皆文件”。从狭义上的理解,文件在磁盘(硬件)上放着,只有操作系统才能真正的去访问磁盘。磁盘是一种永久存储介质,不会受断电的影响,磁盘也是外设之一,所以对文件的所有操作都是对外设的输入输出,简称 IO(Input、Output)。我们知道,当fopen以写入的方式打开一个文件时,若该文件不存在,则会自动在当前路径创建该文件,那么这里所说的当前路径指的是什么呢?
2024-10-31 15:38:48
528
原创 进程控制 -- 详解
思考:什么是进程替换?通过 exec* 函数,把磁盘中的其它程序(代码+数据)加载到内存中,替换当前进程的代码和数据,让页表重新构建映射关系,这期间不会创建新的进程。思考:为什么要进程替换?执行父进程的部分代码,完成特定功能。执行其它新的程序。——> 需要进行「进程替换」,用新程序的代码和数据替换父进程的代码和数据,让子进程执行。思考:操作系统是如何做到重新建立映射的呢?
2024-10-22 15:37:10
875
原创 程序地址空间 -- 详解
进程和程序有什么区别呢?加载的本质就是创建进程。那么是否必须立刻将所有程序的代码和数据加载到内存中,并创建内核数据结构建立映射关系?不是。如果在最极端的情况下,只有内核结构被创建出来了(新建状态)。当真正被调度/执行代码时,才把外设加载内存里,然后再执行代码。理论上,可以实现对程序的分批加载。如果物理内存只有 4G,有一个游戏 16G,能否运行?可以运行。CPU 无论运行多大的程序,都需要从头到尾执行每一行指令。
2024-10-13 21:07:17
1221
原创 进程概念(冯诺依曼体系结构、操作系统、进程)-- 详解
平时创建进程一般是通过 ./myproc 运行某个存储在磁盘上的可执行程序来创建。而我们还可以通过系统调用接口/函数来创建进程:fork是一个系统调用级别的函数,其功能就是创建一个子进程。我们可以通过 man 指令来查看一下 fork 函数$ man fork我们来看一下其中的返回值描述:一旦创建成功了,他会返回子进程的PID给父进程,返回0给子进程。如果创建失败的话,会把-1返回给父进程,没有子进程会被创建。
2024-10-04 15:29:58
901
1
原创 Linux开发工具(git、gdb/cgdb)--详解
我们调试过程中可以使用连招,断点 b + 逐过程n + 逐语句s定位到函数,同时可以结合display显示变量,watch监视,finish结束函数,until跳转等功能进行调试。当依次执行上述命令后,输入 gitee/github 的用户名和密码,在 gitee/github 上的远程仓库刷新就可以看到自己的代码了。然后我们会发现,系统把与linux相同的部分保存不变,与linux不同的内容依次出现在文档里,需要我们手动同步。再次 display 时,i 的值同样是线性递增的,与断点相同。
2024-09-21 18:37:32
2069
1
原创 Linux 开发工具(vim、gcc/g++、make/Makefile)+【小程序:进度条】-- 详解
是的没错,还是从上往下读,执行第一对依赖关系,但是因为第一对依赖关系中的依赖文件proc.o并不存在于当前目录,那么此时make会在当前文件中找目标文件为proc.o的依赖关系,发现依赖文件proc.s也并不存在,以此类推,最后推导发现依赖文件procc存在,就执行对应的依赖方法,在从下往上。在 C 程序中,并没有定义 printf 的函数实现,且在预编译中包含的 stdio.h 中也只有该函数的声明,而没有定义函数的实现,那么是在哪里实现 printf 函数的呢?让清理工作总是被执行!
2024-09-16 20:42:39
2096
1
原创 Shell的运行原理以及Linux当中的权限问题
目录的可执行权限是表示你可否在目录下执行命令。如果目录没有 -x 权限,则无法对目录执行任何命令,甚至无法 cd 进入目录,即使目录仍然有 -r 读权限(这个地方很容易犯错,认为有读权限就可以进入目录读取目录下的文件)。而如果目录具有 -x 权限,但没有 -r 权限,则用户可以执行命令,可以 cd 进入目录。但由于没有目录的读权限所以在目录下,即使可以执行 ls 命令,但仍然没有权限读出目录下的文档。
2024-08-27 17:37:36
893
1
原创 C++智能指针
什么是内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。// 1.内存申请了忘记释放// 2.异常安全问题Func();// 这里Func函数抛异常导致 delete[] p3未执行,p3没被释放.
2024-08-26 20:51:54
884
原创 C++异常
在实际使用中,很多公司都会自定义自己的异常体系来进行规范的异常管理。因为在一个项目中,如果大家随意抛异常,那么外层的调用者基本就没办法玩了,所以在实际中都会定义一套继承的规范体系。这样大家抛出的都是继承的派生类对象,捕获一个基类就可以了。
2024-08-14 20:31:43
862
原创 C++11(下)
function是一种函数包装器,也叫做适配器。它可以对可调用对象进行包装,C++中的function本质就是一个类模板。Ret:被包装的可调用对象的返回值类型。Args…:被包装的可调用对象的形参类型。function包装器可以对可调用对象进行包装,包括函数指针(函数名)仿函数(函数对象)lambda表达式类的成员函数。public:class Pluspublic:int main()//1、包装函数指针(函数名)//2、包装仿函数(函数对象)
2024-08-12 13:06:51
998
原创 C++11(上)
链接:在 2003 年 C++ 标准委员会曾经提交了一份技术勘误表(简称TC1),使得 C++03 这个名字已经取代了 C++98 称为 C++11 之前的最新 C++ 标准名称。不过由于 C++03(TC1)主要是对 C++98 标准中的漏洞进行修复,语言的核心部分则没有改动,因此人们习惯性的把两个标准合并称为 C++98/03 标准。从 C++0x 到 C++11,C++ 标准 10 年磨一剑,第二个真正意义上的标准珊珊来迟。
2024-08-07 20:33:36
708
原创 哈希 Hash(闭散列、开散列介绍及其实现)
链接:【翻译】unordered_map 是存储 <key, value> 键值对的关联式容器,其允许通过 key 快速的索引到与其对应的 value。在 unordered_map 中,键值通常用于唯一地标识元素,而映射值是一个对象,其内容与此键关联。键和映射值的类型可能不同。在内部,unordered_map没有对 <key, value> 按照任何特定的顺序排序, 为了能在 O(1) 内找到 key 所对应的 value,unordered_map将相同哈希值的键值对放在相同的桶中。
2024-07-29 04:40:48
1166
1
原创 红黑树模拟实现STL中的map与set
/ 定义红黑颜色RED,BLACKT _data;// 数据域// 用来标记节点颜色RBTreeNode(const T& data) // 构造函数{}**定义红黑树结构(KV)**==K:键值 key 的类型。T:数据的类型,如果是 map,则为 pair;如果是 set,则为 K。
2024-07-27 01:56:53
760
原创 C++红黑树
AVL树:严格平衡(左右子树高度差不超过 1),所以 AVL 树的查找、插入、删除效率高:O(logN),但在插入和删除节点后,要维持树的平衡状态,做的旋转处理还是很多的。红黑树:近似平衡(控制最长路径不超过最短路径的2倍),变了一种方式来控制树的平衡,相较于 AVL 树而言,没有那么严格。红黑树更多是一种折中的选择,它舍弃平衡二叉树的严格平衡,换取节点插入时尽可能少的调整。因为红黑树的旋转情况少于 AVL 树,使得红黑树整体性能略优于。
2024-07-25 03:54:58
2412
2
原创 AVL树(高度平衡二叉搜索树)
AVL 树节点是一个三叉链结构,除了指向左孩子的指针右孩子的指针,还有一个指向其父亲的指针,数据域是键值对,即 pair 对象,还引入了平衡因子,用来判断是否需要进行平衡操作。// AVL树节点的定义(KV模型)// 该节点的左孩子// 该节点的右孩子// 该节点的双亲指针// 键值对int _bf;// 该节点的平衡因子(balance factor) = 右子树高度-左子树高度// 构造函数, _kv(kv), _bf(0){}// AVL树的定义(KV模型)
2024-07-23 02:39:10
784
原创 二叉树进阶
二叉搜索树作为一种经典的数据结构,它既有链表的快速插入与删除操作的特点,又有数组快速查找的优势;情况d:找到能够替代其位置的值,也就是要满足左子树小于该值,右子树大于该值,我们可以找左子树的最大值或者右子树的最小值,然后被删除节点赋值成找到的最大值或最小值,在删除最大值或最小值节点。尽管是同一组序列,但是不同的插入顺序构造出的搜索二叉树也是不一样的,如上图所示,左边是比较正常的树,右边是极端情况下的歪脖子树。最差情况下,类似于右边的歪脖子树,此时树的高度接近于N,因此其复杂度就是:N、
2024-07-20 15:01:26
1095
原创 set、map、multiset、multimap的介绍及使用
1.set是按照一定次序存储元素的容器,使用set的迭代器遍历set中的元素,可以得到有序序列(默认是升序);2.set虽然是采用了键值对的方式存储数据,但是在set中,value就是key,即相当于是K模型,并且每个value必须是唯一的,不可以重复,所以可以使用set进行去重;
2024-07-19 01:16:07
919
1
原创 Linux常见指令
上面说到 -f 是强制性删除,-r 是递归式删除,这两个结合在一起就是递归式强制性删除,千万不要执行以下指令,因为在Linux当中没有类似回收站的东西也没有撤销删除的功能,删除指令一旦执行则是不可逆的。4)网络操作命令:ifconfig、ip、ping、netstat、telnet、ftp、route、rlogin、rcp、finger、mail、nslookup。2)-a 详细输出所有信息,依次为内核名称,主机名,内核版本号,内核版本,硬件名,处理器类型,硬件平台类型,操作系统名称。
2024-07-16 01:10:56
741
1
原创 C++多态
多态是指不同继承关系的类对象,去调用同一函数,产生了不同的行为。在继承中要想构成多态需要满足两个条件1.必须通过基类的指针或者引用调用虚函数。2.被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写。1.在虚函数的后面写上=0,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。//抽象类(接口类)class Carpublic://纯虚函数int main()Car c;//抽象类不能实例化出对象,errorreturn 0;
2024-07-10 21:50:20
770
原创 C++继承
继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用。例如,以下代码中Student类和Teacher类就继承了Person类。//父类public:protected:string _name = "张三";//姓名//年龄//子类。
2024-07-09 23:28:23
927
原创 模版进阶操作
一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。优点:模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生。增强了代码的灵活性。缺陷:模板会导致代码膨胀问题,也会导致编译时间变长。出现模板编译错误时,错误信息非常凌乱,不易定位错误。
2024-07-07 18:53:06
660
原创 priority_queue的使用与模拟实现
优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中的元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。默认情况下priority_queue是大堆。使用vector作为底层容器,内部构造大堆结构。使用vector作为底层容器,内部构造小堆结构。不指定底层容器和内部需要构造的堆结构。此时默认使用vector作为底层容器,内部默认构造大堆结构。
2024-06-28 17:56:43
734
原创 stack和queue的模拟实现
deque(双端队列):是一种双开口的"连续"空间的数据结构,双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比较高。deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组,其底层结构如下图所示:那deque是如何借助其迭代器维护其假想连续的结构呢?
2024-06-27 23:18:45
601
原创 stack和queue的介绍及使用
使用默认的适配器定义栈。使用特定的适配器定义栈。注意: 如果没有为stack指定特定的底层容器,默认情况下使用deque。使用默认的适配器定义队列。使用特定的适配器定义队列。
2024-06-27 23:00:24
711
原创 list的介绍及使用
1.list是一种可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。2.list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立结点当中,在结点中通过指针指向其前一个元素和后一个元素。3.list与forward_list非常相似,最主要的不同在于forward_list是单链表,只能进行单方向迭代。4.与其他容器相比,list通常在任意位置进行插入、删除元素的执行效率更高。
2024-05-30 20:20:15
799
原创 vector的深度剖析及模拟实现
1.基本框架public:private:// 指向数据块的开始// 指向有效数据的尾// 指向存储容量的尾我们首先定义了一个模版类,这里的vector三个成员均为迭代器,而vector的迭代器是一个原生指针,我们这里为其定义别名iterator这些成员变量用于管理vector内部的动态数组_start:这是一个指针,指向分配给vector的内存区域的开始。这是数组的第一个元素_finish:这个指针指向数组中最后一个实际存在的元素的下一个位置。
2024-05-23 14:52:47
1015
2
原创 vector的介绍及使用
1、vector是表示可变大小数组的序列容器。2、vector就像数组一样,也采用的连续空间来存储元素,这也意味着可以采用下标对vector的元素进行访问。3、vector与普通数组不同的是,vector的大小是可以动态改变的。4、当vector需要重新分配大小时,其做法是,分配一个新的数组,然后将全部元素移到这个数组当中,并释放原来的数组空间。5、vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因此存储空间比实际需要的存储空间一般更大。
2024-05-18 19:00:54
689
3
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人