自定义博客皮肤VIP专享

*博客头图:

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

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

博客底图:

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

栏目图:

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

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

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

原创 fork、exec 踩坑记录

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

2024-05-22 21:54:37 345

原创 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 686

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

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

2024-05-18 12:28:52 726

原创 链表算法题总结

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

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

原创 堆排序算法

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

2024-04-20 17:23:31 336

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

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

2024-04-20 16:10:12 839

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

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

2024-04-13 14:00:30 914

原创 linux tasklet

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

2024-04-02 18:18:55 1063

原创 linux 软中断

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

2024-03-31 22:01:53 1361

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

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

2024-03-31 17:42:41 290

原创 linux 内核模块入门

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

2024-03-31 13:09:31 1102

原创 [c++] 自写 MyString 类

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

2024-03-26 08:20:17 604

原创 [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 697

原创 [c++] 通过汇编代码确定虚函数调用

如下代码,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 880

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

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

2024-03-23 11:59:15 1553

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

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

2024-03-22 17:16:30 1109

原创 [c++] lambda 表达式

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

2024-03-17 13:51:27 1082

原创 [c++] std::future, std::promise, std::packaged_task, std::async

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

2024-03-16 20:29:32 1586

原创 [c++] 右值引用

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

2024-03-13 11:20:31 1063

原创 [linux] socket 非阻塞模式使用注意事项

在使用 socket 的一些 api 的时候,默认情况下都是阻塞模式。比如使用 tcp socket 时,客户端调用 connect() 创建连接,connect() 返回的时候要么是创建连接成功了,要么是出现了错误,反正 connect() 返回的时候结果是确定的;tcp 服务端使用 accept() 接收连接的时候,accept() 返回的时候一般就是接收到了新的连接;使用 recv() 接收数据的时候,直到接收到数据之后,才会返回;

2024-03-12 21:09:48 1030

原创 实例观察 linux 内存懒加载 和 写时拷贝

linux 中写应用程序的时候,使用 malloc() 申请的内存,比如使用 malloc() 申请了 1MB 的内存,系统是立即分配了内存吗?linux 中用户态的内存是懒加载的,不是申请之后就立即分配,而是在第一次访问的时候才会分配。(1)避免内存资源浪费,如果应用申请了内存但是一直没有使用,如果内存是立即分配的话就会导致很多内存资源浪费。懒加载类似于单例设计模式中的懒汉式。(2)减少初始化开销,提升应用启动速度。在进程启动的时候,不需要立即给所有的虚拟内存分配物理内存,这样可以减少初始化开销。

2024-03-12 10:43:44 1361

原创 [c++] 查表 —— 策略模式和职责链模式的核心

查表法在工厂模式、策略模式以及职责链模式中都有使用。以工厂模式为例,表中存储的数据,key 是商品的类型,value 是生产这个商品的工厂。在生产商品的时候,直接根据商品类型从表中获得商品对应的工厂,然后通过工厂生产商品。如果没有表的话,那么类型判断和工厂实现都在一块,代码耦合度高,通过查表法可以对代码进行解耦。

2024-03-10 22:57:04 891

原创 [c++] 模板模式 —— 毕业设计论文模板

在上边的例子中,thread_entry() 就是一个模板方法,其中对 init(),should_stop(),do(),prepare_stop() 进行了调用。thread_entry() 的实现是不变的,是一个模板。init(),should_stop(),do(),prepare_stop() 需要派生类来实现,用户不需要关心这几个函数是怎么调用的。其中 init(),should_stop(),do(),prepare_stop() 均声明为纯虚函数,也就是需要子类来实现的函数。

2024-03-10 18:36:21 369

原创 [linux][调度] linux 下如何观察线程调度延时 ?

linux 内核提供了一个内核观测工具 tracepoint。关于 tracepoint,可以参考如下博客。[linux][观测] ftrace,kprobe,tracepoint 入门简单来说,tracepoint 就是内核在一些关键位置进行了埋点,当这些关键事件发生的时候,会打印一些信息。比如在内存管理方面,有 kmalloc 相关的埋点,在申请内存或者释放内存的时候,可以打印一些信息;在 tcp 协议栈中也有一些埋点,当 tcp 发生重传的时候,或者收到 rst 报文的时候,也可以打印对应的信息;

2024-03-10 17:08:46 1168

原创 [c++] 模板

c++ 中的模板通过将类型参数化,可以提高代码的复用性。模板并不能减少代码量,只是从开发者的角度来看,代码量减少了,复用性提高了;从二进制文件的角度看,代码量没有减小。

2024-03-07 17:37:59 529

原创 [c/c++] const

const 和 #define 的区别?const 和指针一块出现的时候,到底谁不能修改?const 和 volatile 能同时修饰一个变量吗?const 在 c++ 中的作用?

2024-03-06 10:53:56 1018

原创 [c/c++] 结构体对齐

在 c 语言中,结构体的大小并不是结构体每个成员的大小之和,结构体的大小往往比结构体的成员大小之和要大。如下结构体,每个成员的大小分别是 1、4、1,但是结构体的大小却不是 6,而是 8。char a;int b;char c;自然对齐:如果一个数据的内存地址正好是这个数据大小的整数倍,那么说这个数据是自然对齐的。比在 64 位机器上,一个 int 类型的数据地址是 4 的整数倍,一个 long 类型的数据地址是 8 的整数倍,那么说这两个数据是自然对齐的。

2024-03-04 12:06:13 616

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

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

2024-03-04 09:22:27 1498

原创 [c++] c++ 中的顺序(构造,析构,初始化列表,继承)

对象构造的时候,对象成员变量的初始化顺序是什么样的?派生类构造的时候,先构造基类还是先构造派生类?构造函数中的初始化列表,初始化的顺序是列表的顺序吗?析构的时候,析构的顺序是什么样的?本文通过实例代码记录上述场景下的顺序。先说结论:(1)全局对象以及静态全局对象,在 main 函数调用之前创建(2)构造时,先构造类中的成员变量,再调用构造函数(3)类中成员变量的构造顺序是变量在类中声明的顺序(4)初始化列表中,成员变量的初始化顺序也是变量在类中声明的顺序。

2024-03-03 13:25:00 1107

原创 [linux] linux 常见信号以及使用信号时注意事项

信号相对于用户态的线程,类似于中断相对于内核态的线程。在内核态做开发时,如果数据会被线程和中断同时访问,在线程中我们需要使用关中断自旋锁,在中断处理函数中需要使用自旋锁。这样在线程访问数据的时候,中断是关闭的,所以这个时候中断也上不来,不会打断线程的执行。

2024-03-02 20:45:23 1093

原创 [c++] 继承和多态整理二

从实际使用中,纯虚函数有一个优点,假如一个基类中的函数,派生类中必须实现自己的逻辑,而不能使用基类中的逻辑,那么就可以使用纯虚函数,这样在派生类中如果忘记实现了,那么编译器就会提示错误,起到了一个约束的作用;① 使用 Base 类型的指针或者 Base 类型的引用,当指针指向 Derived 对象的时候,调用 func1(),不管传参是 int 类型还是 double 类型,都是调用的 Base 中的 func1()。派生类的构造函数中调用虚函数,调用的是基类的虚函数还是调用的自己的虚函数。

2024-03-02 14:30:17 977

原创 [c++] 继承和多态整理一

如下代码,有一个基类 Base,Derived1,Derived2,Derived3 3 个子类继承了基类 Base,分别是 private 继承,protected 继承,public 继承。在 main 函数里,分别使用 new 来创建 3 个子类,将 3 个子类指针赋值给 Base 指针,private 和 protected 继承的时候,子类指针无法赋值给父类指针。private 继承或者 protected 继承下,父类的属性和函数在子类中的权限发生了变动。

2024-03-02 10:58:23 1023

原创 [linux][xdp] xdp 入门

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

2024-02-28 18:08:19 1751

原创 [c++] 多态,虚函数,虚函数表

封装,抽象,继承,多态是面向对象编程语言的特点。个人认为抽象和继承都是手段,多态才是目标,继承是多态的基础。(1)将属性和函数封装到一个类里边,属性和函数组成一个完整的对象(2)权限管理,public, private, protected, 增强安全性(3)暴露有限的接口,增强了易用性,增强了安全性(1)抽象这个概念存在于很多场景,我们使用函数,本身就是通过函数隐藏具体的实现细节,这也算抽象(2)c++ 中的抽象,可以使用抽象类来认识,抽象类是包含纯虚函数的类,抽象类不能创建对象。

2024-02-28 11:40:35 993

原创 [linux] linux 下怎么定位内存泄漏 + free,buddyinfo

正常情况下,软件使用的内存是随着时间在一定范围内波动的。内存泄漏说的是随着时间的推移,进程使用的内存越来越多。一般情况下,内存泄漏是程序不断地申请内存,但是没有释放导致的。

2024-02-27 14:55:33 1190

原创 [c/c++] static 关键字

1 static 修饰变量从修饰的对象来看,static 可以修饰局部变量,也可以修饰全局变量,可以修饰函数;可以修饰类中的成员变量以及成员函数。从生命周期的角度来看,static 修饰的对象的生命周期,与进程的生命周期是一致的。从作用域的角度来看,局部静态变量的作用域是函数内,全局静态变量的作用域是模块内;类中的静态成员变量和成员函数的作用域,能访问到类的地方都可以访问到静态成员,类中的静态成员同时也受 public,private,protected 权限修饰符的影响。

2024-02-27 10:47:59 999

原创 大端,小端;网络序,主机序

在网络编程中,特别是底层网卡驱动开发时,常常遇到字节序问题。字节序指的是多字节数据类型在内存中存放的顺序,高位保存在低地址还是高地址,以此来划分大端还是小端。

2024-02-26 20:31:37 1140

原创 [c++] 智能指针(shared_ptr, weak_ptr, unique_ptr)

c++ 中的智能指针让 c++ 看起来像 java,go 这种带 GC 的语言,但和 GC 又不完全相同。c++ 中的智能指针使用引用计数,当引用计数减为 0 的时候就会立即释放资源,释放资源具有实时性;而像 java,go 这样的 GC 语言,当对象不被引用时,可能不会立即释放资源,资源的释放有滞后性。如果不使用智能指针,需要开发者使用 new 和 delete 创建和销毁对象。使用智能指针,开发者不需要手动来释放资源,当引用计数变为 0 时自动释放。就这一个好处。

2024-02-26 09:55:59 782

原创 [c++] char * 和 std::string

实例代码如下图所示,s1 和 s2 均是常量字符串,字符串常量保存在只读数据区,是只读的,不能写,代码中注释的那两行代码会导致段错误。s3 是字符数组,字符数组是可以修改的,std::string 类型的字符串也是可以修改的。代码运行之后,可以看到 s1 和 s2 是常量字符串,这两个指针指向的地址也是相同的。从 s1,s2 的地址和 s3,s4 的地址对比可以看出,s3 和 s4 的地址相距比较近,和 s1、s2 的地址相距比较远。

2024-02-25 20:55:59 677

原创 [c++] 深拷贝和浅拷贝,拷贝构造、赋值运算符

拷贝构造在如下场景会被调用:(1)函数调用时,函数参数是对象的值传递(2)声明对象同时初始化的时候(而不是声明和初始化分开,因为声明的时候就创建了对象)(3)函数返回的时候,返回对象的值。第 3 种情况,默认情况下有返回值优化,不会调用拷贝构造函数。通过编译参数 -fno-elide-constructors 可以禁用返回值优化。拷贝构造函数的形参必须是引用传递,如果是值传递的话,那么在传递过程中也会调用拷贝构造函数,这样就造成递归调用。如果是值传递,会有编译错误。

2024-02-25 17:10:38 891

空空如也

空空如也

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

TA关注的人

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