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的内容,如下方式:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/901271.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux中的文件传输(附加详细实验案例)

一、实验环境的设置 ①该实验需要两台主机&#xff0c;虚拟机名称为 L2 和 L3 &#xff0c;在终端分别更改主机名为 node1 和 node2&#xff0c;在实验过程能够更好分辨。 然后再重新打开终端&#xff0c;主机名便都更改了相应的名称。 ②用 ip a 的命令分别查看两个主机的 …

【从0到1学Elasticsearch】Elasticsearch从入门到精通(上)

黑马商城作为一个电商项目&#xff0c;商品的搜索肯定是访问频率最高的页面之一。目前搜索功能是基于数据库的模糊搜索来实现的&#xff0c;存在很多问题。 首先&#xff0c;查询效率较低。 由于数据库模糊查询不走索引&#xff0c;在数据量较大的时候&#xff0c;查询性能很差…

图论基础理论

在我看来&#xff0c;想要掌握图的基础应用&#xff0c;仅需要三步走。 什么是图&#xff08;基本概念&#xff09;、图的构造&#xff08;打地基&#xff09;、图的遍历方式&#xff08;应用的基础&#xff09; 只要能OK的掌握这三步、就算图论入门了&#xff01;&#xff0…

详细解读react框架中的hooks

React Hooks 是 React 16.8 引入的一项革命性特性&#xff0c;它允许你在函数组件中使用状态(state)和其他 React 特性&#xff0c;而无需编写 class 组件。下面将详细解读 React Hooks 的核心概念、常用 Hooks 及其工作原理。 一、Hooks 的核心概念 1. 什么是 Hooks Hooks …

主机IP动态变化时如何通过固定host.docker.internal访问本机服务

场景需求——主机IP动态变化时&#xff0c;通过固定的 http://host.docker.internal:11555 访问本机服务&#xff0c;核心问题在于 host.docker.internal 的解析逻辑与动态IP的适配。以下是分步解决方案&#xff1a; 一、核心原理&#xff1a;host.docker.internal 的本质与局…

插值算法 - 最近邻插值实现

目录 1. 导入必要的库 2. nearest_neighbor_interpolation 3. 测试代码 数学原理 完整代码 本文实现了基于最近邻插值算法的图像缩放功能。 它使用 Python 编写,主要依赖于NumPy和PIL(Python Imaging Library)库。 NumPy用于高效的数值计算,而PIL仅用于图像的加载和…

windows中搭建Ubuntu子系统

windows中搭建虚拟环境 1.配置2.windows中搭建Ubuntu子系统2.1windows配置2.1.1 确认启用私有化2.1.2 将wsl2设置为默认版本2.1.3 确认开启相关配置2.1.4重启windows以加载更改配置 2.2 搭建Ubuntu子系统2.2.1 下载Ubuntu2.2.2 迁移位置 3.Ubuntu子系统搭建docker环境3.1安装do…

MySQL事务机制

目录 原子性 持久性 隔离性 隔离级别(并发事务之间的关系) 读未提交 读已提交 可重复读 串行化(最严格的隔离级别) 一致性 问题 不可重复读性(已经提交的数据) 什么是脏读问题(未提交的数据)? 幻读 保存点 自动提交机制--autocommit 会话隔离级别与全局隔离级…

Cadence学习笔记之---直插元件的封装制作

目录 01 | 引 言 02 | 环境描述 03 | 操作步骤 04 | 结 语 01 | 引 言 在之前发布的Cadence小记中&#xff0c;已经讲述了怎样制作热风焊盘&#xff0c;贴片(SMD)焊盘、通孔、过孔&#xff0c;以及贴片元件的封装。 本篇关于Cadence的小记主要讲如何制作直插元件的封装。 …

【第四十周】文献阅读:用于检索-增强大语言模型的查询与重写

目录 摘要Abstract用于检索-增强大语言模型的查询与重写研究背景方法论基于冻结LLM的重写方案基于可训练重写器的方案重写器预热训练&#xff08;Rewriter Warm-up&#xff09;强化学习&#xff08;Reinforcement Learning&#xff09; 创新性实验结果局限性总结 摘要 这篇论文…

java学习总结(if switch for)

一.基本结构 1.单分支if int num 10; if (num > 5) {System.out.println("num 大于 5"); } 2.双分支if-else int score 60; if (score > 60) {System.out.println("及格"); } else {System.out.println("不及格"); } 3.多分支 int…

yum的基本操作和vim指令

在我们的手机端或者Windows上下载软件&#xff0c;可以在相应的应用商店或者官网进行下载&#xff0c;这样对于用户来说十分的方便和便捷。而在Linux上&#xff0c;也有类似的安装方式&#xff0c;我们来一一了解一下。 Linux安装软件的3种方法 源代码安装 在Linux下安装软件…

C++ CUDA开发入门

CUDA开发笔记 文章目录 CUDA开发笔记[toc]1 概述2 环境3 命令行编译4 CMAKE引入CUDA5 vscode开发CUDA6 Qt中使用CUDA-CMake7 QMake配置CUDA8 核函数9 核函数调用9.1 核函数调用语法9.2 执行配置参数详解9.3 关键调用步骤9.4 重要注意事项9.5 调用示例分析9.6 最佳实践建议 10 线…

llm开发框架新秀

原文链接:https://i68.ltd/notes/posts/20250404-llm-framework3/ google开源ADK-Agent Development Kit 开源的、代码优先的 Python 工具包&#xff0c;用于构建、评估和部署具有灵活性和控制力的复杂智能体项目仓库:https://github.com/google/adk-python 2.6k项目文档:Age…

VM——相机拍照失败

1、问题&#xff1a;相机频闪触发&#xff0c;在MVS中正常出图&#xff0c;在VM中出现拍照失败 2、解决&#xff1a; 1、首先排查网络设置&#xff08;巨帧是否设置&#xff09; 2、电脑的所有防火墙是否关闭 3、在MVS中恢复相机的设置参数为默认参数&#xff0c;删除VM中的全…

【时频谱分析】小波分析

算法配置页面&#xff0c;也可以一键导出结果数据 报表自定义绘制 获取和下载【PHM学习软件PHM源码】的方式 获取方式&#xff1a;Docshttps://jcn362s9p4t8.feishu.cn/wiki/A0NXwPxY3ie1cGkOy08cru6vnvc

怎么免费下载GLTF/GLB格式模型文件,还可以在线编辑修改

​ 现在非常流行glb格式模型&#xff0c;和gltf格式文件&#xff0c;可是之类模型网站非常非常少 1&#xff0c;咱们先直接打开http://glbxz.com 官方glb下载网站 glbxz.com 2 可以搜索&#xff0c;自己想要的模型关键词 3&#xff0c;到自己想下载素材页面 4&#xff0c;…

【6】深入学习http模块(万字)-Nodejs开发入门

深入学习http模块 前言http一个Web服务器项目创建代码运行代码解析 Server属性&#xff1a;keepAlive属性&#xff1a;keepAliveTimeout属性&#xff1a;maxHeaderSize属性&#xff1a;requestTimeout属性&#xff1a;maxRequestsPerSocket方法&#xff1a;close()方法&#xf…

buuctf sql注入类练习

BUU SQL COURSE 1 1 实例无法访问 / Instance cant be reached at that time | BUUCTF但是这个地方很迷惑就是这个 一个 # 我们不抓包就不知道这个是sql注入类的判断是 get 类型的sql注入直接使用sqlmap我们放入到1.txt中 目的是 优先检测 ?id1>python3 sqlmap.py -r 1.t…

(即插即用模块-特征处理部分) 三十二、(TGRS 2024) MDAF 多尺度双表示对齐过滤器

文章目录 1、Multiscale Dual-Representation Alignment Filter2、代码实现 paper&#xff1a;SFFNet: A Wavelet-Based Spatial and Frequency Domain Fusion Network for Remote Sensing Segmentation Code&#xff1a;https://github.com/yysdck/SFFNet 1、Multiscale Dual-…