- 博客(42)
- 收藏
- 关注
原创 【Linux的内存管理】
在内核低地址的896MB的空间,是一块直接映射的区域。一个页或页框是4KB的大小,虚拟和物理地址是4GB的大小,就会有4 * 1024 * 1024 / 4个页或页框(一百多万的页),一个页表的大小就要4MB的大小,如果进程过多的话,采用这样的页表方式,将会浪费物理内存。分段会产生内存碎片问题,产生的原因是,申请段大小的空间,申请多少就会有多少,然后不同的段大小是不一样的,这就会导致有些区域空间无法申请成功,操作系统就会再其他可以申请的区域申请,就会导致无法申请的区域浪费掉,这就是外部的内存碎片问题。
2024-09-29 22:39:50 1034
原创 【Linux的进程管理】
一个task_struct内会有许多的属性,有当前该进程的唯一标识ID,操作系统为该进程创建为模板的父进程ID,当前任务的状态,文件描述符集合,进程地址空间,内存空间,信号集等一些重要属性。然后每个进程都会有一个指向下一个进程的指针。当正在执行的进程被切换的时候,操作系统会保存当前进程的消息,包括当前寄存器的数据,程序计数器PC,内存管理的信息,进程的内核栈,用户堆栈的信息,当前的进程状态或者打开的文件,网络套接字等,因为有些进程是因为进程的时间片用完了,但可以继续在就绪队列中等待下一次被CPU调度。
2024-09-16 17:59:01 841
原创 【水平触发LT和边缘触发ET】
ET模式让底层数据被应用层一次性获取,那底层的缓冲区的空间就更多,如果使用TCP的网络连接方式,epoll模型就会告知发送方可以接受跟多的数据(因为接受缓冲区的窗口更大),这样在通信的时候,就会提高吞吐量,提高效率。ET模式中,如果有事件从无到有,或有新事件的增加,调用epoll_wait的时候,通知到应用层,应用层进行处理,在ET模式中,对新的事件只会通知生产一次,下一次如果没有新事件的到来,即使事件的数据没有处理完,下次也不会在通知上层,只有新的事件到来,epoll_wait才会在一次大的通知。
2024-09-16 09:17:29 416
原创 【I/O多路复用】
epoll模型中,有一个红黑树和队列的数据结构帮助epoll模型I/O多路复用,将需要监听事件的文件描述符添加到红黑树中,这样就不会有文件描述符的限制影响,一旦某一个文件描述符的事件就绪,就将该文件描述符添加到一个就绪队列中,该队列中,所有的文件描述符都是已经事件就绪的,就等到被调度,有了这个就绪队列,事件是否有就绪可以在O(1)的时间复杂度知道,不会在像select和poll模型那样,内核态和用户态遍历文件描述符集合的数组,然后才知道有事件就绪,提高了效率。内核硬编码和效率的原因限制监听的事件有限。
2024-08-22 19:55:28 598
原创 【线程同步】
就好比预约了图书馆位置,申请成功了你就可以在一段时间内由自己独占,如果有其他多人申请,就让申请者排队等待,一旦使用完毕了,把位置腾空了,可以再次申请获取该位置的使用,但需要排队,在之前申请的人后面排队进行等待,直到再轮到你。这样,每一个申请的人都可以的得到该位置的使用机会。只使用互斥锁是可以保证线程安全的,但是如果一个线程访问临界资源的时候获取互斥锁,访问完临界资源后释放锁,可能下一步的调度还会是该线程,继续抢占互斥锁这个资源,不断获取不断释放,导致其他的线程不能得到调用,这就是进程饥饿问题。
2024-05-30 19:23:42 344
原创 【Linux线程】
如果不让主进程一直的执行下去,主进程结束退出,如果线程要处理的任务的时间是100个单位,而主进程创建线程完成后进程就立即结束,则线程也跟着退出。对于父子进程来说,各自有一份独立的虚拟地址空间,当在内存的共享文件有其中的一个进程对数据进行修改的时候,操作系统就会对在内存的文件进行写实拷贝。但是对于进程来说,他们是独立的,所有进程间的交互就变得复杂,共享信息的时候需要进程间通信的机制。在linux当中,线程被称为轻量级进程,轻量级进程是基于进程实现,直接复用了进程的原理,但也可以实现和多线程的一样的功能。
2024-05-05 21:03:42 815 2
原创 【进程替换】
函数进行进程替换,path为文件路径,要找到替换的进程,arg为指令的字符串,替换的进程如何执行,最后要加上NULL表述结尾。进程替换后,代码和数据会改变,所以上述代码和结果为什么next:process没有打印出来,这是因为代码和数据不在是原来的了。子进程的作用是为了和父进程执行不一样单位任务,创建的子进程是和父进程共享代码和数据的。所以要使子进程做不同的任务,就要有新的代码和数据,在Linux的task_struct要找到新的代码和数据。进程替换其实并没有创建子进程,只是不再是原来的代码和数据。
2024-02-25 16:10:36 157
原创 【进程地址空间】
因为是拷贝的,所以他们虚拟地址一样,上层用户看到同样的地址,实际上,进程还要通过一个叫页表的表来映射虚拟地址和物理内存的关系,一旦父子进程对数据有修改的操作,操作系统会为第一个改变数据的进程在物理地址重新找到一个可以使用的物理内存,改变页表的映射关系。进程管理和内存管理解耦。进程需要被描述成一个task_struct结构体对象,并以一定的数据结构来对进程进行管理,进程地址空间也应该如此,Linux进程地址空间其实就是进程task_struct结构体的一个对象,叫mm_struct。引用深入理解计算机系统。
2024-02-24 15:35:10 1667
原创 【进程状态】
一个进程在就绪的状态,是可以随时被CPU调度到的进程,因为一个CPU只能处理一个进程,而且计算机要有并发执行的能力,要运行的进程很多,所以就绪状态的进程会链接到一个双向的运行队列当中,当一个进程在CPU被执行完,进程退出,就可以调度运行队列里的优先级高的进程。在等待键盘资源的期间,进程不是运行的状态,而是等待的状态。非阻塞等待需要在一个轮循的方式等待,即父进程的不能退出,或者父进程要在子进程后面退出,否则,如果父进程先退出,子进程会变成孤儿进程,不在是原来的父进程管理了,由进程pid位1的进程来管理。
2024-02-23 15:48:23 908
原创 【进程创建】
父进程创建子进程后。数据发送了改变,哪个进程先对这个数据改变,操作系统就会申请新的内存给该进程,这是为了保证进程的独立性,互补干扰,让进程有独自的存放空间。查看父进程可以调用getppid(),当程序被运行的时候,我们发现两个循环体尽然都有在执行,而且他们的进程pid都不一样,但是子进程的ppid和父进程的pid一样,说明子进程是由父进程创建的。linux中第一个创建的进程是init,操作系统以后的进程创建,就可以依靠该init进程为父进程创建子进程,创建的子进程也可以做为之后进程创建的父进程。
2024-02-22 15:07:35 1486
原创 【进程概念】
对于一个在磁盘可执行的二进制文件,也可叫做可执行程序。对于一个可执行的程序,程序有自己的代码和数据。一旦运行起来,就会在计算机内部运作起来。根据冯诺依曼体系,一个程序要被运行起来,先是要加载到内存当中,然后再到CPU调度。当代计算机中,能运行一个程序,就能运行多个程序,然后每个二进制的可执行文件程序都像在自己干自己的事情,互不干扰。这就是在计算机运行的程序。
2024-02-21 18:29:47 486
原创 【操作系统】
以Linux操作系统为例,Linux操作系统是一款软件,是通过大量的C语言和一些汇编的语言来进行编写的。通过上面的结论,知道了操作系统是管理计算机硬件和软件的一款软件,操作系统是在计算机硬件和软件之间的。操作系统中有大量的内核数据结构和数据对象,这些数据结构和对象是用户的数据,这些大量的数据结构和对象要由操作系统进行管理。操作系统主要的功能 :①进程管理②存储管理③设备管理④文件管理。
2024-02-21 08:21:35 724
原创 【冯诺依曼体系】
因为虽然比CPU存储的效率低,但是相对于外设的输入设备是有非常大的提升,所以先把外设资源加载到内存当中,然后CPU再从内存获得数据,CPU执行完毕后,再以极短的时间内继续再内存获得要运行的数据,避免CPU被空闲,浪费资源。CPU的效率是最快的,比如磁盘这个外设,磁盘存储的速率相比于CPU是非常非常的慢的,如果磁盘直接和CPU关联起来加载数据到CPU,CPU很快的运行完第一次的数据,那么下一次的磁盘数据内容还没加载到CPU当中,这就会造成CPU资源的浪费,所以就不会让CPU和磁盘等外设直接有数据的交互。
2024-02-20 15:23:34 701
原创 【C++11 lambda函数表达式】
通过捕捉列表捕捉的参数他们的地址是一样的,所以我们可以采取matable或引用的方式对他进行修改,如果没有采取,我们只能对变量进行使用但不能修改。当使用=(或&引用)进行捕捉时,我们不能在对某个变量或对象进行值(或&引用)得捕捉,这会导致重复捕捉,只能=全部(或全部&捕捉)进行值捕捉,然后对某个变量或对象进行引用捕捉(或值捕捉)。我们可以使用函数指针实现防御值大小比较排序,仿函数实现价格排序比较功能,但是我们如果需要用其他的属性比较,我们就需要在实现类似的函数指针或仿函数。&引用捕捉,=值捕捉。
2024-01-05 10:53:53 989
原创 【C++智能指针】
智能指针是行为类似于指针的类对象,可以通过这个对象进行动态内存资源进行管理。如果程序员进行了动态内存的申请,执行程序的开始到结束,如果没有进行正确的释放,就会导致内存泄漏,失去对该内存的管理控制。如果使用了智能指针进行管理,当程序结束时,智能指针这个对象将自己调用析构函数,进行资源的释放。
2024-01-05 10:34:11 818
原创 【C++11 initializer_list 列表初始化】
这里的a1用大括号进行初始化,开辟的空间里有5个数据,一开始的五个数据在initializer_list,在常量区,因为initializer_list有指向开始和结束的两个原生指针(_First和_Last)和迭代器,和成员变量begin()和end(),所以支持范围for的使用方法。//代表了开辟了三个空间,也是initializer_list初始化的,分别存储了1,2,3三个值。//代表开了一个空间,是用initializer_list初始化的,开辟了一个空间,储存的值是10。
2023-12-30 20:57:15 378
原创 【散列表(哈希表)————数据结构】
这时候我们就可以把整个字符串的每一个字符用对应的ASCII来进行相加,但是我们有面临另一个问题,比如abc和acb和aad,如果他们都相加,最后的值是一样的,然后所映射的值是相同的,最后还是会多次哈希冲突,造成效率的下降。然后通过映射在表上的位置来获取这个关键字所在的结构体中其他的数据。在开放的拉链法当中,当负载因子是1的时候,我们就需要扩容,因为我们写完希望的是平均一个关键字对应一个哈希表的位置,这才可以让我们的查找,插入,删除的操作的平均时间复杂度为一个常数的时间复杂度。关键字我们使用一个结构体实现。
2023-11-24 10:43:58 75
原创 【C++实现红黑树】
红黑树(Red Black Tree) 是一种自平衡搜索二叉查找树。红黑树是一种特化的AVL树(平衡二叉树),都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能,消耗较小的时间复杂度的一种查找方式。虽然实现的情况非常复杂,但是在最坏的情况下运行效率也是非常可观的,最坏的时间复杂度就是树的最高的高度O(logN)。N即为树的结点数。我们需要用枚举出两种颜色或者两种不同的标记。在定义结点的时候,我们要把第一个结点做为根结点,每个结点需要三个指针。
2023-11-16 21:38:58 47
原创 【C++手撕AVL树】
如果左边比右边高一个高度差,这个结点的平衡因子就是-1,如果右边比左边高一个高度差,平衡因子就是1,在以此类推,往上遍历,左高自减,右高自增,如果高度差为2的时候,就不能再往上++或–了,因为平衡因子为2或-2的时候就不是平衡的条件。把发生第一次高度差的节点的左子树给这个节点的_parent指向的父亲节点的右子树,节点的右子树的这个节点给这棵树的根结点的左子树,然后这个节点做这棵树的根,原来这个节点指向的_parent作为这个节点的左子树(根节点),原来的根节点作为这个节点的右子树。不平衡的情况有很多种。
2023-11-10 16:57:59 63
原创 【leedcode——随机链表的复制】
给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点。3.创建拷贝的结点,然后把原链表相对应的结点的值赋值,再把拷贝的结点链接到原结点相对应结点的下一个。
2023-11-01 20:56:48 91 3
原创 【C++搜索二叉树】
生成一颗树,我们树的每个结点都要有指向左子树和指向右子树的两个指针,和一个存放数据的变量。我们可以写一个结构体或类来表示。第一个二叉树符号搜索二叉树的条件,第二个如果把10的结点换成比13大的数或者比8小的数都不会满足搜索二叉树的条件,第三个二叉树如此。如果最开始是一个空树,我们直接把根结点的结构体中赋值,然后把指向左子树和指向右子树的指针指向nullptr。如果调用自己生成的拷贝构造,会是浅拷贝,所以需要自己生成拷贝构造。搜索二叉树是一个排序的树,或者是一课空树,搜索二叉树有三种性质。
2023-10-30 13:38:28 34
原创 【C++多态】
(因为派生类对象给基类对象赋值的时候,调用了拷贝构造,实例化的对象就是基类的对象了,就构成不了多态,但是不会把虚函数表的指针进行拷贝,直接再生成一个虚表指针,如果说可以拷贝,那虚表可能是派生类的,不是基类的,但是基类的对象不能调用派生类的虚函数)。我们可以发现,这种单继承的类的每个对象都会有一个虚函数表,每个表的地址都不一样,如果基类的虚函数没有在派生类进行重写,派生类的虚表里存储没有重写的虚函数地址跟基类的一样,如果重写了,就会是新的地址。如果派生类有自己独有的虚函数,则会把虚函数的地址放在第一个虚表。
2023-10-25 11:46:28 98 2
原创 【类继承和多继承】
这里一个派生类学生的对象s,和一个基类person基类的对象man,引用rman,Preson* 的pman,这里s对象给man对象赋值,就把派生类中基类的对象那部分赋值给man对象,如果是rman引用,就把派生类中基类那部分的对象起别名,如果是指针pman,就指向派生类中基类的那一部分的地址。在继承中,如果基类和派生类都有同名的函数,同名的函数构成了一种隐藏的关系,只要是函数名相同了,不管类型还是参数的不同,都构成了隐藏,他们不构成重载,因为虽然同样的函数名,但他们却是不同的作用域。
2023-10-18 15:28:32 102 4
原创 【C++模拟实现优先级队列priority_queue】
优先级队列是对一个一个vector进行大小堆的一个建立,默认的是建立一个大堆,因为less<>()是他的默认函数,less<>()是一个函数对象。less<>()是生成大堆,vector的首元素将会是最大值,如果是想建立一个小堆,我们可以传一个greater<>()函数对象。这两个函数可以通过仿函数来实现,是一个可以行使函数功能的一个类,函数对象可以以函数方式与()结合使用的任意对象,即定义了函数operator()()。仿函数多用在STL中。比如less<>,用一个类模板,定义一个less的类。
2023-10-08 17:07:21 64 1
原创 【动态规划————买股票的最佳时机含冷冻期LeedCode.309】
给定一个整数数组prices,其中第 prices[i] 表示第 i 天的股票价格。设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
2023-10-06 16:02:29 55 2
原创 【C++模拟实现队列】
队列是一种数据先进先出的一种数据结构,是一种容器适配器,queue可以使用底层模板deque来实现。但queue不允许随机访问队列的元素,没有迭代器,不能遍历queue,queue可以实现尾部插,和队首删除,可以访问到队首和队伍的数据。检查队列大小和判断队列是否为空的功能。
2023-10-05 22:27:05 78
原创 【C++模拟实现stack栈】
栈是一种先进后出的一种数据结构,当访问栈的数据结构的时候,我们可以访问栈顶元素,再C++中,栈是一个容器适配器,栈可以使用vector和list还有deque双端队列进行适配。
2023-10-05 11:16:45 82 1
原创 【leedcode翻转链表】
当head->next指向new的时候,就会没有原本链表的下一个位置,所以tmp之前记录的head->next就可以重新给head赋值。然后继续原来的步骤,先把head->next赋值给tmp,然后把head->next重新指向new,head的值赋值给new,tmp再赋值给head。然后再把head->next指向一个new的结点(new结点为NULL),这样原来的链表的头结点就会指向NULL。把链表1->2->3->4->5->NULL翻转成5->4->3->2->1->NULL。
2023-09-11 22:47:21 39
原创 【vector模拟实现】
vector是C++ STL的一种容器,是一种顺序表,可以按照连续的空间储存数据。使用vector可以方便我们使用,因为再使用的时候不再需要考虑空间扩容的问题,而且提供函数接口来进行增删查改的操作,不再需要我们自己来进行具体的实现,直接使用上层的接口来直接通过底层代码进行程序。且支持迭代器。vector只需要三个成员变量就可以实现它所需要的条件,一个是连续空间的起始地址,一个是连续数据结尾的地址,另一个是整个空间大小最后的地址,因为vector可以存放不同的数据类型,所以我们可以使用模板。这里的三个指针是
2023-09-05 22:52:21 90 1
原创 【C++类的构造,析构,拷贝构造,赋值运算符重载的成员函数】
**拷贝构造对于某些类是非常重要的,这里的栈就是其中之一,因为如果自己不写拷贝构造,就会调用默认生成的拷贝构造,而默认生成的拷贝构造是一种浅拷贝(值拷贝),对于栈而言,它需要开辟空间,一旦浅拷贝,将会有两个指针指向同一块空间,当程序结束时,调用析构函数时,就会造成同一块空间被释放两次,就会造成程序的崩溃。实例化一个st时,会走初始化列表,如果没有编写初始化列表,私有的成员如果是内置类型,将会生成随机值,如果是自定义类型,将会调用自己的构造函数。当程序结束时,先调用s3的析构函数,把s3._a的空间释放。
2023-08-15 09:04:36 55
原创 【C++的类】
1.C语言是一门面向过程的一门编程语言,而CPP是一门面向对象的语言。如果要知道一架无人机从起飞到降落的过程,在面向过程中,首先地上操作人员要启动开关,无人机自行操作,在跑道到达一定的速度时,然后向上拉升,达到一定的高度时候在平稳飞行,块到达目的地的时候在降低飞行高度,在降落过程中,接触到跑道,就进行反向制动,最后无人机速度降为零,关闭开关。在面向对象中,只需要关注操作人员和无人机,在天空的一系列行为只需要无人机自行处理,我们就不需要过多关注。
2023-08-11 18:10:44 45 1
原创 【C++的引用】
C++使用了一种符合类型,叫引用变量。(符号&)跟C 和 C++的取地址符一样,作用有点类似,但不是取地址符号。引用是给一个变量或者对象取一个别名,但是编译器并不会给引用开辟空间。可以说引用和它引用的对象是同一块空间,在编译器的角度来说。可以发现val变量和它的引用变量reference的地址是一致的,reference被声名为int& 的类型,意思是指向int 变量的int& 的引用。引用的作用有点类似指针,但引用并不是指针,虽然它看起来有点像。
2023-07-29 23:48:43 37
原创 【C++的开始第一步】
命名空间是C++的一个特性,使用命名空间可以在编写大型程序或者多人多厂商编写程序组合的时候可以没有命名的冲突,造成重定义。其在C的基础上,增加了类的概念,并引入了引用,重载,多态的理念,使面向过程的C语言进步到面向对象的C++语言。这里的cin 是一个C++标准的输入流对象,使用右移运算符 “>>” 从设备键盘取得数据,送到输入流对象cin中,然后送到内存,cin也是。可以发现,如果是用自己的命名空间的变量,输出的是自己的初始化。这里的用法是将std命名空间全部展开,可以使用命名空间里面的所有名称。
2023-07-19 23:50:37 52
原创 自定义类型中的结构体类型
结构的声名是描述了一个结构的组织布局例如:在结构体中,可以声名多种数据来描述这个结构体变量的多种成员变量。比如描述一本书的书名,价格,出版社,作者,日期等等。int date;} b;//声名一个结构体的时候创建了一个全局的结构体变量b int main() {0 };//定义一个结构体变量B并初始化为0 struct book * pb;//定义了一个结构体指针变量pb B = {"C语言" , 100 , 2023 };//给结构体变量B的成员变量赋值 return 0;
2023-03-19 21:59:59 85
原创 字符串函数
dest,const char* str):**:该函数接收的是两个数组的首地址或两个指针,源字符串用const修饰,解引用的内容不能修改,函数返回的是一个char*的指针,是目标空间的首地址。*:该函数接收的是两个数组的首地址或两个指针,源字符串用const修饰,解引用的内容不能修改,函数返回的的是一个char*的指针,是目标空间的首地址。函数接收的是两个指针,返回的是一个有符号的整数,如果第一个跟第二个不匹配,第一个大于第二给,返回值大于0,否则小于0,如果两个相等,则返回0。
2023-03-15 18:35:16 49
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人