借助内核逻辑锁pagecache到内存
一、背景
内存管理是一个永恒的主题,尤其在内存紧张触发内存回收的时候。系统在通过磁盘获取磁盘上的文件的内容时,若不开启O_DIRECT方式进行读写,磁盘上的任何东西都会被缓存到系统里,我们称之为page cache。可以想象,如果这样的行为持续,且如果我们持续地不断要访问磁盘上新的文件时,那么page cache就会一直增长,page cache毕竟也是占用物理内存的,所以物理内存终有一天还是会不够的。
针对这种场景,内核有一些现有的机制,如使能swap分区,这样可以把不活跃的匿名页给交换出去,交换到磁盘上,在后面再使用的时候再给交换回来,还有就是回收掉page cache,因为大部分的page cache上的数据都是有磁盘文件与之对应的,为什么说大部分,因为还有一部分是共享内存的数据,如使用shm_open出来的共享内存的数据,它也被统计进了free -h里的buff/cache里,所以,你说它算是pagecache,也是OK的,毕竟shmem.c里的shmem_get_folio_gfp里有如下调用:
但是,另外一方面,它还是比较特别的,因为这部分共享内存的数据是没有磁盘对应的文件的,所以它除了被交换到swap分区之外,它对应的内存是不能回收的。另外,这块内存虽然统计到buff/cache里,但是并没有统计到/proc/meminfo里的Active(file)和Inactive(file)里,所以严格意义上来说,它又不是pagecache。
剔除共享内存这种特殊的pagecache以外,对于真正的文件页pagecache,在不开swap分区的话,这部分文件页的pagecache仍然会被系统里的内存回收逻辑给回收。触发该内存回收逻辑的可能有node里的memory zone触及了低水位,也有memory cgroup触及memory.high水位,还可能是加的内核功能逻辑去主动做释放的动作。
如果一旦内核把一些将来会被用到的文件页给回收了,那么就会造成下次再次使用时重新从磁盘上读取该文件页的同步读的性能损耗。为了减少这样的性能损耗,我们可以把系统里的一些关键的代码段或者一些关键的文件对应的内存锁住,不让系统在回收时选择它们。
我们可以使用一些上层的手段如mlock去锁住,但是用mlock去锁相关文件页的一个前提是这个进程得一直在,如果进程退出,mlock的锁住的行为就会被“释放”。而如果用内核手段去锁住文件页,那么这个锁住状态是持久了,不会因为进程的退出而释放。我们只需要在必要的时候去解锁即可。
这篇博客里下面第二章会给出锁住文件页的一个内核模块的示例程序,并演示效果。在第三章里,我们对第二章代码里的细节做出分析和原理解释。
二、源码及效果展示
2.1 锁文件page的内核模块代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/dcache.h>
#include <linux/namei.h>
#include <linux/pagemap.h>// 模块参数
static char *filepath = "/tmp/testfile"; // 默认文件路径
module_param(filepath, charp, S_IRUGO);
MODULE_PARM_DESC(filepath, "Path of the file to open");static char *mode = "nothing";
module_param(mode, charp, S_IRUGO);char buffer[4096];int getfullpath(struct inode *inode)
{struct dentry *dentry;printk("inode = %p\n", inode);hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) {char *path;path = dentry_path_raw(dentry, buffer, PAGE_SIZE);if (IS_ERR(path)){continue; }printk("dentry name = %s , path = %s\n", dentry->d_name.name, path);}return 0;
}static int __init my_module_init(void) {struct file *file;printk(KERN_INFO "Opening file: %s\n", filepath);// 打开文件file = filp_open(filepath, O_RDONLY, 0);printk(KERN_INFO "file[%p]\n", file);if (IS_ERR(file)) {printk(KERN_ERR "Error opening file: %ld\n", PTR_ERR(file));return PTR_ERR(file);}// getfullpath(file->f_inode);// do {// char *path;// path = dentry_path_raw(file->f_path.dentry, buffer, PAGE_SIZE);// if (IS_ERR(path)){// break;// }// printk("[2] dentry name = %s , path = %s\n", file->f_path.dentry->d_name.name, path);// } while(0);// lock pages of the input filepath fileif (strcmp(mode, "nothing") != 0) {struct address_space *mapping = file->f_mapping;struct page *page;pgoff_t index;unsigned long start_index, end_index;start_index = 0;printk("i_size=%ld\n", mapping->host->i_size);end_index = (mapping->host->i_size >> PAGE_SHIFT);//printk("end_index=%lu\n", end_index);
#if 1for (index = start_index; index < end_index; index++) {if (strcmp(mode, "lock") == 0) {page = find_get_page(mapping, index);if (!page) {page = read_cache_page_gfp(mapping, index, GFP_KERNEL);if (!page) {printk("page[%lu] is NULL!\n", index);}else {//get_page(page);//SetPageMlocked(page);//page = find_get_page(mapping, index);unsigned long ref_count = page_ref_count(page);printk("page[%lu] ref=%lu\n", index, ref_count);}}else {unsigned long ref_count = page_ref_count(page);printk("page[%lu] ref=%lu\n", index, ref_count);}//mapping_set_unevictable(mapping);}else if (strcmp(mode, "unlock") == 0) {page = find_get_page(mapping, index);if (page) {//__ClearPageMlocked(page);put_page(page);put_page(page);}//mapping_clear_unevictable(mapping);}else if (strcmp(mode, "query") == 0) {page = find_get_page(mapping, index);if (!page) {printk("page[%lu] is NULL!\n", index);}else {//get_page(page);//SetPageMlocked(page);//page = find_get_page(mapping, index);unsigned long ref_count = page_ref_count(page);printk("page[%lu] ref=%lu\n", index, ref_count);put_page(page);}}// else if (strcmp(mode, "grablock") == 0) {// page = grab_cache_page(mapping, index);// if (!page) {// printk("page[%lu] is NULL!\n", index);// }// else {// unsigned long ref_count = page_ref_count(page);// printk("page[%lu] ref=%lu\n", index, ref_count);// }// }}
#endif}// 关闭文件filp_close(file, NULL);return -EINVAL;
}static void __exit my_module_exit(void) {printk(KERN_INFO "Module exiting\n");
}module_init(my_module_init);
module_exit(my_module_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zhaoxin");
MODULE_DESCRIPTION("A simple module to read file and lock pagecache");
2.2 配合做实验的用户态程序代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/resource.h>#define FILE_NAME "large_file.img"
#define FILE_SIZE 1024*1024*1024ullint main() {int fd;//char *buffer;// 创建并打开文件//fd = open(FILE_NAME, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);fd = open(FILE_NAME, O_RDWR, S_IRUSR | S_IWUSR);if (fd == -1) {perror("open");return EXIT_FAILURE;}//buffer = (char*)malloc(FILE_SIZE);// if (!buffer) {// perror("malloc");// close(fd);// return EXIT_FAILURE;// }// memset(buffer, 0, FILE_SIZE);// const char *data = "This is some sample data to be written to the file.";// strncpy(buffer, data, FILE_SIZE);// getchar();char *mapped = (char*)mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (mapped == MAP_FAILED) {perror("mmap");close(fd);exit(EXIT_FAILURE);}// if (read(fd, buffer, FILE_SIZE) == -1) {// perror("read");// free(buffer);// close(fd);// return EXIT_FAILURE;// }getchar();{unsigned int i = 0;unsigned int sum = 0;for (i = 0; i < FILE_SIZE; i++) {sum += *(mapped + i);*(mapped + i) = (char)sum;}}printf("finish read write all\n");getchar();// 锁定内存区域if (mlock(mapped, FILE_SIZE) == -1) {perror("mlock");//free(buffer);close(fd);return EXIT_FAILURE;}printf("finish mlock\n");getchar();//printf("Memory locked successfully. Data written to file: %s\n", buffer);// 释放内存if (munlock(mapped, FILE_SIZE) == -1) {perror("munlock");}munmap(mapped, FILE_SIZE);printf("finish munlock munmap\n");getchar();//free(buffer);close(fd);return EXIT_SUCCESS;
}
2.3 效果展示
在上面 2.2 里的测试程序里,通过mmap来读写大文件,叫large_file.img,大小是1G。
我们分几种情况来进行测试:
1)mmap方式读写大文件(注意是MAP_SHARED方式),触发完所有页的缺页异常,让磁盘文件都加载进page cache,但不mlock锁定
——在这种情况下通过内核逻辑锁住page cache,并用vmtouch -e来做移除实验
——然后再调用内核逻辑解锁page cache,看是否能vmtouch -e来移除掉
通过读写文件所有的字节来触发完所有页的缺页异常:
2)mmap映射了大文件,但是还未进行任何读写,也就是还未触发缺页异常
——在这种情况下通过内核逻辑锁住page cache,并用vmtouch -v、vmtouch -e来打印情况和做移除实验
——然后再调用内核逻辑解锁page cache,看是否能vmtouch -e来移除掉
3)mmap映射了大文件,并调用mlock锁住内存页
——在这种情况下通过内核逻辑锁住page cache,并用vmtouch -v、vmtouch -e来打印情况和做移除实验
——然后再调用内核逻辑解锁page cache,看是否能vmtouch -e来移除掉
上面 2.2 的测试程序里有多处响应按键的地方,在按第一次按键前,进行了mmap该大文件的映射,但是并未进行任何的读写:
按第一次按键后是进行1Gsize读写,自然会加载进pagecache里:
按第二次按键后,按第三次按键前,调用了mlock锁住了这个1G的内存:
按第三次按键后,程序会执行munlock再munmap后再做退出:
下面,我们针对上面这几次按键的不同场景分别做实验。
2.3.1 mmap方式已触发缺页异常后的场景
这一节测试的场景是mmap方式,并已经触发了缺页异常,但是还未mlock锁定的场景下,执行我们的内核模块程序来进行内核态锁住逻辑,看执行完是否能驱逐掉,再进行内核态解锁,看是否能驱逐掉。
如下图,触发缺页异常之后,large_file.img这个文件对应的pagecache都加载进去了。
我们进行内核态锁定后,可以看到是驱逐不掉的:
然后通过内核态解锁后,可以看到已经发生缺页异常的部分,在没有munmap时就算不mlock也是不能驱逐掉的:
这个原因会在下面 3.2 里进行解释。
我们下面展示一下,不进行内核逻辑的锁定,看是否能驱逐出去这部分已经触发了缺页异常的pagecache:
如上图可以看到,对于mmap方式(MAP_SHARED方式)进行读写,就算不调用mlock,在munmap之前,就算不用内核锁定逻辑,对应的pagecache都是驱逐不出去的。
2.3.2 mmap方式未触发缺页异常后的场景
分两种情况来测,先测执行filetest但是不触发缺页异常,看vmtouch -v的情况,然后加载内核模块进行锁定,再看vmtouch -v的情况,并看是否可以驱逐出去;然后我们再测,在执行filetest之前直接运行内核模块的锁定逻辑,再运行filetest但是不触发缺页异常,看是否可以驱逐出去。
如上图可以看到,在filetest未触发缺页异常时,相关的pagecache未被加载,然后调用了内核逻辑,让其全部被加载,并驱逐无效。
下面我们测试看先调用内核模块的锁定逻辑,再调用filetest并不触发缺页异常,看是否可以驱逐出去,可以从下图看到,同样是驱逐不出去的:
2.3.3 mmap方式mlock后及munmap后的场景
其实在上面 2.3.1 的实验里,我们已经知道,如果是mmap方式MAP_SHARED方式对于已经触发缺页异常的部分,就算不做mlock对应的pagecache也是无法被驱逐的。
我们这里只需再做补充实验,就是不调用内核锁定逻辑,并不触发缺页异常,直接调用mlock,看是否相关pagecache已经被加载进来了,并看是否可以被驱逐掉。
我们改写一下程序,让触发缺页异常的逻辑干掉:
看不调用内核逻辑锁定,只靠mlock是否可以保证mmap MAP_SHARED的读写方式对应的pagecache是不是会被驱逐。
如下图可以看到是不会被驱逐的:
关于mlock的内核逻辑之前的博客有详细的介绍,可以参考 内存管理相关——malloc,mmap,mlock与unevictable列表-CSDN博客。
我们下面再看一下munmap后,如果不调用内核锁定逻辑看是否可以被驱逐,如下图看到是可以被驱逐的:
然后,我们试一下,执行过内核锁定逻辑之后,在munmap后是否能被驱逐:
可以从上图看到,是不会被驱逐的。
三、源码分析及原理解释
3.1 使用inode的i_size获取文件的大小
通过file->f_mapping->host可以获得打开的文件对应inode,这里的file是指进程地址空间实例的file,通过inode->i_size可以得到文件的大小:
上图对应的是执行两次filetest,第一次没有写入文件,第二次写入了一段时间就ctrl+c停止了:
对于64位系统上,直接获取inode->i_size是没有什么问题的,如果是32bit系统上,得使用i_size_read来获取,如i_size_read里的实现,使用到了顺序锁,顺序锁的细节见之前的博客 顺序锁的原理和使用注意事项-CSDN博客:
3.2 对于mmap文件出来的地址空间,一旦触发了缺页异常,其pagecache不会回收
上面 2.3.1 的实验可以看到,如果mmap一个磁盘上的文件到一个进程的虚拟地址空间之后,一旦触发了缺页异常,就算不执行mlock,也不执行内核态的锁pagecache,这些mmap且已经触发缺页异常的pagecache系统是回收不了的。
对于系统里的程序,如果程序并未退出,mmap加载的一些so库(程序的代码段都是mmap方式加载进地址空间)如果一旦触发过缺页异常,那么它们对应的pagecache是不会被回收的。但是要注意,对于一些会退出的进程,一旦进程退出,对应的代码段就可以被回收了,比如像grep/ls等这些系统命令的代码段。
通过mlock或者内核态的锁定逻辑可以提前把这些代码段给加载进pagecache,如果加上锁定后,它们就一直不回被回收了(如果用内核逻辑进行锁定,就算程序退出后,相关已经锁定的代码段也不会被回收了)。
3.2.1 mmap的MAP_PRIVATE方式的说明
虽然我们这篇博客里的示例程序用的是MAP_SHARED方式进行的读写,但是对于库文件来说,一般都是用的MAP_PRIVATE方式。
MAP_PRIVATE方式要注意的是,如果是读,那其pagecache肯定是可以多个进程共享同一个so的代码文件的。但是对于可写的部分,MAP_PRIVATE方式进行的映射会把可写的部分触发一个COW分配匿名内存并拷贝一份出来改写,这样原代码文件不会被改写,这也是so库里的data段也是这么一个方式。
对于我们这篇博客里的示例程序而言,如果用MAP_PRVIATE方式,如果要进行写的话,那么就写的是匿名内存,那么所有的pagecache的实验都不凑效了。但是如果用MAP_PRIVATE方式只是读的话,那么这篇博客里的实验也是一样凑效的。
3.3 对于非mmap方式的文件读写情况的说明
所谓非mmap方式的文件读写,就是直接通过read/write/fread/fwrite的这样的文件系统的接口来读写文件数据。这种方式,由于并没有直接映射相关文件到进程地址空间,而是借助vfs进行代码读写,在完成读写之后,内核是可以对其pagecache内容进行回收的。
对于读和写还得分开来看,对于读而言,虽然不放面使用mlock(因为通过常规手段不mmap根本拿不到pagecache对应的虚拟内存),但是可以使用内核模块的锁定逻辑进行锁定。
但是对于写而言,要特别注意,如果是一个1G文件,重新O_CREAT创建并从开头开始写入,那么对于旧文件的这些锁定逻辑包括内核锁定逻辑,由于文件大小已经变更回过0了,所以之前的page是可以被拿去回收的,这一点要额外注意。
3.4 通过增加page引用计数来防止被驱逐
通过page引用计数来方式被内存回收的做法,其实在之前的博客 内存管理之——get_user_pages和pin_user_pages及缺页异常_get user page-CSDN博客 里也有讲到。
这里,我们的关键逻辑是如何找到文件相关的page,如下方式通过find_get_page来根据address_space来获取到指定index的page:
address_space的指针可通过file的f_mapping拿到,另外上图里的index即address_space的映射的pages里的序号。
如果find_get_page找不到的话,再通过read_cache_page_gfp来读取磁盘上的文件读到pagecache,当然自然需要按需创建pagecache的内存,所以需要传入分配内存时的gfp参数:
由于find_get_page和read_cache_page_gfp都是会增加page的引用计数的,所以就没必要再get_page一次了。只需在对应的unlock逻辑里也得相应的扣除引用计数(多put_page一次就是为了扣除,另一次是抵消find_get_page的引用计数):
相关文章:
借助内核逻辑锁pagecache到内存
一、背景 内存管理是一个永恒的主题,尤其在内存紧张触发内存回收的时候。系统在通过磁盘获取磁盘上的文件的内容时,若不开启O_DIRECT方式进行读写,磁盘上的任何东西都会被缓存到系统里,我们称之为page cache。可以想象࿰…...
✨ Apifox:这玩意儿是接口界的“瑞士军刀”吧![特殊字符][特殊字符]
——全网最皮最全测评,打工人看了直呼“真香” 📢 友情提醒 还在用 Postman 测接口、Swagger 写文档、Mock.js 造假数据、脑细胞搞团队协作? 停! 你仿佛在玩《工具人环游记》,而隔壁同事已经用 Apifox 「一杆清台」了…...
《普通逻辑》学习记录——性质命题及其推理
目录 一、性质命题概述 二、性质命题的种类 2.1、性质命题按质的分类 2.2、性质命题按量的分类 2.3、性质命题按质和量结合的分类 2.4、性质命题的基本形式归纳 三、四种命题的真假关系 3.1、性质命题与对象关系 3.2、四种命题的真假判定 3.3、四种命题的对当关系 四、四种命题…...
设备接入与APP(应用程序)接入华为云iotDA平台的路径元素有哪些不同?
目录 壹、设备接入华为云iotDA 🏢 形象比喻:设备 员工,IoTDA 平台 安保森严的总部大楼 一、📍 平台接入地址 总部大楼地址 二、🧾 接入凭证 出入证 / 门禁卡 / 工牌 1. 设备密钥或证书 2. 预置接入凭证密钥&a…...
【git#4】分支管理 -- 知识补充
一、bug 分支 假如我们现在正在 dev2 分支上进行开发,开发到一半,突然发现 master 分支上面有 bug,需要解决。 在Git中,每个 bug 都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临…...
AXOP34062: 40V双通道运算放大器
AXOP34062是一款通用型高压双通道运算放大器,产品的工作电压为2.5V至40V,具有25MHz的带宽,压摆率为10V/μs,静态电流为650A。较高的耐压和带宽使其可以胜任绝大多数的高压应用场景。 主要特性 轨到轨的输入输出范围低输入失调电…...
OpenCv高阶(十)——光流估计
文章目录 前言一、光流估计二、使用步骤1、导库读取视频、随机初始化颜色2、初始化光流跟踪3、视频帧处理循环4、光流计算与可视化5、循环控制与资源释放完整代码 总结 前言 在计算机视觉领域,光流估计是捕捉图像序列中像素点运动信息的核心技术。它描述了图像中每…...
BS客户端的单点登录
1、参数类似于“XXXXX://?userIdsystem&time1696830378038&token38a8ea526537766f01ded33a6cdfa5bd” 2、在config里加一个LoginSecret参数可随意指定一个字符串 3、BS登录代码里会对“LoginSecret的参数值用户ID时间戳”进行MD5加密形成token,与传过来的…...
通讯录完善版本(详细讲解+源码)
目录 前言 一、使通讯可以动态更新内存 1、contact.h 2、contact.c 存信息: 删除联系人,并试一个不存在的人的信息,看看会不会把其他人删了 编辑 修改: 编辑 排序: 编辑 销毁: 编辑 …...
第3讲:ggplot2完美入门与美化细节打磨——从基础绘制到专业级润色
目录 1. 为什么选择ggplot2? 2. 快速了解ggplot2绘图核心逻辑 3. 基础绘图示范:柱状图、折线图、散点图 (1)简单柱状图 (2)折线图示范 (3)高级散点图 + 拟合线 4. 精细美化:细节打磨决定专业感 5. 推荐的美化小插件(可选进阶) 6. 小练习:快速上手一幅美化…...
带宽?增益带宽积?压摆率?
一、带宽(Bandwidth) 1.科学定义: 带宽指信号或系统能够有效通过的频率范围,通常定义为信号功率下降到中频值的一半(即 - 3dB)时的最高频率与最低频率之差。对于运算放大器(Op-Amp)…...
为什么栈内存比堆内存速度快?
博主介绍:程序喵大人 35- 资深C/C/Rust/Android/iOS客户端开发10年大厂工作经验嵌入式/人工智能/自动驾驶/音视频/游戏开发入门级选手《C20高级编程》《C23高级编程》等多本书籍著译者更多原创精品文章,首发gzh,见文末👇…...
什么是非关系型数据库
什么是非关系型数据库? 引言 随着互联网应用的快速发展,传统的基于表格的关系型数据库(如 MySQL、Oracle 等)已经不能完全满足现代应用程序的需求。在这种背景下,非关系型数据库(NoSQL 数据库)…...
制作一个简单的操作系统9
自定义 myprintf 函数实现解析 探索如何实现一个自定义的 printf 函数来处理任意 %d 和 %s 组合 (说实话,想不用任何库函数和头文件,纯C实现太难了,我放弃了,弄了一个简陋版本 对付用) 运行效果: Hello 123 World 456 Coding这样参数传递:(最多支持5个参数,按顺序…...
华为Pura X的智控键:让折叠机体验更上一层楼的设计
还记得Mate 70系列刚出那会,我体验了下智控键,那时候就觉得这个“把快捷方式做进电源键”的交互方式非常惊艳,没想到在Pura X上,这种便捷体验感更上了一层楼。 智控键:折叠屏手机的天选快捷方式? 传统折叠…...
打造高功率、高电流和高可靠性电路板的厚铜PCB生产
厚铜PCB生产是指制作一种具有较厚铜层的PCB(Printed Circuit Board,印刷电路板)。这种PCB通常用于高功率、高电流和高可靠性的电子设备中。厚铜PCB的生产过程包括以下几个 主要步骤: 1. 基材准备 厚铜PCB的基材通常采用FR4或CEM-…...
AI超级智能体教程(三)---程序调用AI大模型的四种方式(SpringAI+LangChain4j+SDK+HTTP)
文章目录 1.安装SDK(查看文档)2.创建API-key3.项目引入灵积大模型4.HTTP接入的方式5.SpringAI引入5.1添加依赖5.2添加配置5.3测试代码 6.LangChain4j引入6.1依赖引入6.2测试提问 1.安装SDK(查看文档) 安装阿里云百炼SDK_大模型服…...
JDBC连接数据库
一、查询 sqlserver数据库 private List<Map<String, String>> getPathList(String id) throws Exception {String driverName "com.microsoft.sqlserver.jdbc.SQLServerDriver";String dataBaseurl "jdbc:sqlserver://localhost:1433;SelectMeth…...
常见缓存淘汰算法(LRU、LFU、FIFO)的区别与实现
一、前言 缓存淘汰算法主要用于在内存资源有限的情况下,优化缓存空间的使用效率。以确保缓存系统在容量不足时能够智能地选择需要移除的数据。 二、LRU(Least Recently Used) 核心思想:淘汰最久未被访问的数据。实现方式&#x…...
深度学习--循环神经网络RNN
文章目录 前言一、RNN介绍1、传统神经网络存在的问题2、RNN的核心思想3、 RNN的局限性 二、RNN基本结构1、RNN基本结构2、推导3、注意4、循环的由来5、再谈RNN的局限 总结 前言 循环神经网络(RNN)的起源可以追溯到1982年,由Saratha Sathasiv…...
大学IP广播系统解决方案:构建数字化智慧化大学校园IP广播平台
大学IP广播系统解决方案:构建数字化智慧化大学校园IP广播平台 北京海特伟业科技有限公司任洪卓于2025年4月24日发布 随着教育信息化建设的深入推进,传统的模拟广播系统已无法满足现代化校园对智能化、场景化、融合化的管理需求。为此,海特伟业提出构建…...
#ifndef #else #endif条件编译
目录 一、#ifdef 1. 基本用法 2. 查看头文件 3. 目的 4. 常见用途 4. 取消定义 5.小结 二、#ifndef和#ifdef区别 1. #ifdef 2. #ifndef 3.结论 一、#ifdef 宏定义 #define H_PWM_L_ON 的作用是创建一个名为 H_PWM_L_ON 的宏。以下是这个宏定义的一些关键点ÿ…...
SystemVerilog语法之typedef与自定义结构
1.7 使用typedef创建新的类型 在Verilog中,你可以为操作数的位宽或者类型分别定义一个宏,但是你并没有创建新的数据类型,而是进行了文本的替换。在SystemVerilog中,可以使用typedef创建新的类型。可以将parameter和typedef语句放…...
【防火墙 pfsense】2配置
(1)接口配置和接口 IP 地址分配 ->配置广域网(WAN)和局域网(LAN)接口,分配设备标识符,如 eth0、eth1 等; ->如将WAN 接口将被分配到 eth0,而 LAN 接口将…...
数据结构之排序
排序 一.比较排序1.插入排序基本思想1.1直接插入排序1.2希尔排序 2.选择排序直接选择排序堆排序 3.交换排序冒泡排序快速排序hoare版本挖坑法lomuto前后指针非递归版本 4.归并排序非递归的归并排序 非比较排序1.计数排序 排序算法复杂度及稳定性分析 一.比较排序 1.插入排序 …...
cgroup sched_cfs_bandwidth_slice参数的作用及效果
一、背景 cgroup是一个非常重要的功能,其中cgroup cpu这块有不少功能,在之前的博客 CFS及RT调度整体介绍_rt调度器-CSDN博客 里,我们分析了cfs的组调度也就是cgroup cpu的这块内核逻辑的细节侧重于调度逻辑这块,在之前的博客 cgr…...
【C++指南】告别C字符串陷阱:如何实现封装string?
🌟 各位看官好,我是egoist2023! 🌍 种一棵树最好是十年前,其次是现在! 💬 注意:本章节只详讲string中常用接口及实现,有其他需求查阅文档介绍。 🚀 今天通过了…...
液体神经网络LNN-Attention创新结合——基于液体神经网络的时间序列预测(PyTorch框架)
1.数据集介绍 ETT(电变压器温度):由两个小时级数据集(ETTh)和两个 15 分钟级数据集(ETTm)组成。它们中的每一个都包含 2016 年 7 月至 2018 年 7 月的七种石油和电力变压器的负载特征。 traffic(交通) :描…...
kafka和Spark-Streaming2
Kafka 工作流程及文件存储机制 Kafka 中消息是以topic 进行分类的,生产者生产消息,消费者消费消息,都是面向topic 的。 “.log”文件存储大量的数据,“.index”文件存储偏移量索引信息,“.timeindex”存储时间戳索引文…...
MySQL日期函数的详细教程(包含常用函数及其示例)
概述 以下是一个关于MySQL日期函数的详细教程,包含常用函数及其示例内容以转换为PDF电子书,喜欢的朋友可以转存慢慢享用:https://pan.quark.cn/s/57d2e491bbbe 1. 获取当前日期和时间 • CURDATE() / CURRENT_DATE() 返回当前日期…...
P4017 最大食物链计数-拓扑排序
P4017 最大食物链计数 题目来源-洛谷 题意 要求最长食物链的数量。按照题意,最长食物链就是指有向无环图DAG中入度为0到出度为0的不同路径的数量(链数) 思路 在计算时,明显:一个被捕食者所…...
C语言——字串处理
C语言——字串处理 一、问题描述二、格式要求1.输入形式2.输出形式3.样例 三、实验代码 一、问题描述 现有两个字符串s1和s2,它们最多都只能包含255个字符。编写程序,将字符串s1中所有出现在字符串s2中的字符删去,然后输出s1。 二、格式要求…...
工业排风轴流风机:强劲动力与节能设计的完美融合
在工业生产中,通风换气是保障作业环境安全、维持设备正常运行的关键环节。工业排风轴流风机凭借其独特的设计,将强劲动力与节能特性完美融合,成为众多工业场景的首选通风设备,为企业高效生产与绿色发展提供了可靠支持。 工业排风…...
【Test】单例模式❗
文章目录 1. 单例模式2. 单例模式简单示例3. 懒汉模式4. 饿汉模式5. 懒汉式和饿汉式的区别 1. 单例模式 🐧定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。 单例模式是一种常用的软件设计模式,在它的核心结构中只包…...
3.3 Spring Boot文件上传
在 Spring Boot 项目中实现文件上传功能,首先创建项目并添加依赖,包括 Commons IO 用于文件操作。接着,创建文件上传控制器 FileUploadController,定义上传目录并实现文件上传逻辑,通过生成唯一文件名避免文件冲突。创…...
【玩泰山派】7、玩linux桌面环境xfce - (4)使用gstreamer
文章目录 前言gstreamergstreamer概述基本概念主要功能应用场景开发方式 安装gstreamer使用gstreamer使用gstreamer播放视频 前言 玩一下gstreamer,使用gstreamer去播放下音视频 gstreamer gstreamer概述 GStreamer是一个用于构建多媒体应用程序的开源库和框架&…...
cpu性能统计
cpu负载 top中avg,/proc/loadavg, 包括cpu密集型任务io型任务 统计流程 每cpu scheduler_tick ----calc_global_load_tick : 当前瞬时 cpu::this_rq:: nr_runningnr_inunterrupt->calc_load_tasks(全局变量) 全局 do_timer ----calc_global_load&a…...
Java对接企业微信实战笔记
Java对接企业微信实战笔记 微信开发文档 有关企业微信的服务商的一些配置参考企业微信创建的服务商配置信息 一 流程图 只要企业安装应用后,就可以获取到企业的信息 二 创建应用获取suite_ticket 1.创建应用 微信开发平台得是服务商角色才能进入服务商后台创建一…...
HashMap的源码解析
HashMap基于哈希表的Map接口实现,是以key-value存储形式存在,即主要用来存放键值对。HashMap的实现不是同步的,这意味着它不是线程安全的。它的key、value都可以为null。此外,HashMap中的映射不是有序的。 JDK1.8 之前 HashMap由数…...
【金仓数据库征文】金仓数据库KingbaseES:在技术与人文交织中开拓信创未来
🎁个人主页:User_芊芊君子 🎉欢迎大家点赞👍评论📝收藏⭐文章 🔍系列专栏:AI 【引言】 在信息技术应用创新(信创)的浪潮下,数据库作为数字经济的基石…...
【AI】[特殊字符]生产规模的向量数据库 Pinecone 使用指南
一、Pinecone 的介绍 Pinecone是一个完全托管的向量数据库服务,专为大规模机器学习应用设计。它允许开发者轻松存储、搜索和管理高维向量数据,为推荐系统、语义搜索、异常检测等应用提供强大的基础设施支持。 1.1 Pinecone的核心特性 1. 高性能向量搜…...
OpenHarmony之电源模式定制开发指导
OpenHarmony之电源模式定制开发指导 概述 简介 OpenHarmony默认提供了电源模式(如正常模式、性能模式、省电模式、极致省电模式)的特性。但由于不同产品的部件存在差异,导致在同样场景下电源模式的配置需要也存在差异,为此&…...
Jsp技术入门指南【十】IDEA 开发环境下实现 MySQL 数据在 JSP 页面的可视化展示,实现前后端交互
Jsp技术入门指南【十】IDEA 开发环境下实现 MySQL 数据在 JSP 页面的可视化展示,实现前后端交互 前言一、JDBC 核心接口和类:数据库连接的“工具箱”1. 常用的 2 个“关键类”2. 必须掌握的 5 个“核心接口” 二、创建 JDBC 程序的步骤1. 第一步…...
JDBC之ORM思想及SQL注入
目录 一. ORM编程思想 1. 简介 2. 实操ORM思想 a. Students实体类 b. ORM映射 二. SQL注入 1. 简介 2. 解决SQL注入 三. 总结 前言 本文来讲解ORM编程思想和SQL注入,旨在帮助大家更容易的理解和掌握 个人主页:艺杯羹 系列专栏:JDBC …...
UniApp学习笔记
在uniapp中使用View标签来代替div标签,使用rpx来取代px,rpx动态适配屏幕宽度750rpx100vw H5端不支持*的css选择器 body的元素选择器请改为page div和ul和li等改为view、 span和font改为text a改为navigator img改为image scoped:非H5端默认并未启…...
统计术语学习
基期、现期 作为对比参照的时期称为基期,而相对于基期的称为现期。 描述具体数值时我们称之为基期量和现期量。 【例 1】2017 年比 2016 年第三产业 GDP 增长 6.8%, (2016)为基期,(2017) 为现…...
认识 Linux 内存构成:Linux 内存调优之页表、TLB、缺页异常、大页认知
写在前面 博文内容涉及 Linux 内存中 多级页表,缺页异常,TLB,以及大页相关基本认知理解不足小伙伴帮忙指正对每个人而言,真正的职责只有一个:找到自我。然后在心中坚守其一生,全心全意,永不停息。所有其它的路都是不完整的,是人的逃避方式,是对大众理想的懦弱回归,是…...
Java File 类的常用方法详解
Java File 类的常用方法详解 File 类是 Java 中用于操作文件和目录的核心类,位于 java.io 包。它提供了丰富的方法来管理文件系统,包括创建、删除、重命名、查询属性、遍历目录等操作。 1. 构造方法 File 类提供多种构造方法,用于创建文件或…...
【AIGC】基础篇:VS Code 配置 Python 命令行参数调试debug超详细教程
文章目录 前言一、安装必要的扩展二、安装 debugpy三、创建 launch.json 配置文件四、配置调试环境五、开始调试六、命令行调试七、远程调试八、调试技巧九、常见问题及解决方法 前言 在 Python 开发过程中,调试是必不可少的环节。VS Code 提供了强大的调试功能&am…...
【金仓数据库征文】金仓数据库KingbaseES:千行百业国产化征程中的璀璨之星
🎁个人主页:User_芊芊君子 🎉欢迎大家点赞👍评论📝收藏⭐文章 🔍系列专栏:AI 【引言】 在数字化转型浪潮奔涌向前的时代,数据库作为数据存储与管理的核心枢纽,其重要性不…...