vdso概念及原理,vdso_fault缺页异常,vdso符号的获取
一、背景
vdso的全称是Virtual Dynamic Shared Object,它是一个特殊的共享库,是在编译内核时生成,并在内核镜像里某一段地址段作为该共享库的内容。vdso的前身是vsyscall,为了兼容一些旧的程序,x86上还是默认加载了vsyscall:
但是在arm64上并不支持vsyscall,也没有这样的程序段:
我们在做一些用户态程序的栈的抓取时,有时候是会运行到vdso里去的,在x86上也甚至可能运行到vsyscall里去,这时候,我们也需要知道vdso里有哪些符号。
在下面第二章里,我们会介绍vdso的概念和原理,原理侧重于介绍内核部分的相关逻辑,用户态部分的会在后面的博客里介绍。另外,还会对vdso的代码段的fault函数也就是vdso_fault函数进行介绍,并拓展到page的引用计数和vm_insert_page函数的使用。另外,也会介绍vvar_fault等也属于vdso范畴的其他相关细节。
vdso可以用来做性能相关的优化,但是前提肯定是得先了解其原理。
二、vdso概念及实现原理,vdso_fault缺页异常逻辑
2.1 vdso函数以时间获取为主
在下面的第三章里,我们会将如何捞取vdso里的符号,我们把捞取到的内容展示一下:
x86下的vdso的符号多一些:
arm64下的vdso的符号少一些:
可以从上两张图里可以看到,无论是x86还是arm64,符号里基本都是和时间接口有关。
2.2 时间获取走vdso的原因
原因是为了性能优化,因为时间获取并不是一个敏感的信息,并不涉及很多安全考虑,另外,时间获取的逻辑相对也较为简单,并不依赖内核里的很多函数,这样,把时间获取有关的数据和代码段映射到用户态来执行也相对简单。
如果时间获取逻辑走了vdso,那么用户态代码在执行该时间获取路基时就不需要陷入内核来执行,这很明显能提升运行效率。因为每次系统调用陷入内核之后要做上下文切换,用户栈和内核栈的保存和切换,另外在内核态代码执行完后返回用户态时也得做reschedule的判断,更别提使能了rt-linux之后内核逻辑里如果用到了锁还会更加一些调度有关的检查和切换逻辑,这些都是消耗cpu的。
2.3 以时间获取为例介绍vdso的实现原理
这一节我们介绍vdso的实现原理,以时间获取为例来介绍。不过无论是哪个接口,底层这块的vdso的逻辑都是一样的,我们从底到上来介绍实现原理。
2.3.1 内核的vdso映射逻辑,vdso段和vvar段及测试ko
我们运行如下命令可以看到两个vdso模块会用到的地址段:
cat /proc/1/maps | grep -E "vdso|vvar"
我们上图看到的是用户态地址段,是进程1的用户空间地址范围,可以看到这两个maps的条目比较特殊,是用"[]"来包裹的,事实上,内核里确实对其进行了特殊映射,相关函数是_install_special_mapping,如在x86时,在arch/x86/entry/vdso/vma.c里的map_vdso函数里有如下映射逻辑:
上图里的vdso_mapping和vvar_mapping如下定义:
这里,[vdso]是.text段也就是代码段,[vvar]是数据段内容,是用户态和内核态共享维护的vdso逻辑相关的数据的地址段。
针对代码段和数据段的缺页异常有不同的.fault函数,代码段的缺页异常函数vdso_fault,我们使用一个ko代码来kprobe这个vdso_fault来确定它的触发点的调用堆栈并非来自于_install_special_mapping时同步触发(同步触发的场景只在映射时带上MAP_POPULATE/MAP_LOCKED时才会同步触发pagefault,在之前的博客 内存管理相关——malloc,mmap,mlock与unevictable列表-CSDN博客 里的 3.2.1 一节里讲到),抓到的堆栈如下:
对于vvar_fault,抓到的堆栈如下,和vdso_fault是一样的,不是同步触发pagefault:
测试用的源码:
#include <linux/module.h>
#include <linux/capability.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/proc_fs.h>
#include <linux/ctype.h>
#include <linux/seq_file.h>
#include <linux/poll.h>
#include <linux/types.h>
#include <linux/ioctl.h>
#include <linux/errno.h>
#include <linux/stddef.h>
#include <linux/lockdep.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/init.h>
#include <asm/atomic.h>
#include <trace/events/workqueue.h>
#include <linux/sched/clock.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/tracepoint.h>
#include <trace/events/osmonitor.h>
#include <trace/events/sched.h>
#include <trace/events/irq.h>
#include <trace/events/kmem.h>
#include <linux/ptrace.h>
#include <linux/uaccess.h>
#include <asm/processor.h>
#include <linux/sched/task_stack.h>
#include <linux/nmi.h>
#include <asm/apic.h>
#include <linux/version.h>
#include <linux/sched/mm.h>
#include <asm/irq_regs.h>
#include <linux/kallsyms.h>
#include <linux/kprobes.h>
#include <linux/stop_machine.h>MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhaoxin");
MODULE_DESCRIPTION("Module for vdso_fault debug.");
MODULE_VERSION("1.0");struct kprobe _kp1;static bool _blog = false;int kprobecb_vdso_fault_pre(struct kprobe* i_k, struct pt_regs* i_p)
{if (!_blog) {_blog = true;dump_stack();}return 0;
}int kprobe_register_func_vdso_fault(void)
{int ret;memset(&_kp1, 0, sizeof(_kp1));_kp1.symbol_name = "vvar_fault";_kp1.pre_handler = kprobecb_vdso_fault_pre;_kp1.post_handler = NULL;ret = register_kprobe(&_kp1);if (ret < 0) {printk("register_kprobe fail!\n");return -1;}printk("register_kprobe success!\n");return 0;
}void kprobe_unregister_func_vdso_fault(void)
{unregister_kprobe(&_kp1);
}static int __init testvdso_init(void)
{kprobe_register_func_vdso_fault();return 0;
}static void __exit testvdso_exit(void)
{kprobe_unregister_func_vdso_fault();
}module_init(testvdso_init);
module_exit(testvdso_exit);
在接下来的三节里,我们依次来分析一下上面提到的vdso_fault,vvar_fault,_install_special_mapping三个函数。
2.4 vdso_fault缺页异常逻辑,及get_page
表面上来看vdso_fault的函数实现,其实还是比较简单的,就是判断一个地址范围,超出的话报VM_FAULT_SIGBUS错误,除此以外就获取到vdso代码段的page,增加该物理页的引用计数:
如上图里,核心逻辑其实就是两步:设置vmf->page设置对应的物理页,再调用get_page。
内核里相似的在缺页异常里处理的做法如下图:
但是事实上,虽然我们看到的只是简单的两步,但是缺页异常的调用链上还是配合有很多其他逻辑的。
2.4.1 详细跟踪vdso_fault的调用链
我们回过来看一下vdso_fault相关的调用链:
根据里面的调用链里的函数的offset,结合vmlinux.txt(objdump -S出来的文件),我们得到上图的调用链包含inline函数的完整调用链是:
handle_mm_fault->__handle_mm_fault->handle_pte_fault->do_pte_missing->do_fault->do_read_fault->__do_fault,拆解一下如下:
handle_mm_fault调用了__handle_mm_fault:
__handle_mm_fault调用了handle_pte_fault:
handle_pte_fault调用了do_pte_missing:
do_pte_missing调用了do_fault:
do_fault调用了do_read_fault:
do_read_fault调用了__do_fault:
2.4.2 do_read_fault里在执行完__do_fault后调用了finish_fault进行了页表设置
详细来说,就是在__do_fault函数里设置了vmf->page后,在finish_fault里根据vmf->page来进行pte的设置:
finish_fault里根据vmf的页的标志位信息,如果是可写且不共享的,则用vmf->cow_page,如果是其他情况,则用vmf->page:
finish_fault里用page和vma的信息来设置页表和tlb:
2.4.3 关于缺页异常里的使用的vmf_insert_pfn情形
在上面几节搞清楚了vdso_fault的缺页异常的流程里设置pte的操作是在vdso_fault的这个special vma的.fault函数执行之后做的这个细节之后,还有一个get_page的疑问,就是为什么在vdso_fault里有这样的get_page的显示的调用,而在别的缺页异常的处理函数里,vm_insert_page或vmf_insert_pfn这样的调用之后不需要再get_page调用了,我们依次看一下原因,先看vmf_insert_pfn的情形,如下图例子:
如上图看到,在执行完vmf_insert_pfn之后,返回了VM_FAULT_NOPAGE。这个VM_FAULT_NOPAGE表示什么含义呢?
它是表示缺页异常处理函数里配置了新的PTE,这次缺页异常并没有返回一个新的页面。既然不是新的页面,那么也并不需要通过get_page来增加引用计数。
当return了VM_FAULT_NOPAGE之后,在do_read_fault里执行了__do_fault函数,拿到的返回值如果是VM_FAULT_NOPAGE时,如下图,就会直接返回,并不会执行finish_fault的根据vmf->page进行pte配置的动作,这种情况,相关的pte动作都是在vmf_insert_pfn里执行的。
vmf_insert_pfn里执行相关pte配置的动作的截图:
2.4.4 关于缺页异常里的使用的vm_insert_page情形
上面一节里介绍的vmf_insert_pfn是直接拿着页框去做映射,缺页异常里使用vmf_insert_pfn来做映射的情况也是非常常见的。另外,我们其实也可以用vm_insert_page或者vm_insert_pages函数是根据page结构体来去做映射。如下图方式:
如上图情况下,用的是vm_insert_page接口,从使用角度来说,这个vm_insert_page相对更方便,不用再去找页框,有page就可以了。使用vm_insert_page的时候不需要再去get_page一下,因为vm_insert_page里已经有了get_page动作。
vm_insert_page里先是要检查page的引用计数不能是0,这对应的是kmalloc这种分配接口,分配出来以后自然引用计数就不为0了,这个我们用一个测试ko来验证,这个ko会在下面一节里介绍,另外,例子里也会使用vm_insert_page函数,也有一个用户态的mmap改ko创建的dev的节点的对应的例子程序。
我们继续分析vm_insert_page下面的逻辑:
看一下insert_page里,会调用insert_page_into_pte_locked:
insert_page_into_pte_locked里会调用get_page增加page的引用计数:
get_page:
2.4.5 关于page的引用计数和vm_insert_page的实验
测试ko源码:
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/slab.h>MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhaoxin");
MODULE_DESCRIPTION("Module for kernel test fault.");
MODULE_VERSION("1.0");static void *kaddr;static vm_fault_t my_fault(struct vm_fault *vmf)
{struct vm_area_struct *vma = vmf->vma;int offset, ret;offset = vmf->pgoff * PAGE_SIZE;ret = vm_insert_page(vma, vmf->address, virt_to_page(kaddr + offset));if (ret)return VM_FAULT_SIGBUS;return VM_FAULT_NOPAGE;
}static const struct vm_operations_struct vm_ops = {.fault = my_fault,
};static int my_mmap(struct file *file, struct vm_area_struct *vma)
{//vma->vm_flags |= VM_MIXEDMAP;vm_flags_set(vma, VM_MIXEDMAP);vma->vm_ops = &vm_ops;return 0;
}static struct file_operations my_fops = {.owner = THIS_MODULE,.mmap = my_mmap,
};static struct miscdevice mdev = {.minor = MISC_DYNAMIC_MINOR,.name = "my_dev",.fops = &my_fops,
};static int __init my_init(void)
{kaddr = kzalloc(PAGE_SIZE * 3, GFP_KERNEL);for (int i = 0; i < 3; i++) {printk("page[%d]:count[%d]\n",i, page_count(virt_to_page(kaddr + PAGE_SIZE*i)));}return misc_register(&mdev);
}static void __exit my_exit(void)
{misc_deregister(&mdev);kvfree(kaddr);
}module_init(my_init);
module_exit(my_exit);
insmod之后,可以看到:
可以如上下图看到k*alloc函数分配出来的每个page的引用计数都是1:
用户态程序使用mmap来触发缺页异常:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>#define DEVICE "/dev/my_dev"
#define PAGE_SIZE 4096
#define NUM_PAGES 3int main() {int fd;void *mapped_memory;// 打开设备fd = open(DEVICE, O_RDWR);if (fd < 0) {perror("Failed to open device");return EXIT_FAILURE;}// 使用 mmap 映射设备mapped_memory = mmap(NULL, NUM_PAGES * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (mapped_memory == MAP_FAILED) {perror("mmap failed");close(fd);return EXIT_FAILURE;}// 写入数据const char *data = "Hello, mmap!";memcpy(mapped_memory, data, strlen(data) + 1); // +1 以包含字符串的终止符// 读取数据char buffer[PAGE_SIZE];memset(buffer, 0, sizeof(buffer));memcpy(buffer, mapped_memory, sizeof(buffer));printf("Read from mmap: %s\n", buffer);// 解除映射munmap(mapped_memory, NUM_PAGES * PAGE_SIZE);close(fd);return EXIT_SUCCESS;
}
运行测试程序后,可以成功读写(说明缺页异常的逻辑基本是正确的):
2.4.6 新版本内核设置VM_MIXEDMAP时需要用vm_flags_set接口
在linux-6.5版本上,使用vma->vm_flags会编译报错:
这是因为新版本内核设置VM_MIXEDMAP时需要用vm_flags_set接口。
下面就是找的一个相对旧的版本和相对新的版本的同样函数里使用上的对比:
下图左边是linux-6.5,右边是linux-source-5.19.0(都是fs/cramfs/inode.c里的cramfs_physmem_mmap函数):
2.5 vvar_fault函数的相关细节
x86下的vvar_fault会考虑多种情形:
根据上图里的sym_offset来去匹配不同的vvar_page的种类。
如上图,默认选择的sym_vvar_start是sym_vvar_page。
这个sym_vvar_start和sym_vvar_page等其他种类的定义都是在vdso-image-64.c里定义的:
回过来看vvar_fault里的逻辑:
上图里先通过page_to_pfn得到页框pfn,再通过vmf_insert_pfn进行映射的逻辑其实在上面 2.4.3 里已经介绍过了。
2.6 _install_special_mapping的调用链,涉及execve系统调用
我们改写一下 2.3.1 里的测试ko,kprobe这个_install_special_mapping函数,打印这个函数的调用栈情况,如下:
分析vmlinux之后得到:
exec_binprm->do_execveat_common->bprm_execve->exec_binprm->search_binary_handler->load_elf_binary->ARCH_SETUP_ADDITIONAL_PAGES宏->arch_setup_additional_pages
从execve系统调用出发:
然后运行do_execve:
然后调用到do_execveat_common:
然后调用bprm_execve:
bprm_execve调用了exec_binprm:
exec_binprm调用了search_binary_handler:
然后search_binary_handler调用了fmt->load_binary:
fmt->load_binary里的load_binary和load_elf_binary的映射关系:
继续看load_elf_binary函数里:
load_elf_binary里调用了如下图的ARCH_SETUP_ADDITIONAL_PAGES(bprm, elf_ex, !!interpreter):
ARCH_SETUP_ADDITIONAL_PAGES宏使用了arch_setup_additional_pages:
arch_setup_additional_pages调用了map_vdso_randomized(&vdso_image_64):
(这里面的vdso_image_64在下面第三章里会详细介绍)
map_vdso_randomized调用了map_vdso:
map_vdso调用_install_special_mapping:
三、如何捞取vdso里的符号
在上面的 2.5 一节里有提到vdso-image-64.c里定义了vdso_image_64数组:
这个vdso_image结构体里的.data变量就是放的vdso的代码段裸数据:
3.1 通过内核空间来获取vdso代码段内容
输入如下命令:
cat /proc/kallsyms | grep vdso_image_64
得到如下图:
我们通过之前的博客 获取内存内容的几种方法-CSDN博客 里的第五章里的ko的方法:
insmod testgetkmem.ko address=0xffffffff8e8010e0 size=8 filedir="output.txt"
读到的这个地址的前8字节是0xffffffff8ee47000:
正好和:
cat /proc/kallsyms | grep raw_data
得到的一个raw_data的符号的数值一样:
这个raw_data是小写的d的符号,即如下图里的raw_data的static数组:
我们再读一下这个raw_data的内容是否和代码里的一致,可以看到是一致的:
3.2 通过/dev/mem来读取mmap到用户空间的vdso代码段的内容
在之前的博客 获取内存内容的几种方法-CSDN博客 里的第四章里也提及。
cat /proc/1/maps | grep vdso
把grep到的vdso的段的起始地址转换成10进制数,替换下面命令里skip=后面的数字:
dd if=/proc/1/mem of=vdso.so skip=140736471179264 ibs=1 count=8192
我们比较 3.1 导出的output.txt的md5sum和这个vdso.so的md5sum,如下图是一样的:
四、关于vsyscall和vvar及捞取它们符号的实验
4.1 vsyscall相比vdso的劣势
如第一章里描述所说,vsyscall是比vdso早的东西,vdso相比vsyscall改进了很多。
vdso本质上是一个elf目标文件,而vsyscall仅仅是代码+数据。如何理解这句话呢,意思就是vdso这个模拟出来的一个elf目标文件有了elf的一些基本属性,比如可以像so文件一样按照进程颗粒度动态映射到进程地址空间中,所有所谓的PIC(Position-Independent Code)属性,而vsyscall则是固定的一段内核空间的地址段,不可更改,常见的地址是起始于0xffffffffff60000,size是4096,如下图:
由于映射的地址不变,所以它是非常不安全的,内核里也对其相关页进行了保护,下面会讲到vsyscall的内容是不可读的,只可执行,是拿不出来的。
另外,vdso的内容是so的格式,而vsyscall的内容是二进制格式,这也是两者的区别。
4.2 vsyscall符号的捞取
上面的vsyscall的maps里条目可以看到vsyscall的地址段是不可读的,但是我们也可以通过设置grub把vsyscall设置成emulate模式,再配合修改下图里的__PAGE_KERNEL_VVAR宏,红色框出的部分改成__RW:
重编内核,来通过gdb dump来获取。
我们也可以用上面 3.1 一节里差不多的方法:
然后用之前的博客 获取内存内容的几种方法-CSDN博客 里第五章集成的dumpkmem的工具来导出到文件vsyscall.bin里去:
dumpkmem 0xffffffff8f004000 4096 vsyscall.bin
然后,我们可以用如下命令把该bin文件objdump出可阅读代码:
objdump -b binary -Mintel,x86-64,addr64 -m i386:x86-64 --adjust-vma=0xffffffffff600000 -D vsyscall.bin > vsyscall.txt
上图里的--adjust-vma后面的数值是vsyscall的代码段的固定起始地址:0xffffffffff60000
看一下上面的命令导出到的vsyscall.txt文件里的内容:
如下图里命令方式去grep一下看到相关的几个syscall的开始的指令位置:
对应于源码里(第一个vsyscall的syscall是PAGE_SIZE对齐,后面的两个符号是1024字节对齐):
4.3 vvar数据段的捞取方法
vvar在上面 2.3 一节里讲vdso原理也介绍过是用于用户vdso相关用户态和内核态代码共享内存数据所需要的。
先确认vvar的size是多少(如下图看到是4个page):
这四个page的size对应代码里的位置:
上图里的sym_vvar_start对应于下面的在_install_special_mapping时传入的size参数:
虽然vvar地址段显示的是可读,但是实际还是读不出来的(用上面 3.2 里的方法):
dd if=/proc/1/mem of=vvar.bin skip=140736471162880 ibs=1 count=8192
如下图提示错误:
我们需要知道vvar的用的是哪个符号,通过kprobe来打印出相关逻辑的数值(相关逻辑的介绍在上面 2.5 一节里有介绍):
vvar用到的page除了__vvar_page以外,还有可能会用到namespace里的time_ns里的vvar_page:
当前我们没用到,如kprobe里打出来的情况:
我们读__vvar_page,我们可以直接读内核空间里的相关vvar的page的内容,如下方式:
相关文章:
vdso概念及原理,vdso_fault缺页异常,vdso符号的获取
一、背景 vdso的全称是Virtual Dynamic Shared Object,它是一个特殊的共享库,是在编译内核时生成,并在内核镜像里某一段地址段作为该共享库的内容。vdso的前身是vsyscall,为了兼容一些旧的程序,x86上还是默认加载了vs…...
4.13学习总结
学习完异常和文件的基本知识 完成45. 跳跃游戏 II - 力扣(LeetCode)的算法题,对于我来说,用贪心的思路去写该题是很难理解的,很难想到,理解了许久,也卡了很久。...
Day14:关于MySQL的索引——创、查、删
前言:先创建一个练习的数据库和数据 1.创建数据库并创建数据表的基本结构 -- 创建练习数据库 CREATE DATABASE index_practice; USE index_practice;-- 创建基础表(包含CREATE TABLE时创建索引) CREATE TABLE products (id INT PRIMARY KEY…...
概率论与数理统计核心知识点与公式总结(就业版)
文章目录 概率论与数理统计核心知识点与公式总结(附实际应用)一、概率论基础1.1 基本概念1.2 条件概率与独立性 二、随机变量及其分布2.0 随机变量2.0 分布函数(CDF)2.1 离散型随机变量2.2 连续型随机变量2.3 多维随机变量2.3.1 联…...
AF3 ProteinDataset类的_patch方法解读
AlphaFold3 protein_dataset模块 ProteinDataset 类 _patch 方法的主要目的是围绕锚点残基(anchor residues)裁剪蛋白质数据,提取一个局部补丁(patch)作为模型输入。 源代码: def _patch(self, data):"""Cut the data around the anchor residues."…...
openssh 10.0在debian、ubuntu编译安装 —— 筑梦之路
OpenSSH 10.0 发布:一场安全与未来兼顾的大升级 - Linux迷 OpenSSH: Release Notes sudo apt-get updatesudo apt install build-essential zlib1g-dev libssl-dev libpam0g-dev libselinux1-devwget https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/opens…...
Go 跨域中间件实现指南:优雅解决 CORS 问题
在开发基于 Web 的 API 时,尤其是前后端分离项目,**跨域问题(CORS)**是前端开发人员经常遇到的“拦路虎”。本文将带你了解什么是跨域、如何在 Go 中优雅地实现一个跨域中间件,支持你自己的 HTTP 服务或框架如 net/htt…...
【数据结构_6】双向链表的实现
一、实现MyDLinkedList(双向链表) package LinkedList;public class MyDLinkedList {//首先我们要创建节点(因为双向链表和单向链表的节点不一样!!)static class Node{public String val;public Node prev…...
【双指针】专题:LeetCode 1089题解——复写零
复写零 一、题目链接二、题目三、算法原理1、先找到最后一个要复写的数——双指针算法1.5、处理一下边界情况2、“从后向前”完成复写操作 四、编写代码五、时间复杂度和空间复杂度 一、题目链接 复写零 二、题目 三、算法原理 解法:双指针算法 先根据“异地”操…...
Foxmail邮件客户端跨站脚本攻击漏洞(CNVD-2025-06036)技术分析
Foxmail邮件客户端跨站脚本攻击漏洞(CNVD-2025-06036)技术分析 漏洞背景 漏洞编号:CNVD-2025-06036 CVE编号:待分配 厂商:腾讯Foxmail 影响版本:Foxmail < 7.2.25 漏洞类型&#x…...
39.[前端开发-JavaScript高级]Day04-函数增强-argument-额外知识-对象增强
JavaScript函数的增强知识 1 函数属性和arguments 函数对象的属性 认识arguments arguments转Array 箭头函数不绑定arguments 函数的剩余(rest)参数 2 纯函数的理解和应用 理解JavaScript纯函数 副作用概念的理解 纯函数的案例 判断下面函数是否是纯…...
0x05.为什么 Redis 设计为单线程?6.0 版本为何引入多线程?
回答重点 单线程设计原因: Redis 的操作是基于内存的,其大多数操作的性能瓶颈主要不是 CPU 导致的使用单线程模型,代码简便的同时也减少了线程上下文切换带来的性能开销Redis 在单线程的情况下,使用 I/O 多路复用模型就可以提高 Redis 的 I/O 利用率了6.0 版本引入多线程的…...
CST1019.基于Spring Boot+Vue智能洗车管理系统
计算机/JAVA毕业设计 【CST1019.基于Spring BootVue智能洗车管理系统】 【项目介绍】 智能洗车管理系统,基于 Spring Boot Vue 实现,功能丰富、界面精美 【业务模块】 系统共有三类用户,分别是:管理员用户、普通用户、工人用户&…...
CST1018.基于Spring Boot+Vue滑雪场管理系统
计算机/JAVA毕业设计 【CST1018.基于Spring BootVue滑雪场管理系统】 【项目介绍】 滑雪场管理系统,基于 Spring Boot Vue 实现,功能丰富、界面精美 【业务模块】 系统共有两类用户,分别是管理员和普通用户,管理员负责维护后台数…...
剖析 Rust 与 C++:性能、安全及实践对比
1 性能对比:底层控制与运行时开销 1.1 C 的性能优势 C 给予开发者极高的底层控制能力,允许直接操作内存、使用指针进行精细的资源管理。这使得 C 在对性能要求极高的场景下,如游戏引擎开发、实时系统等,能够发挥出极致的性能。以…...
SDHC接口协议底层传输数据是安全的
SDHC(Secure Digital High Capacity)接口协议在底层数据传输过程中确实包含校验机制,以确保数据的完整性和可靠性。以下是关键点的详细说明: 物理层与数据链路层的校验机制 物理层(Electrical Layer)&…...
Gateway-网关-分布式服务部署
前言 什么是API⽹关 API⽹关(简称⽹关)也是⼀个服务, 通常是后端服务的唯⼀⼊⼝. 它的定义类似设计模式中的Facade模式(⻔⾯模式, 也称外观模式). 它就类似整个微服务架构的⻔⾯, 所有的外部客⼾端访问, 都需要经过它来进⾏调度和过滤. 常⻅⽹关实现 Spring Cloud Gateway&a…...
c++STL——string学习的模拟实现
文章目录 string的介绍学习的意义auto关键字和范围forstring中的常用接口构造和析构对string得容量进行操作string的访问迭代器(Iterators):运算符[ ]重载 string类的修改操作非成员函数 string的模拟实现不同平台下的实现注意事项模拟实现部分所有的模拟实现函数预…...
【寻找Linux的奥秘】第四章:基础开发工具(下)
请君浏览 前言1. 自动化构建1.1 背景1.2 基本语法1.3 make的运行原理1.4通用的makefile 2. 牛刀小试--Linux第一个小程序2.1 回车与换行2.2 行缓冲区2.3 倒计时小程序2.4 进度条小程序原理代码 3. 版本控制器git3.1 认识3.2 git的使用三板斧 3.3 其他 4. 调试器gdb/cgdb4.1 了解…...
RK3588上Linux系统编译C/C++ Demo时出现BUG:The C/CXX compiler identification is unknown
BUG的解决思路 BUG描述:解决方法:首先最重要的一步:第二步:正确设置gcc和g的路径方法一:使用本地系统中安装的 aarch64-linux-gnu-gcc 和 aarch64-linux-gnu-g方法二:下载使用官方指定的交叉编译工具方法三…...
记录一次/usr/bin/ld: 找不到 -lOpenSSL::SSL
1、cmake 报错内容如下: /usr/bin/ld: 找不到 -lOpenSSL::SSL /usr/bin/ld: 找不到 -lOpenSSL::Crypto2、一开始以为库没有正确安装 sudo yum install openssl-devel然后查看openssl 结果还是报错! 3、尝试卸载安装都不管用,网上搜了好多…...
[16届蓝桥杯 2025 c++省 B] 水质检测
思路:分类讨论,从左到右枚举,判断当前的河床和下一个河床的距离是第一行更近还是第二行更近还是都一样近,分成三类编写代码即可 #include<iostream> using namespace std; int main(){string s1,s2;cin>>s1>>…...
基于PySide6与pycatia的CATIA绘图比例智能调节工具开发全解析
引言:工程图纸自动化处理的技术革新 在机械设计领域,CATIA图纸的比例调整是高频且重复性极强的操作。传统手动调整方式效率低下且易出错。本文基于PySide6pycatia技术栈,提出一种支持智能比例匹配、实时视图控制、异常自处理的图纸批处理方案…...
四、Appium Inspector
一、介绍 Appium Inspector 是一个用于移动应用自动化测试的图形化工具,主要用于检查和交互应用的 UI 元素,帮助生成和调试自动化测试脚本。类似于浏览器的F12(开发者工具),Appium Inspector 的主要作用包括: 1.检查 UI 元素 …...
玩转Docker | 使用Docker部署MicroBin粘贴板
玩转Docker | 使用Docker部署MicroBin粘贴板 前言一、MicroBin介绍MicroBin 简介主要特点二、系统要求环境要求环境检查Docker版本检查检查操作系统版本三、部署MicroBin服务下载镜像创建容器检查容器状态检查服务端口安全设置四、访问MicroBin服务访问MicroBin首页登录管理后台…...
BGP分解实验·23——BGP选路原则之路由器标识
在选路原则需要用到Router-ID做选路决策时,其对等体Router-ID较小的路由将被优选;其中,当路由被反射时,包含起源器ID属性时,该属性将代替router-id做比较。 实验拓扑如下: 实验通过调整路由器R1和R2的rout…...
MQTT:单片机中MQTTClient-C移植定时器功能
接下来我们完善MQTTTimer.c和MQTTTimer.h两个功能 MQTTTimer.h void TimerInit(Timer* timer); 功能:此函数用于对 Timer 结构体进行初始化。在 MQTT 客户端里,定时器被用于追踪各种操作的时间,像连接超时、心跳包发送间隔等。初始化操作会…...
可拖动的关系图谱原型案例
关系图谱是一种以图结构形式组织和呈现实体间复杂关联关系的可视化数据模型。它通过节点和线构建多维度网络,能直观揭示隐藏的群体特征和传播路径。在社交网络分析、智能推荐系统、知识图谱构建等领域广泛应用。 软件版本:Axure RP 9 作品类型…...
CST1016.基于Spring Boot+Vue高校竞赛管理系统
计算机/JAVA毕业设计 【CST1016.基于Spring BootVue高校竞赛管理系统】 【项目介绍】 高校竞赛管理系统,基于 DeepSeek Spring AI Spring Boot Vue 实现,功能丰富、界面精美 【业务模块】 系统共有两类用户,分别是学生用户和管理员用户&a…...
从三次方程到复平面:复数概念的奇妙演进(二)
注:本文为 “复数 | 历史 / 演进” 相关文章合辑。 因 csdn 篇幅限制分篇连载,此为第二篇。 生料,不同的文章不同的点。 机翻,未校。 History of Complex Numbers 复数的历史 The problem of complex numbers dates back to …...
PCL 点云投影至指定平面
文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 之前的文章中介绍过一个点在平面上的投影坐标,其主要的思路就是利用投影垂线与平面法向量平行的特性,通过推导出的投影公式可以很容易的计算出在某点在某一平面内的投影点。因此只需要重复该过程就可以将整个点云…...
批量将文件名称、文件路径、文件扩展名提取到 Excel 清单
在数字化时代,文件的高效管理至关重要。当我们想要对磁盘中的文件进行整理,想要获取多个文件夹中的文件和路径信息,就需要现将这些文件的名称及路径信息提取出来。本文将介绍一种实用的批量提取技术,帮助用户优化文件管理流程&…...
KWDB创作者计划—KWDB场景创新:多模态数据融合与边缘智能的产业实践
引言:AIoT时代的数据基座重构 在工业物联网设备数量突破千亿、边缘计算节点覆盖率达75%的2025年,传统数据库面临多模态数据处理效率低下、边缘端算力利用率不足、跨域数据协同困难等核心挑战。KWDB(KaiwuDB Community Edition)通过…...
计算机系统概论
1. 计算机系统的基本组成 计算机系统由 硬件系统 和 软件系统 两大部分协同工作: 硬件系统: 基于冯诺依曼体系结构(存储程序原理),包含五大核心部件: 运算器(ALU):执行算…...
Android Cmake构建的项目,需不需要配置指定ndk及版本
在 CMake 构建的 Android 项目中,是否需要显式配置 NDK 及其版本,取决于项目的具体需求和环境。以下是详细分析和建议: 1. 是否需要显式配置 NDK 及版本? 情况 1:Android Studio 自动管理 NDK(推荐&#x…...
国内AI大模型卷到什么程度了?
目录 1.开源大模型更有前景吗? 2.参数越大真的越牛逼吗? 3.榜单排名有意义吗? 大家好这里是AIWritePaper官方账号,官网👉AIWritePaper~ 大模型开源更有前景? 参数越大真的越牛逼吗? 榜单排…...
【HDFS入门】Hadoop 2.0+ HDFS核心架构深度解析:高可用设计揭秘
目录 1 HDFS核心架构概述 2 高可用设计背景 3HDFS核心组件 3.1 Active与Standby NameNode 3.2 JournalNode 3.3 ZKFailoverController(ZKFC) 3.4 DataNode 4 高可用设计的工作流程 写入阶段: 元数据同步: 健康监测&…...
RabbitMQ安装
RabbitMQ安装 Ubuntu环境安装 一、安装Erlang #更新软件包 sudo apt-get update #安装erlang sudo apt-get install erlang 二、安装RabbitMQ #更新软件包 sudo apt-get update #安装rabbitmq sudo apt-get install rabbitmq-server #确认安装结果 systemctl status rabbitmq-…...
2022 CCPC Henan Provincial Collegiate Programming Contest K 复合函数
补题链接 看网上题解很少,来写一份,这题个人觉得思维难度不是特别大,难度主要在于代码准确度,首先将问题转化成 x x x 向 f ( x ) f(x) f(x) 连边,这一步转化应该是比较容易想到的,通过手模样例,会有类…...
Linux : 多线程互斥
目录 一 前言 二 线程互斥 三 Mutex互斥量 1. 定义一个锁(造锁) 2. 初始化锁 3. 上锁 4. 解锁 5. 摧毁锁 四 锁的使用 五 锁的宏初始化 六 锁的原理 1.如何看待锁? 2. 如何理解加锁和解锁的本质 七 c封装互斥锁 八 可重入…...
【数学建模】佳点集(Good Point Set)在智能优化算法中的应用与实现
佳点集(Good Point Set)在智能优化算法中的应用与实现 文章目录 佳点集(Good Point Set)在智能优化算法中的应用与实现1. 佳点集概述2. 佳点集的数学原理3. 佳点集在智能优化算法中的应用3.1 改进麻雀搜索算法(SSA)3.2 改进量子粒子群优化算法(QPSO)3.3 自适应分组差分变异狼群…...
redis linux 安装简单教程(redis 3.0.4)
redis.3.0.4.tar.gz 下载地址 链接: https://pan.baidu.com/s/19VAcrA6XS4mIesH6e5Jftg 提取码: bn2r (1)以安装目录:/home/zsl (2)将redis-3.0.4.tar.gz 拷贝到/home/zsl (3)tar xzvf redis-3.…...
探秘 Python 网络编程:构建简单聊天服务器
在计算机网络的世界里,网络编程是实现不同设备之间通信的关键技术。Python 凭借其简洁的语法和强大的库支持,在网络编程领域有着广泛的应用。无论是构建简单的聊天服务器,还是开发复杂的网络应用,Python 都能轻松胜任。 1 理论基础…...
debian转移根目录
如何在 BIOS 启动的 Debian 虚拟机中将根目录转移到 /dev/sda 设备上?本文将从硬盘分区,根目录复制,重新启动等几个方面介绍。 硬盘分区 1.检查磁盘:查看当前的磁盘和分区情况,确认新添加的磁盘设备名称。 parted -…...
vue3 element-plus表单验证
第一准备一个表单 form.vue <template><div><el-form><el-form-item label"姓名" prop"name"><el-input v-model"data.name" placeholder"请输入姓名"></el-input></el-form-item></e…...
Deepseek IP-Adapter与InstantID的区别
IP-Adapter与InstantID均为基于扩散模型的图像生成控制技术,但两者的算法设计目标、核心模块及应用场景存在显著差异。以下从技术架构、特征处理、条件控制等维度对比两者的差异: 1. 核心设计目标 IP-Adapter 由腾讯团队提出(2023年8月&…...
OSI 七层模型与 TCP/IP 协议栈详解
OSI 七层模型与 TCP/IP 协议栈详解 网络协议模型是理解计算机网络和通信的基础,而 OSI 七层模型和 TCP/IP 协议栈是最常见的两种网络通信模型。虽然这两者有些不同,但它们都提供了一种分层的结构,帮助我们理解和设计网络通信。本文将详细介绍…...
synchronize 或者lock 锁常见的使用场景
在 Java 多线程编程中,synchronized 和 Lock(如 ReentrantLock)是两种常见的线程同步机制。以下是它们的核心区别和典型使用场景,结合代码示例说明: 一、synchronized 的常见场景 1. 简单的临界区保护 public class …...
Redis之缓存更新策略
缓存更新策略 文章目录 缓存更新策略一、策略对比二、常见的缓存更新策略三、如何选择策略四、实际应用示例五、使用 Cache-Aside TTL 的方式,实现缓存商铺信息详情1.引入StringRedisTemplate2.将查询商铺信息加入缓存3.更新商铺信息时移除缓存总结 六、注意事项 一…...
【操作系统学习篇-Linux】进程
1. 什么是进程 课本概念:程序的一个执行实例,正在执行的程序等 内核观点:担当分配系统资源(CPU时间,内存)的实体。 如果你就看这个来理解进程,那么恭喜你,作为初学者,你…...