自定义博客皮肤VIP专享

*博客头图:

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

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

博客底图:

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

栏目图:

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

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

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

原创 性能优化随笔(一)

在软件开发过程中,一般要先实现功能方面的需求,功能方面的需求开发完毕之后,往往会考虑性能方面的优化。在业务发展的初期,性能往往能满足使用的需求,这时性能优化不是必不可少的。随着业务的发展,软件复杂度的提高,性能有时会成为瓶颈,这时性能优化是必须要做的工作。

2024-06-02 20:43:00 1635 2

原创 使用 kprobe 观察 tcp 拥塞窗口的变化

tcp 中拥塞窗口用来做拥塞控制。在发送侧,要发送数据的时候会基于拥塞窗口进行判断,当前这个包还能不能发送出去。tcp 发包函数是 tcp_write_xmit(),在这个函数中调用 tcp_cwnd_test() 来判断当前拥塞窗口让不让发包。从 tcp_cwnd_test() 函数能看出来,in_flight 是已经发送出去,但是还没有被确认的包,如果这个数大于拥塞窗口,那么就返回 0,不能发包了,因为没有确认的包太多了,再发送可能加重网络拥塞,所以就返回 0。

2024-03-04 09:22:27 1541

原创 xdp 入门

xdp 全称 eXpress Data Path,是 linux ebpf 中的一个功能。ebpf 在内核中预留了一些插入点,用户可以在这些插入点插入自己的处理逻辑,当数据路过插入点时可以做一些预期的处理,具体实现方式如下:① 用户编写数据处理代码,也就是对于路过这个插入点的数据想做什么处理② 将代码编译③ 将编译好的目标文件安装到插入点安装之后,数据路过插入点时便会被安装的代码处理。插入点的处理逻辑就像一些路口的收费站,不同身份的车辆通过收费站的时候,可能需要做不同的事情。

2024-02-28 18:08:19 2217

原创 SCHED_FIFO 和 SCHED_RR 同时存在时,如何调度

(1)从上边的实验现象可以看出来,实验现象符合 SCHED_FIFO 先入先出,SCHED_RR 时间轮询的调度原理。(2)在实时调度策略中,高优先级具有绝对的优先调度的的特权,即使 SCHED_RR 这种时间片轮询的调度算法,也是说的多个 SCHED_RR 的优先级相等的前提下才会轮询,如果优先级不相等,那么只会调度优先级最高的那个线程。

2024-01-19 21:03:55 1465

原创 带着 6 个问题深入理解 epoll

epoll 是一种多路复用技术。多路复用技术即一个线程可以监听多个文件句柄的的事件,多路指的是多个句柄,复用是指多个句柄复用一个线程资源。可想而知,和多路复用技术相对的就是一个文件句柄使用一个线程来监听。多路复用技术适用于高并发的场景。除了 epoll, linux 中的多路复用技术还有 poll 和 select。nginx, libevent, golang net 库中均使用了 epoll。

2024-01-13 16:09:11 929

原创 linux 中进程的 D 状态和 Z 状态

僵尸态是 linux 进程的一种状态,用 Z (zombie) 表示。处于 Z 状态的进程已经不在工作,进程的资源(内存,打开的文件) 都已经释放,只保留 struct task_struct 一个空壳子,用僵尸来表示这个状态非常形象。僵尸进程不能被信号杀死(因为僵尸进程已经死了,当然也不能响应信号),只能被父进程回收。进程处于僵尸态时保存的信息非常少,其中包括进程号,退出码,退出码是比较重要的,父进程回收僵尸进程的时候可以根据退出码确定子进程的退出原因。

2024-01-13 11:05:54 2530

原创 linux中可执行文件为什么不能拷贝覆盖

如下的代码,使用 gcc hello.c -o hello, gcc hello.c -o hello1分别编译出两个可执行文件hello和hello1,然后执行hello,再执行cp hello1 hello,这样会报错。如果可执行文件没有被执行,那么是cp命令是可以执行成功的。可执行文件被执行的时候,通过execve系统调用来进行。对于一个普通的文件,假如有两个文件,分别是file和file1,我们使用 cp file1 file的方式使用file1的内容来覆盖file的内容,这样是可以的。

2024-07-07 10:55:57 236

原创 linux软链接和硬链接的区别

如下图所示,一开始有两个文件soft和hard。使用 ln -s soft soft1创建软链接,soft1是soft的软链接;使用ln hard hard1创建硬链接,hard1是hard的硬链接。可以看到软链接的文件类型和其它3个文件的文件类型是不一样的,软链接的文件类型是l开头的。

2024-07-07 10:08:18 373

原创 leetcode判断二分图

图的问题肯定要用到深度优先遍历或者广度优先遍历,但又不是单纯的深度优先遍历算法和广度优先遍历算法,而是需要在遍历的过程中加入与解决题目相关的逻辑。题干中说了,这个图可能不是连通图,这个提示有什么作用呢?很多时候我们接触的题目,图都是连通的,对于连通图来说,从一个点开始遍历,不管是深度优先还是广度优先,遍历一遍就可以把图中的所有点都遍历到;而对于非连通图来说,从一个点开始遍历,就无法将所有点都遍历一遍,这就需要针对每个点都进行一次遍历。

2024-07-06 14:21:22 320

原创 cgroup memory使用超过限制会怎样?

cgroup可以对一个进程或者一组进程使用的资源进行限制,可以限制的资源包括cpu、memory、io等。其中memory可以对内存资源进行限制,比如我们限制进程所能使用的内存最大是1G,那么当进程已经使用了1G的内存的时候,这个时候进程再申请内存会怎么样呢?内存使用超过限制的时候有如下两种结果:(1)默认情况下,内存使用超过限制,会oom(out of memory),oom的时候系统会使用SIGKILL信号将进程杀死。(2)cgroup中也可以进行配置,oom的时候不杀死进程,此时进程进入D状态。

2024-07-06 10:56:31 947

原创 leetcode LRU 缓存

所以需要使用链表来表示缓存,当访问一个数据的时候需要将当前这个数据从原来的位置上删除,然后加到链表的头部,删除一个节点的话,需要将这个节点的前一个节点和下一个节点连接起来,如果使用单向链表的话,那么只能找到这个节点的下一个节点,找不到上一个节点,所以需要使用双向链表。(6)向缓存中放入数据 1,此时缓存是满的,所以需要先淘汰一个数据,从访问时间来看,最后一次访问 50 的间隔时间最长,也就是排在最右边的数据,所以将 50 淘汰,其它数据向后移动,将新数据 1 放入最左边的位置。

2024-06-15 08:36:21 496

原创 明朝那些事摘记-历史的主人

本人还是更认可前半句,历史的发展不以人的意志为转移,有其内在的规律,所有世上发生的事情,什么人在什么时间做了什么事,或者哪朵花在哪个时间盛开了,也许都是背后的规律确定好的。历史真正的主人是历史本身。他相信,就如同以往一样,他会胜利的,事情是会按照他的设想进行下去的。它没有去招惹朱元璋这位猛人,去不断地缠绕着朱元璋的子孙,而朱元璋的体系也在时间的磨砺下变得千疮百孔,最终失去控制力和约束力,真正支配历史的人,不是朱元璋,是稻田里辛勤劳作的老农,是官道上来往的商贾,是朝堂上进言的官员,是孤灯下苦读的学子。

2024-06-12 21:54:18 260

原创 error while loading shared libraries 找不到动态库问题如何解决

在使用 c 或 c++ 开发应用时,在启动程序时,有时会遇到这个错误,找不到动态库。这个时候,我们使用 ldd 来查看,发现可执行文件依赖的动态库显示为 not found。

2024-06-10 11:04:58 631

原创 system 和 exec 的区别

在 linux 中,使用 system 和 exec 都可以执行一个程序或者执行一个命令。两者的区别如下:system 中创建了一个子进程,在子进程中执行用户的命令,子进程执行完毕之后,system 会返回。exec 不会创建子进程,而是直接用 exec 要执行的进程来代替当前的进程,并且如果在执行过程中没有出现错误,那么 exec 是不会返回的,调用 exec 的进程永远也回不来了。

2024-06-09 19:44:03 919

原创 leetcode 数组排序

leetcode:排序算法包括基础的选择排序、插入排序、交换排序;也包括快速排序,堆排序,归并排序等。其中快速排序是交换排序的升级版;堆排序是选择排序的升级版。那插入排序有没有升级版呢,也是有的,是希尔排序,但是希尔排序的思想不好理解,所以本文中不涉及希尔排序。选择排序,插入排序,交换排序属于基础排序算法,时间复杂度是O(n * n)。快速排序和堆排序的时间复杂度是 O(nlogn)。

2024-06-09 12:36:23 938

原创 leetcode 所有可能的路径(图的遍历:深度优先和广度优先)

对于无向图来说,图中的边没有方向,两个节点之间只可能存在一条边,比如 0 和 1 之间的边,因为是无向图,这条边可以表示从 0 到 1 的边,也可以表示从 1 到 0 的边。当对图做遍历时,如果图是连通的,那么从一个节点开始,遍历一次,就能将图中所有的节点遍历一遍,所以遍历一次就可以了。连通图,指的是从一个节点出发沿着边进行遍历,能把图中的节点都遍历到的图。有环图说的是从一个节点开始遍历,在遍历过程中还能遍历到这个节点的图,除了开始节点和结束节点是相同的,其它节点不能重复出现,并且路径长度大于 2。

2024-06-08 14:05:29 953

原创 回文串算法题

回文串是一个正着读和反着读顺序一样的字符串。"aba" 是回文串,"abba" 是回文串,"abc" 不是回文串。回文串的题目,都要使用一个基本的逻辑,就是判断当前这个字符串是不是回文串。以 c++ 为例,代码如下。这种方法也可以称为双指针法,两个指针从字符串的两端向中间遍历每个字符,如果中间发现两个字符不相同,则不是回文字符串;遍历到最后,说明是回文串。双指针法,在其它数据结构题目中也会用到,比如链表中会用到快慢指针,也属于双指针。

2024-06-02 10:47:38 1014

原创 阻塞、非阻塞、同步、异步

异步机制,在实际工作中,往往是用户向异步实现模块注册回调,线程发送请求并注册回调之后,就交给底层的异步模块来负责,线程就不用关心了,有事件到来之后,异步调度模块会自动调用用户注册的回调。领导给你下发任务之后,并不是只盯着你的工作,其它什么工作都不做了。而是给你安排任务之后,就去做其它工作了,但是领导过一会就来问你,工作完成了没有,直到你工作完成,就不来问你了。领导给你下发了任务,下发之后,领导什么事都不做了,而是坐在你的工位旁边,一直监督着你来工作,直到你完成工作,领导才回去做其它的工作,这就是阻塞模式。

2024-06-01 22:04:51 604

原创 linux 内核哪种锁可以递归调用 ?

当数据被多线程并发访问(读/写)时,需要对数据加锁。linux 内核中常用的锁有两类:自旋锁和互斥体。在使用锁的时候,最常见的 bug 是死锁问题,死锁问题很多时候比较难定位,并且影响较大。本文先会介绍两种引起死锁的原因,对比自旋锁和互斥体的区别,最后记录一下可以递归调用的锁。本文通过内核模块来展示锁的使用。锁保护的是数据,不是代码。数据在代码中要么是一个变量,要么是一个数组,一个链表,红黑树等。

2024-06-01 18:59:41 896 2

原创 c++ 虚函数常见问题

如下代码,编译之后,使用 readelf -t a.out 可以查看看到这一段的大小是 0x68,如果从代码中删除一个虚函数,比如把函数 Do2() 不用 virtual 修饰,可以看到这个段的大小减小了 16。虚函数表需要在类定义的时候就要确定,对于函数模板来说,类定义的时候,这个函数有多少实例是不确定的,所以函数模板不能定义为虚函数。但是类模板中的函数可以声明为虚函数,因为对于类模板来说,在定义一个实例的时候,这个类的定义就是确定的,虚函数表的大小也是确定的。构造函数声明为虚函数,编译错误如下。

2024-05-26 16:35:27 289

原创 用队列实现栈,用栈实现队列

有两个地方会讨论到栈,一个是程序运行的栈空间,一个是数据结构中的栈,本文中讨论的是后者。栈是一个先入后出,后入先出的数据结构,只能操作栈顶。栈有两个操作,push 和 pop,push 是向将数据压栈,pop 是将数据出栈。栈还有一个操作 top,这个操作可以查看栈顶的元素,不会出栈。队列是一种先入先出,后入后出的数据结构。

2024-05-25 11:18:22 856

原创 fork、exec 踩坑记录

在 linux 中,使用 fork 创建的子进程,子进程共享着父进程的资源,这些资源包括内存,信号,打开的文件等。就如同刚出生的小孩,和父母共享着家里的房子,存款等。(1)内存不需要我们特别关心,因为系统的写时拷贝保证了,在写的时候,父子进程的内存是分离的。(2)信号需要我们注意,在 fork 之后,子进程和父进程的共享着信号处理函数,如果不想共享,那么可以使用 signal 来修改信号的行为。

2024-05-22 21:54:37 554

原创 linux 中 fd 申请和释放管理(两级 bitmap)

通过上边的文章,我们可以知道,在 linux 中,fd 有以下几点需要了解:(1)fd 表示进程打开的文件,是进程级别的资源,不是系统级别的资源(2)struct task_struct 在内核中用于描述一个进程,其中打开的文件使用 fd table 来描述(3)在用户态看 linux,一些皆文件(4)一个进程可以打开的文件个数是有限制的,使用 ulimit -a 可以查看可以想象,如果让我们自己来实现的话,我们会选择一个 bitmap 来维护 fd 的被使用情况。

2024-05-20 22:28:21 857

原创 背包问题(01背包和无限背包)

假设有一个背包,体积是 V,另外有 n 个物品,物品的体积分别是 v1, v2, ... vn,每个物品的价值是 w1, w1, ... wn。求怎么将物品放到背包里,才能使背包中物品的价值最大?背包问题是一个典型的动态规划问题。动态规划问题中经常包含一个最字,比如最大价值?最短路径?动态规划问题的求解思路包括以下几点:(1)只看眼前利益动态规划,关键字是动态,也就是说结果是在变化的。在计算过程中,只看眼前利益,只要当前这种情况满足要求,那么这就是中间的一个结果。下边的代码是找数组的最大值。

2024-05-18 12:28:52 879

原创 链表算法题总结

链表作为一个基础的数据结构,在开发中经常被使用。链表往往使用如下的数据结构来表示,struct node 表示链表中的一个节点,data 表示节点的数据;next 表示这个节点指向的下一个节点,如果是链表的尾节点,那么 next 是空。在使用链表的时候,head 指针是需要维护的,head 是链表的头节点。在使用中,head 可以包含具体的数据,表示链表的第一个节点;head 也可以不包含数据,head->next 表示链表的第一个节点。

2024-05-01 06:27:47 750 1

原创 堆排序算法

堆是一棵二叉树,有以下几个约束条件:(1)用数组表示(2)是一棵完全二叉树所谓完全二叉树就是用数组表示的时候,孩子节点的下标和父节点的下标满足如下的关系:下标从 0 开始。下图中,左边的二叉树是完全二叉树,右边的二叉树不是完全二叉树。(3)分大顶堆和小顶堆如果子节点的数值都不大于父节点,那么堆是大顶堆。如果子节点的数值都不小于父节点,那么堆是小顶堆。下图中,左边是大顶堆,右边是小顶堆。

2024-04-20 17:23:31 359

原创 napi —— linux 网卡驱动收包机制

linux 操作系统一般指 linux 内核。在 linux 上开发应用的时候,可以使用 linux 提供的系统调用。linux 内核管理着机器上的硬件资源:内存,磁盘,网卡等。开发应用的时候不能直接操作这些硬件,而只能通过系统调用来使用这些资源。linux 系统调用可以说是 linux 内核提供给上层的一些接口。这种内核和用户隔离的机制保证了安全性的同时,也简化了应用的开发。linux 内核除了给上层应用提供了接口之外,给底层的硬件驱动也提供了框架和机制。

2024-04-20 16:10:12 1102

原创 二叉树遍历算法和应用

二叉树是指度为 2 的树。它是一种最简单却又最重要的树,在计算机领域中有这广泛的应用。二叉树的递归定义如下:二叉树是一棵空树,或者一棵由一个根节点和两棵互不相交的分别称为根节点的左子树和右子树所组成的非空树,左子树和右子树同样都是一棵二叉树。在二叉树中,每个节点的左子树的根节点被称为左孩子节点,右子树被称为右孩子节点。二叉树通常使用如下数据结构来表示:data 表示二叉树节点的数值,left_child 表示节点的左孩子节点,right_child 表示节点的右孩子节点。

2024-04-13 14:00:30 977

原创 linux tasklet

软中断、tasklet 以及工作队列,均是 linux 中将任务推后执行的机制。其中工作队列与用户态使用的线程池类似。什么是任务推后执行呢?可以借助于开发应用时经常使用的线程池来理解。任务推后执行,就是任务本该执行的时候没有立即执行,而是将任务放到任务容器(标志位或者任务队列)中,相当于生产者;任务容器还有消费者,消费者从容器中取出任务来执行。这就是推后执行,其中有 3 个组成元素,生产者,任务容器和消费者。

2024-04-02 18:18:55 1107

原创 linux 软中断

在 linux 中,任务执行的载体有很多,包括线程,中断,软中断,tasklet,定时器等。。软中断和 tasklet 的执行可能在中断中,也可能在线程中,定时器的执行可能在中断、软中断或者线程中。在讨论中断和软中断的时候,经常以网卡收包为例子来理解。如下是网卡收包的主要环节,分别说明每个环节:① 网卡从链路上收包,网卡的作用是成帧,网卡在链路上收到的是字节流,根据前导码,帧开始界定符,帧间隙来界定一个帧,这就是成帧。② 网卡界定一帧报文之后,将这一帧报文通过 dma 保存到内存中。

2024-03-31 22:01:53 1370

原创 c++ 共享指针简化版实现

(2) 共享指针中有一个计数,这个计数标志着有多少指针在共享当前这个对象。共享计数需要使用一个指针申请的空间来表示,这样方便在多个共享指针之间共享;如果直接使用一个变量,那么不便于在多个共享指针间共享。③ 析构的时候,引用计数减 1,这个时候判断引用计数是不是 0,如果是 0 的话,就将对象析构。(1) 共享指针是一个模板,通过模板参数指定共享指针关联的对象。② 拷贝构造或者赋值的时候,引用计数增加 1。① 构造时,引用计数初始化为 1。

2024-03-31 17:42:41 324

原创 linux 内核模块入门

内核模块可以动态地被安装到内核,从而扩展内核的功能,使用内核模块时不需要重新编译内核。内核模块常用的场景是驱动,随着芯片种类的增加,硬件种类的增加,这些芯片或者硬件(比如网卡) 的驱动可以以模块的方式进行开发,这样可以在需要的时候加载对应的模块,不使用的时候卸载模块,不至于因为芯片或硬件的种类的快速增加而导致内核镜像的膨胀。使用 lsmod 可以看到当前系统中安装的内核模块。字符设备,块设备,网络设备,文件系统等都可以通过内核模块的方式来安装。

2024-03-31 13:09:31 1122

原创 [c++] 自写 MyString 类

比如下边代码 MyString 类中重载了 、==、!=、 运算符,那么使用这些运算符操作对象的时候,便会使用对象重载之后的运算符。实现了 MyString 类,同时实现了运算符重载,重载的运算符包括 、==、!=、、[] 等。如下代码,、==、!= 声明为成员函数,当成员函数和友元函数同时存在时,优先选用成员函数。(2) 流操作符,需要声明为友元函数,不能声明为成员函数。如下代码,、==、!=、 均声明为友元函数。

2024-03-26 08:20:17 686

原创 [c++] c++ 中的一些关键字:explicit, delete, default, override, final, noexcept

explicit 的意思是清楚的,明显的。一般用在类的构造函数中,防止隐式转换。如下代码,(1) 类 A 的两个构造函数都没有使用 explicit 修饰,所以如下两行代码,隐式转换,是允许的A a1 = 1;(2) 类 B 的两个构造函数都使用 explicit 修饰了,不允许隐式构造,所以下边两行代码编译不通过B b1 = 1;(3) 类 B,如下代码,使用强制类型转换,是可以的。

2024-03-23 21:01:48 723

原创 基类构造函数和析构函数中调用的虚函数是基类的还是派生类的 ?

如下代码,Base 是基类,其中有 3 个虚函数 Do1(),Do2() 和 Do3(),另外还有一个非虚函数 Do()。Derived 是继承 Base 的派生类,在 Derived 中覆写了 Do1(),Do2() 和 Do3()。在 main() 函数中创建了一个 Derived 对象,并把指针赋值给 Base 类型的指针 b。使用 b 调用 Do1(),Do2() 和 Do3() 的时候,我们都知道调用的是 Derived 中的函数。

2024-03-23 16:25:20 902

原创 linux 内核抢占入门 —— 高优先级线程被唤醒时会立即抢占当前线程吗 ?

在支持抢占的内核中,如果高优先级的线程被唤醒的时候,这个时候 cpu 被其它线程占用着,并且正在运行的这个线程的优先级比刚被唤醒的这个线程优先级低。在内核抢占中,有两种类型的点,一个是检查点,一个是抢占点。在检查点的地方会做检查,如果需要抢占,那么会设置一个需要抢占的标志,但是在检查点的时候不做真正的抢占;真正的抢占是在抢占点,抢占点会判断检查点中设置的标志,如果需要抢占并且允许抢占的话,那么就会进行抢占调度。

2024-03-23 11:59:15 1648

原创 [linux][调度] 内核抢占入门 —— 线程调度次数与 CONFIG_PREEMPTION

当打开内核抢占时,也就是定义了 CONFIG_PREEMPTION 这个宏。那么打开这个宏的时候,具体定义了那些内容呢?本人使用的源码版本是 5.10.186。

2024-03-22 17:16:30 1168

原创 [c++] lambda 表达式

在 c++ 中,一个可以执行的任务有多种形式:函数,lambda 表达式,std::function 对象,std::bind 绑定的任务,std::packaged_task 封装的任务。

2024-03-17 13:51:27 1094

原创 std::future, std::promise, std::packaged_task, std::async

std::promise 进程间通信,std::packaged_task 任务封装,std::async 任务异步执行;std::future 获取结果。

2024-03-16 20:29:32 1652

原创 [c++] 右值引用

在说右值引用之前,需要先说一下左值引用。左值引用类似于 c 语言中经常使用的指针,c++ 引入左值引用,在一定程度上可以代替指针。什么是左值,简单来说,左值是等号左边的值,是可以取地址的。左值引用就是左值的别名,左值引用和左值指向同一块内存;左值引用就像人的小名,指的是同一个人,这个人长高了 5cm,那么说这个人的大名和小名的时候,都长高了 5cm。如下代码,是左值引用的例子:(1)左值引用需要在声明的时候初始化(2)左值引用不能指向一个右值,但是 const 左值引用可以用一个右值进行初始化。

2024-03-13 11:20:31 1070

空空如也

空空如也

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

TA关注的人

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