自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(93)
  • 收藏
  • 关注

原创 LINUX:懒汉单例模式线程池

创建一个线程,就是要这个线程去完成某种任务的。池化技术就是,提前创建一批线程,有任务这一批线程就会执行。而不是有任务的时候在去创建线程。池化技术有着更高的效率,因为线程都是提前创建好的,直接执行任务。

2024-07-16 12:57:18 421

原创 LINUX系统编程:基于环形队列和信号量的生产者消费者模型

当下标遍历到vector末尾的时候, 下标 %=判断环形队列为空还是为满 vector容量,下标就回到数组的开始。所以生产者在生产之前要对空间的信号量进行p操作,生产完成之后要对数据的信号量进行v操作。所以消费者在生产之前要对数据的信号量进行p操作,消费完成之后要对空间的信号量进行v操作。队列一定不为空&&不为满,这个时候生产者可以生产,消费者可以消费。环形队列可以实现并发生产和消费,就是在消费的同时也可以生产。为空的时,只能让生产者先生产,消费者后消费,为满时,消费者先消费,生产者后生产。

2024-07-12 22:55:39 499

原创 LINUX系统编程:基于阻塞队列的生产者消费者模型

它是时一个经典的多线程并发的协作模型,也就是生产者(线程)向一个公共区域(阻塞队列)生产,生产出的“商品”被消费者(线程)消费。其中生产者可以拥有多个,消费者可以拥有多个,但是公共区域只有一个。

2024-07-12 10:17:54 755

原创 LINUX系统编程:多线程互斥

这也就解释了,为什票会抢到负数,究其原因就是我们抢票+判断的操作不是原子的,所以我们要通过互斥锁把这两个操作编程"原子"的,这个原子是在线程看来是原子的,不是真正意义上的原子。thread1开始执行,thread1进行判断,thread1发现票数也是大于0的,进入循环,这个时候hread1的时间片到了,thread1进入等待队列。thread0进行判断,thread0发现票数是大于0的,他就会进入循环,但是这个时候thread0的时间片到了,thread0进入等待队列。3.销毁的互斥量,就不要在加锁了。

2024-06-30 19:55:47 758

原创 LINUX系统编程:原生线程库的封装

在c++11之后,c++也支持了多线程,也就是thread库,thread库一定是对pthread库的一个封装,因为在Linux下不存在线程的概念,只有轻量级进程,pthread库就是对轻量级进程的系统调用接口封装成线程接口。那么对线程的管理一定不是操作系统做的,因为操作系统不认识线程,这个工作是由pthread原生线程库做的。pthread动态链接到共享区,使用struct pthread描述线程,struct pthread在共享区的起始地址就是该线程的 tid。可以打印看看sleep(1);

2024-06-30 14:54:28 643

原创 C++11

这个也不知道是哪个神人想出来的,大概思路就是,[1,3,4,5,6],第一次调用show将[1]传给t,[3,4,5,6]传给arg,第二次递归将[3]传给t,[4,5,6]传给arg,依次类推就将所有参数解包。bind是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。在c++98中,我们使用sort这类的接口,想要比叫大于或者是小于,必须自己写一个仿函数,非常的不人性,非常的麻烦。

2024-06-28 20:11:46 918

原创 LINUX系统编程:线程控制

但是别忘了还有缓存命中这种东西,cpu在加载代码的时候,会把该代码附近几行也加载到cpu的高速缓存中,一旦进程切换了,这些高速缓存的数据,对于下个进程就没有用了,都是失效的数据,但是对线程不是这样的,线程代码和全局数据是共享的。在LINUX内核中是不存在线程的概念的,只有轻量级进程的概念,但是在用户的角度来看,用户就是要把轻量级进程当做线程的使用,所以将轻量级进程的控制接口,封装成了一个线程库,用户直接使用库,也就屏蔽了底层的实现。2.操作系统切换线程的工作,要比进程小的很多。

2024-06-21 16:58:17 845

原创 LINUX系统编程:线程的概念

本文主要介绍,在LIUNX下的线程。

2024-06-20 09:54:39 320

原创 LIUNX系统编程:可重入函数&&volatile

在执行流执行到mian函数,insert函数中的1号位置的时候,突然就陷入内核,处理信号,执行信号自定义方法,这个方法调用的也是insert,执行完之后,导致了n2的节点丢失,那么这个函数就叫不可重入函数,如果在执行流重复进入的情况下不会出问题的就叫,可重入函数。我们发现已经终止不了,这是因为编译器看我们的main函数中没有对flag的修改,直接就把flag放到寄存器中了,省着还要和内存交互,但是我们对内存的flag修改,并不会影响寄存器。这个关键字主要就是防止编译器的优化,保持内存的可见性。

2024-06-12 22:07:11 213

原创 LIUNX系统编程:信号(3)

内核级的页表只有一份,因为操作系统只有一个,操作系统的内核数据也只有一份,只需要一个内核级的页表,建立不同进程的虚拟地址到操作系统所处物理内存的映射,就可以让多个进程看到一个操作系统。硬件中断:键盘直接连接到cpu的针脚上,当键盘输入的时候,会给对应的针脚一个高电频,cpu会拿着这个针脚的编号,去操作系统的中断向量表(函数指针数组),执行对应的函数。由一个硬件,向操作系统发送一个频率非常高,时间非常短的周期时钟中断,操作系统会拿着中断号去操作系统的中断向量表中,执行进程调度,内存管理,更新系统时间。

2024-06-07 21:22:23 713

原创 LINUX系统编程:信号(2)

当进程收到2信号,首先会将pending位图第二比特位变为1,等到机会合适时处理pending中的信号,处理时会看2号信号的位置在block中是否为1,如果为1就阻塞住,不递达,为0就递达,调用handler中对应的函数。它们也是不可阻塞的,因为它们是用来控制进程执行状态的信号,不受进程阻塞的影响。3.向进程发送2号信号,因为2号信号被阻塞了,所以2号信号会一直在pending位图中。处理信号的动作叫做递达(Delivery),处理信号有三种方式,默认,忽略,自定义。

2024-06-06 11:26:12 570

原创 LINUX系统编程:核心转储

Core终止,在终止之后会生成,一个core文件,将进程在内存中的内核数据(与调试有关)转储到磁盘当中形成core文件,然后将进程干掉。在使用信号的时候,我们发现大多数的信号都是终止进程,虽然都是终止进程,但是终止进程是有两种方式的。我们发现core文件默认的最大大小是0,我们只需要将core文件最大大小修改一下,确保能够捕获足够的信息用于调试即可。如果每次启动就挂掉,会一直重启,一直挂掉,会生成很多的core文件,最后磁盘被打满,整个系统就挂了。当一个服务挂了,第一肯定是先恢复服务,然后在去解决问题。

2024-06-05 20:38:58 322

原创 LINUX系统编程:信号(1)

Linux让系统提供让用户给其他进程异步发送消息的一种方式。站在进程的角度看待信号1.信号在没被发送之前,进程是认识这个信号。2.在接受到信号之后,进程是被设定好去怎么处理这个信号的。3.信号到来的时候,如果进程有更重要的事情要做,这个信号会被临时保存。4.收到信号之后可以不立即处理,等到机会合适在处理信号。5.信号产生是不定时,随时都可能接受到信号,所以信号是异步发送的,是别的用户和进程。

2024-06-03 22:18:41 1155

原创 LINUX系统编程:信号量

信号量可以在多执行流下对,共享资源进行保护(例:共享内存本身是是一种共享资源,但是本身是不带协同机制的,这样就会出现数据不一致的问题,写端想写"hello world",但是在写端写到hello时,读端就把数据读走了,造成了数据不一致)。每个小区域都有自己的编号,当一个执行流想使用小块资源时必须,申请对应的编号,然后才能让问对应的小区域,使用完之后还要将编号还回去,这样保证每一块的小资源都是互斥访问的,大块的资源就是多个进程同步访问的。信号量是一种资源预定机制,只要预定了,资源在未来一定是该进程的。

2024-06-03 11:12:13 626

原创 LINUX系统编程:进程间通信之共享内存

ftok使用参数pathname和最少8个有效位的参数proj_id(必须不为0)生成一个key_t类型的System V IPC密钥,适用于msgget,semget,shmget这三个函数。size:就是开辟多大的共享内存,共享内存的开辟是以4kb为单位的,size的大小建议设置4*n,因为即使size为7,还是会开辟8个字节的内存,剩下的一个字节就浪费了。进程间通信的本质就是让进程看到同一块系统提供的资源,共享内存就是让不同进程看到同一块内存,用内存作为媒介,让两个不同进程通信。

2024-05-30 19:31:06 494

原创 LINUX系统编程:命名管道

匿名管道的通信只能在,有血缘关系的进程中,本质就是,子进程会拷贝一份父进程的文件描述符表,父子进程就可以看到操作系统的同一块资源(文件),以这块资源为媒介进行通信。命名管道,就是以路径为标识符,让两个不相干的进程,看到同一块资源(文件),以这个文件进行通信。

2024-05-24 20:48:47 555

原创 C++11:万能引用,完美转发,成员函数新功能。

在模版中,&&既可以接收左值,又可以接收右值。运行结果如果把模板去掉,prefectForward(a);会报错的确是两个都调用成功了,但是有一个问题,为什么prefectForward(10)参数明明是右值,为什么模板实例化的确实左值的版本?这是因为,右值引用右值,会把右值的属性变为左值,所以在调用fun的时候会调用左值版本。使用完美转发可以解决上面的情况。

2024-05-23 16:39:26 347

原创 LIUNX系统编程:进程池的实现

每一个可执行程序,在被执行前都要转化为进程,操作系统都要为其创建PCB,地址空间,页表,构建映射关系,进程池就是创建进程时,创建很多个进程,如果要执行程序,就直接使用创建好的进程。就像一次性创建5个进程,肯定是比一次创建一个创建5次的效率更好的,将任务均匀的分配给进程执行。

2024-05-06 20:23:47 444

原创 C++11:右值引用和左值引用

左值是一个具有长久生命周期,可以被赋值,可以被修改,取地址的表达式。以下都是左值int a = 0;右值是一个将亡值,快要死了生命周期很短,不能被修改,不能被取地址,一般都是临时对象,表达式的结果,函数返回值,字面常量。5;//5字面常量就是右值x + y;//结果就是右值fun1();//返回值是右值3.右值引用的意义对右值的引用,就是给右值取别名。1.左值引用解决了,传参的时候拷贝的问题。2.左值引用解决了,函数返回非局部对象拷贝的问题。

2024-05-04 16:21:59 637

原创 LIUNX系统编程:进程间通信-管道(2)

说明代码会把管道的数据读完,然后读到文件末尾,结束读取。说明读端关闭,os会终止写进程,使用信号13杀死写进程。同上,什么也读不出来,管道写满,写端阻塞等待。什么也没读出来,说明读端一直在阻塞等待。子进程的退出信号是13,

2024-05-03 21:59:55 220

原创 LINUX系统编程:进程通信-管道

pipe创建一个管道,这个单向数据管道被用于进程间通信,这个pipefd的数组(输出型参数)返回两个管道读端和写端的文件描述符,pipefd[0]是管道的读端,pipefd[1]是管道的写端。打开一个文件,操作系统都会给该文件分配一个文件描述fd,用读打开会分配一个fd,用写打开文件也会分配一个fd,但是这两个fd指向的是同一个文件。进程通信,肯定是不能直接是,进程和进程之间直接通信的,因为进程具有独立性,代码数据独立之间互不影响。管道的本质就是文件,一个进程向文件写入,一个进程读取文件。

2024-05-03 15:48:26 358

原创 腾讯云ubuntu新建用户后,命令行只显示$

这是因为,新建用户命令行解释器默认是sh,需要手动切换为bash,bash可以认为是sh的加强版本。所以我们只需要将,shell切换为bash就好了。将sh修改为bash。

2024-05-03 12:31:22 363

原创 LIUNX系统编程:动态库加载(2)

虚拟地址空间并不是有操作系统一个完成的,而是需要cpu,编译器,操作系统,三者共同联动,互相配合,编译器在编译的时候就已经生成虚拟地址了,CPU的MMU + 操作系统的页表才能讲虚拟的地址转化为物理地址。使用绝对编址的方式,对每个指令,进行编址,然后将链接了哪些动态库,main函数的地址(虚拟地址),代码区,数据区,已初始化的数据.....各个区域的大小划分,存储到到表头中。当指令寄存器,执行指令的时候,需要访问物理地址,这是时候cpu会给MMU一个请求,MMU区页表中查询,将虚拟的地址转化为物理地址。

2024-04-29 11:52:34 345

原创 LIUNX:系统编程动态库加载(1)

首先main想要运行,首先要为main创建task_struct和mm_struct,然后将main的代码和数据加载到内存,将main的代码通过页表映射到mm_struct的正文代码段,看这个程序运行是需要哪些动态库,将这些动态库加载到内存中,建立映射到共享区。我们发现一个文件的代码,数据,已初始化的数据,未初始化的数据的大小已经规定好了,这说明即使程序未被加载到内存,可执行程序已经存储好初始进程地址空间的数据了!对库的管理依旧采用面相对象,现描述再组织,抽离出库的主要信息,用链表连接起来。

2024-04-28 10:50:23 258

原创 LIUNX系统编程:动态库的制作

因为#include""的搜索规则是,在当前目录下寻找,找不到再去/usr/include中寻找头文件,但是我们的头文件在/mylib/inclue中,所以在编译的时候要指明我们的路径。但是我们并没有告诉操作系统,这个库的位置,所以在运行的时候,操作系统找不到这个共享库的位置。这个时候因为因为只在main编译的时候我们告诉编译器,这个库的位置,才得以编译通过。这样动态库就制作完了,还是需要对这个库进行一些包装,让库更规范一下。这种方式只建议比较成熟的库我们可以这么做,自己写的就不要瞎搞了。

2024-04-27 10:59:01 345

原创 LINUX系统编程:动静态库的制作

gcc -o main mian.c -L./ -lmymath //库的名字是去掉前缀和后缀的libmymath.a mymath。例:我写了一个函数,我想让别人使用,但是并不像让使用者看到我写的代码,就可以把我的代码制作成一个库,提供给使用者。ar -rc libmymath.a add.o sub.o//-rc(crate和replace)将add.c sub.c add.h sub.h 制作成静态库。1.首先要将add.c sub.c编译生成.o文件。使用-L选项制定库的路径,-l指定库的名称。

2024-04-26 20:42:24 195

原创 LINUX系统编程:软硬链接,动静态连接

什么是硬连接?,我们或许可以用操作清楚的认识什么是硬连接。在我们学习文件的时候这个数字好像完全没有被提到过,这个代表什么意思呢?这个代表该文件的inode编号与文件映射的次数,现在该文件的inode只与test.c这个文件名映射,所以该数字为1,这时候使用ln命令,给该文件建立一个硬连接文件。ln test.c test.a//为test.c建立名为test.a的硬连接看这两个文件的标号变为2,说明这两个文件使用的是一个inode编号,inode的编号一样说明他们本质上是一个文件。

2024-04-26 19:47:45 436

原创 LIUNX系统编程:文件系统

在用户使用LIUNX系统的时候,我们查找一个文件用的都是路径去查找,但是想找到一个磁盘的文件,必须找到该文件的inode,怎么用一个路径,就找到文件的inode的呢?依次类推,直到根目录,根目录的inode编号是确定的,系统是直接知道的,开机的时候,就会用这个编号,将根目录打开到内存当中,根目录的inode编号是2。想找到test.c,就必须找到先找到buffer目录的inode,想找到buffer目录的inode,就必须先找到code的目录的inode。

2024-04-21 18:41:01 328

原创 C++11统一列表初始化,initializer_list

initializer_list对象引用数组的元素,但是并不包含他们,拷贝一个initializer_list的对象,产生的另一个对象,底层引用相同的元素,不是新拷贝一份。vector之所以能向上面那样写,是因为c++11引入了initializer_list,用initializer_list作为参数,进行重载了一个构造函数。这个lt的类型就是 initializer_list,就是用来接收{1,2,3,4,5}。包含<initializer_list>头文件就可以使用initializer_list。

2024-04-20 16:56:27 495

原创 条款12:复制对象时勿忘记其每个成分

如果拷贝构造和赋值重载有重复的代码,就把共同的部分抽离的到一个函数中,调用这个函数就好了,把这个函数写成init和private的。这时候不要忘记修改拷贝构造和赋值重载,因为编译器不会对这种行为,发生任何的警告,但是拷贝构造和赋值重载已经是局部拷贝了。拷贝构造:这个时候基类对象会自己调用没有实参的默认构造(没有默认构造编译不过)。好的面相对象的系统会将内部封装,只留下赋值重载,和拷贝构造对 对象进行拷贝。当拷贝构造,赋值重载被调用时,会打印对应的信息。拷贝构造调用赋值就是对一个未初始化的对象进行赋值。

2024-04-20 10:35:26 272

原创 LIUNX文件系统

了解文件系统,首先要了解磁盘是如何存储和读取数据的。

2024-04-18 17:37:53 535

原创 LINUX系统编程:stdin的实现

主要就是根据打开方式,打开文件,创建一个myFILE,返回个myFILE*即可。5.刷新的策略,不同文件的刷新策略不同,显示器文件为行刷新,普通文件为全刷新。C语言的输入接口,都会向FILE*stdin里面的缓冲区打印。fflush把当缓冲区的内容,清空,拷贝到系统的缓冲区。向c语言缓冲区写入,再根据刷新策略,刷新到系统缓冲区。首先我们要封装的就是FILE结构体。1.一个C语言的缓冲区buffer。fopen是对open的封装。这个结构体大概含有以下内容。3.当前缓冲区的大小。

2024-04-15 09:11:56 197

原创 LINUX系统编程:缓冲区

缓冲区分成和先说结论,语言的缓冲区可以减少系统调用的次数进而提高向文件写入和读取的效率。向屏幕打印,无非就是向屏幕这个文件的缓冲区写入,然后在由操作系统刷新到显示器文件,这样显示器就可以显示内容了。直接调用系统调用,写到显示器文件的缓冲区,然后由操作系统刷新。

2024-04-14 20:54:55 268

原创 红黑树代码实现

enum Color//定义颜色red,black{}T _data;

2024-04-09 19:56:25 298

原创 AVL树的实现

int bf;//平衡因子, data(kv), bf(0){}

2024-03-21 16:04:01 331

原创 条款12:复制对象时勿忘记复制其每一个成分

public:{}private:在成员变量中,新增一个Transaction变量,切记要修改,构造函数和拷贝构造,如果不新增编译器不会报错,很难令人察觉。

2024-03-19 15:40:33 222

原创 条款13:用对象管理资源(智能指针)

来看看吧,没有收获把我腿打断。

2024-03-18 00:01:54 302

原创 条款11:operator=中处理自我赋值

相比第一种写法,这种写法的效率更高,判断是需要成本的,会使代码变大,引入一个新的控制流分支,二者都会降低运行速度。Widget&Widget::operator=( Widget rhs)//利用函数传值,是对原值的拷贝。正常来说自我赋值是一个很愚蠢的行为,但是有一些自我赋值其实是不太容易察觉的。swap(rhs);//将rhs和*this的数据交换。这种自我赋值会引起引起申请的资源在没有被使用完,就被释放的错误。*px = *py //px py指向同一块内容就是自我赋值。//不安全的operator。

2024-03-16 23:07:53 380

原创 条款09:绝不在析构和构造中调用virtual函数

上述代码的本意是,每当进行买入或卖出时,都会创建买入的操作日志或者卖出的操作日志但是当BuyTransaction对象b创建的时候,会先调用Transaction的构造函数,构造函数去调用logTransaction(),因为这个时候BuyTransaction对象的成员全部未初始化,如果调用BuyTransaction的成员函数,势必要使用BuyTransaction的成员变量,使用未初始化的变量,C++肯定会制止这种行为。

2024-03-16 22:25:32 271

原创 条款07:为多态基类声明virtual析构函数

声明为虚函数,结构中会多存一个虚表指针vptr,在64bit的机器上,如果有两个对象是两个int+vptr可能会占用64~128bit,使其不能直接塞入64-bit的缓存器。我们用一个基类的指针,指向一个派生类的对象,这个时候,对基类的指针执行delete,会出现只有基类的部分删除,造成内存泄漏。加上virtual之后就会变成多态调用,调用delete会调用派生类的析构函数,再调用基类的析构函数。加上虚表指针之后,C++的对象,不在和其他语言具有相同的结构,因为其他语言没有虚表指针。

2024-03-12 23:09:22 385

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除