mmap详解
mmap详解
- mmap基础概念
- mmap内存映射原理
- mmap相关函数调用
- mmap的使用细节
- mmap和常规文件操作的区别
mmap基础概念
mmap
是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必再调用read
,write
等系统调用函数。相反,内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程间的文件共享。mmap
还可以用于实现共享内存,允许不同进程间共享数据,如下图所示:
我们知道,在进程虚拟地址空间中,内存映射部分是处于堆栈之间的,linux内核使用vm_area_struct
结构来表示一个独立的虚拟内存区域,由于每个不同质的虚拟内存区域功能和内部机制都不同,因此一个进程使用多个vm_area_struct
结构来分别表示不同类型的虚拟内存区域。各个vm_area_struct
结构使用链表或者树形结构链接,方便进程快速访问,如下图所示:
mm_struct
就是进程用户空间的抽象,一个进程只有一个mm_struct
结构,当一个mm_struct
结构却可以为多个进程所共享,例如当一个进程创建一个子进程时(vfork
或clone
),子进程与父进程共享一个mm_struct
,mm_struct
的代码就如下:
struct mm_struct {struct vm_area_struct *mmap; /* list of VMAs */ //指向VMA对象的链表头struct rb_root mm_rb; //指向VMA对象的红黑树的根u64 vmacache_seqnum; /* per-thread vmacache */
#ifdef CONFIG_MMUunsigned long (*get_unmapped_area) (struct file *filp,unsigned long addr, unsigned long len,unsigned long pgoff, unsigned long flags); // 在进程地址空间中搜索有效线性地址区间的方法
#endifunsigned long mmap_base; /* base of mmap area */unsigned long mmap_legacy_base; /* base of mmap area in bottom-up allocations */
#ifdef CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES/* Base adresses for compatible mmap() */unsigned long mmap_compat_base;unsigned long mmap_compat_legacy_base;
#endifunsigned long task_size; /* size of task vm space */unsigned long highest_vm_end; /* highest vma end address */pgd_t * pgd; //指向页全局目录/*** @mm_users: The number of users including userspace.** Use mmget()/mmget_not_zero()/mmput() to modify. When this drops* to 0 (i.e. when the task exits and there are no other temporary* reference holders), we also release a reference on @mm_count* (which may then free the &struct mm_struct if @mm_count also* drops to 0).*/atomic_t mm_users; //使用计数器/*** @mm_count: The number of references to &struct mm_struct* (@mm_users count as 1).** Use mmgrab()/mmdrop() to modify. When this drops to 0, the* &struct mm_struct is freed.*/atomic_t mm_count; //使用计数器atomic_long_t nr_ptes; /* PTE page table pages */ //进程页表数
#if CONFIG_PGTABLE_LEVELS > 2atomic_long_t nr_pmds; /* PMD page table pages */
#endifint map_count; /* number of VMAs */ //VMA的个数spinlock_t page_table_lock; /* Protects page tables and some counters */struct rw_semaphore mmap_sem;struct list_head mmlist; /* List of maybe swapped mm's. These are globally strung* together off init_mm.mmlist, and are protected* by mmlist_lock*/unsigned long hiwater_rss; /* High-watermark of RSS usage */unsigned long hiwater_vm; /* High-water virtual memory usage */unsigned long total_vm; /* Total pages mapped */ //进程地址空间的页数unsigned long locked_vm; /* Pages that have PG_mlocked set */ //锁住的页数,不能换出unsigned long pinned_vm; /* Refcount permanently increased */unsigned long data_vm; /* VM_WRITE & ~VM_SHARED & ~VM_STACK */ //数据段内存的页数unsigned long exec_vm; /* VM_EXEC & ~VM_WRITE & ~VM_STACK */ //可执行内存映射的页数unsigned long stack_vm; /* VM_STACK */ //用户态堆栈的页数unsigned long def_flags;unsigned long start_code, end_code, start_data, end_data; //代码段,数据段等的地址unsigned long start_brk, brk, start_stack; //堆栈段的地址,start_stack表示用户态堆栈的起始地址,brk为堆的当前最后地址unsigned long arg_start, arg_end, env_start, env_end; //命令行参数的地址,环境变量的地址unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv *//** Special counters, in some configurations protected by the* page_table_lock, in other configurations by being atomic.*/struct mm_rss_stat rss_stat;struct linux_binfmt *binfmt;cpumask_var_t cpu_vm_mask_var;/* Architecture-specific MM context */mm_context_t context;unsigned long flags; /* Must use atomic bitops to access the bits */struct core_state *core_state; /* coredumping support */
#ifdef CONFIG_MEMBARRIERatomic_t membarrier_state;
#endif
#ifdef CONFIG_AIOspinlock_t ioctx_lock;struct kioctx_table __rcu *ioctx_table;
#endif
#ifdef CONFIG_MEMCG/** "owner" points to a task that is regarded as the canonical* user/owner of this mm. All of the following must be true in* order for it to be changed:** current == mm->owner* current->mm != mm* new_owner->mm == mm* new_owner->alloc_lock is held*/struct task_struct __rcu *owner;
#endifstruct user_namespace *user_ns;/* store ref to file /proc/<pid>/exe symlink points to */struct file __rcu *exe_file;
#ifdef CONFIG_MMU_NOTIFIERstruct mmu_notifier_mm *mmu_notifier_mm;
#endif
#if defined(CONFIG_TRANSPARENT_HUGEPAGE) && !USE_SPLIT_PMD_PTLOCKSpgtable_t pmd_huge_pte; /* protected by page_table_lock */
#endif
#ifdef CONFIG_CPUMASK_OFFSTACKstruct cpumask cpumask_allocation;
#endif
#ifdef CONFIG_NUMA_BALANCING/** numa_next_scan is the next time that the PTEs will be marked* pte_numa. NUMA hinting faults will gather statistics and migrate* pages to new nodes if necessary.*/unsigned long numa_next_scan;/* Restart point for scanning and setting pte_numa */unsigned long numa_scan_offset;/* numa_scan_seq prevents two threads setting pte_numa */int numa_scan_seq;
#endif/** An operation with batched TLB flushing is going on. Anything that* can move process memory needs to flush the TLB when moving a* PROT_NONE or PROT_NUMA mapped page.*/atomic_t tlb_flush_pending;
#ifdef CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH/* See flush_tlb_batched_pending() */bool tlb_flush_batched;
#endifstruct uprobes_state uprobes_state;
#ifdef CONFIG_HUGETLB_PAGEatomic_long_t hugetlb_usage;
#endifstruct work_struct async_put_work;#if IS_ENABLED(CONFIG_HMM)/* HMM needs to track a few things per mm */struct hmm *hmm;
#endif
} __randomize_layout;
struct vm_area_struct
用于描述进程地址空间中的一段虚拟区域,每一个VMA都对应一个struct vm_area_struct
。
/** This struct defines a memory VMM memory area. There is one of these* per VM-area/task. A VM area is any part of the process virtual memory* space that has a special rule for the page-fault handlers (ie a shared* library, the executable area etc).*/
struct vm_area_struct {/* The first cache line has the info for VMA tree walking. */unsigned long vm_start; /* Our start address within vm_mm. */ //起始地址unsigned long vm_end; /* The first byte after our end addresswithin vm_mm. */ //结束地址,区间中不包含结束地址/* linked list of VM areas per task, sorted by address */ //按起始地址排序的链表struct vm_area_struct *vm_next, *vm_prev;struct rb_node vm_rb; //红黑树节点/** Largest free memory gap in bytes to the left of this VMA.* Either between this VMA and vma->vm_prev, or between one of the* VMAs below us in the VMA rbtree and its ->vm_prev. This helps* get_unmapped_area find a free area of the right size.*/unsigned long rb_subtree_gap;/* Second cache line starts here. */struct mm_struct *vm_mm; /* The address space we belong to. */pgprot_t vm_page_prot; /* Access permissions of this VMA. */unsigned long vm_flags; /* Flags, see mm.h. *//** For areas with an address space and backing store,* linkage into the address_space->i_mmap interval tree.*/struct {struct rb_node rb;unsigned long rb_subtree_last;} shared;/** A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma* list, after a COW of one of the file pages. A MAP_SHARED vma* can only be in the i_mmap tree. An anonymous MAP_PRIVATE, stack* or brk vma (with NULL file) can only be in an anon_vma list.*/struct list_head anon_vma_chain; /* Serialized by mmap_sem &* page_table_lock */struct anon_vma *anon_vma; /* Serialized by page_table_lock *//* Function pointers to deal with this struct. */const struct vm_operations_struct *vm_ops;/* Information about our backing store: */unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZEunits */struct file * vm_file; /* File we map to (can be NULL). */ //指向文件的一个打开实例void * vm_private_data; /* was vm_pte (shared mem) */atomic_long_t swap_readahead_info;
#ifndef CONFIG_MMUstruct vm_region *vm_region; /* NOMMU mapping region */
#endif
#ifdef CONFIG_NUMAstruct mempolicy *vm_policy; /* NUMA policy for the VMA */
#endifstruct vm_userfaultfd_ctx vm_userfaultfd_ctx;
} __randomize_layout;
我们的mmap
函数在使用的过程中就是要创建一个新的vm_area_struct
,并将其与文件的物理磁盘地址相连,关系图如下图:
mmap内存映射原理
mmap内存映射的实现过程,总的来说可以分为三个阶段:
- 进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域
-
1. 进程在用户空间调用库函数
mmap
; -
2. 在当前进程的虚拟地址空间中,寻找一段空闲的满足要求的连续的虚拟地址;
-
3. 为此虚拟区分配一个
vm_area_struct
结构,接着对这个结构的各个域进行了初始化; -
4. 将新建的虚拟区结构(
vm_area_struct
)插入进程的虚拟地址区域链表或树中。
- 调用内核空间的系统调用函数
mmap
(不同于用户空间函数),实现文件物理地址和进程虚拟地址的一一映射关系
- 5. 为映射分配了新的虚拟地址区域后,通过待映射的文件指针,在文件描述符表中找到对应的文件描述符,通过文件描述符,链接到内核“已打开文件集”中该文件的文件结构体(
struct file
),每个文件结构体维护着和这个已打开文件相关各项信息。 - 6. 通过该文件的文件结构体,链接到
file_operations
模块,调用内核函数mmap
,其原型为:intmmap(struct file *filp, struct vm_area_struct *vma)
,不同于用户空间库函数。 - 7. 内核
mmap
函数通过虚拟文件系统inode
模块定位到文件磁盘物理地址。 - 8. 通过
remap_pfn_range
函数建立页表,即实现了文件地址和虚拟地址区域的映射关系。此时,这片虚拟地址并没有任何数据关联到主存中。
注意:前两个阶段仅在于创建虚拟区间并完成地址映射,但是并没有将任何文件数据的拷贝至主存。真正的文件读取是当进程发起读或写操作时,也就是接下来这个阶段。
- 进程发起对这片映射空间的访问,引发缺页异常,实现文件内容到物理内存(主存)的拷贝
- 9. 进程的读或写操作访问虚拟地址空间这一段映射地址,通过查询页表,发现这一段地址并不在物理页面上。因为目前只建立了地址映射,真正的硬盘数据还没有拷贝到内存中,因此引发缺页异常;
- 10. 缺页异常进行一系列判断,确定无非法操作后,内核发起请求调页过程;
- 11. 调页过程先在交换缓存空间(
swap cache
)中寻找需要访问的内存页,如果没有则调用nopage
函数把所缺的页从磁盘装入到主存中。 - 12. 之后进程即可对这片主存进行读或者写的操作,如果写操作改变了其内容,一定时间后系统会自动回写脏页面到对应磁盘地址,也即完成了写入到文件的过程。
注意:修改过的脏页面并不会立即更新回文件中,而是有一段时间的延迟,可以调用msync()来强制同步, 这样所写的内容就能立即保存到文件里了。
mmap相关函数调用
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t length);
返回值说明:
- 成功执行时,
mmap
返回被映射区的指针。失败时,mmap
返回MAP_FAILED
[其值为(void *)-1
],error
被设为以下的某个值:
1 EACCES:访问出错2 EAGAIN:文件已被锁定,或者太多的内存已被锁定3 EBADF:fd不是有效的文件描述符4 EINVAL:一个或者多个参数无效5 ENFILE:已达到系统对打开文件的限制6 ENODEV:指定文件所在的文件系统不支持内存映射7 ENOMEM:内存不足,或者进程已超出最大内存映射数量8 EPERM:权能不足,操作不允许9 ETXTBSY:已写的方式打开文件,同时指定MAP_DENYWRITE标志
10 SIGSEGV:试着向只读区写入
11 SIGBUS:试着访问不属于进程的内存区
参数说明:
void *addr
:一个提示地址,表示希望映射区域开始的地址。然⽽,这个地址可能会被内核忽略,特别是当我们没有足够的权限来请求特定的地址时。如果addr
是NULL
,则系统会⾃动选择⼀个合适的地址;size_t length
: 要映射到进程地址空间中的字节数。这个长度必须是系统页面大小的整数倍(通常是 4KB ,但可能因系统而异)。如果指定的length
不是页面大小的整数倍,系统可能会向上舍入到最近的页面大小边界(系统内存页大小为4KB(即4096字节),而请求的内存大小为3500字节,则按照向上舍入的原则,应分配4096字节的内存);int prot
: 指定了映射区域的内存保护属性。可以是以下值的组合(使用按位或运算符|
):
—PROT_READ
:映射区域可读。
—PROT_WRITE
:映射区域可写。
—PROT_EXEC
:映射区域可执行。
—PROT_NONE
:页不可访问int flags
: 指定了映射的类型和其他选项,可以是一下位的组合值;
1 MAP_FIXED //使用指定的映射起始地址,如果由start和len参数指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。如果指定的起始地址不可用,操作将会失败。并且起始地址必须落在页的边界上。2 MAP_SHARED //与其它所有映射这个对象的进程共享映射空间。对共享区的写入,相当于输出到文件。直到msync()或者munmap()被调用,文件实际上不会被更新。3 MAP_PRIVATE //建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。这个标志和以上标志是互斥的,只能使用其中一个。4 MAP_DENYWRITE //这个标志被忽略。5 MAP_EXECUTABLE //同上6 MAP_NORESERVE //不要为这个映射保留交换空间。当交换空间被保留,对映射区修改的可能会得到保证。当交换空间不被保留,同时内存不足,对映射区的修改会引起段违例信号。7 MAP_LOCKED //锁定映射区的页面,从而防止页面被交换出内存。8 MAP_GROWSDOWN //用于堆栈,告诉内核VM系统,映射区可以向下扩展。9 MAP_ANONYMOUS //匿名映射,映射区不与任何文件关联。
10 MAP_ANON //MAP_ANONYMOUS的别称,不再被使用。
11 MAP_FILE //兼容标志,被忽略。
12 MAP_32BIT //将映射区放在进程地址空间的低2GB,MAP_FIXED指定时会被忽略。当前这个标志只在x86-64平台上得到支持。
13 MAP_POPULATE //为文件映射通过预读的方式准备好页表。随后对映射区的访问不会被页违例阻塞。
14 MAP_NONBLOCK //仅和MAP_POPULATE一起使用时才有意义。不执行预读,只为已存在于内存中的页面建立页表入口。
int fd
: ⼀个有效的文件描述符,指向要映射的文件或设备。对于匿名映射,这个参数可以是-1
(在某些系统上,也可以使用MAP_ANONYMOUS
或MAP_ANON
标志来指定匿名映射,此时fd
参数会被忽略);off_t offset
: ⽂件中的起始偏移量,即映射区域的开始位置。offset
和length
一起定义了映射区域在文件中的位置和大小。
相关函数:int munmap( void * addr, size_t len )
- 成功执行时,
munmap
返回0
。失败时,munmap
返回-1
,error
返回标志和mmap
一致; - 该调用在进程地址空间中解除一个映射关系,
addr
是调用mmap
时返回的地址,len
是映射区的大小; - 当映射关系解除后,对原来映射地址的访问将导致段错误发生。
接下来我们来daemon一段代码验证一下:
写入映射
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <iostream>
#include <unistd.h>
#include <sys/mman.h>#define SIZE 4096int main(int argc, char *argv[])
{if (argc != 2){std::cerr << "Usage: " << argv[0] << "filename" << std::endl;return 1;}std::string filename = argv[1];// 首先需要打开一个文件,要成功写入文件映射,这里打开文件的模式必须是:O_RWDRint fd = ::open(filename.c_str(), O_CREAT | O_RDWR, 0666);if (fd < 0){std::cerr << "open failed!!!" << std::endl;return 2;}// 默认文件大小是0,无法与mmap形成文件映射,这里需要手动设置文件大小::ftruncate(fd, SIZE);char *mmap_addr = (char *)::mmap(nullptr, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (mmap_addr == MAP_FAILED){perror("mmap error");return 3;}// 对文件进行操作for (int i = 0; i < SIZE; i++){mmap_addr[i] = 'a' + i % 26;}// 取消文件映射::munmap(mmap_addr, SIZE);// 关闭文件::close(fd);return 0;
}
读取映射
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <iostream>
#include <unistd.h>
#include <sys/mman.h>#define SIZE 4096int main(int argc, char *argv[])
{if (argc != 2){std::cerr << "Usage: " << argv[0] << "filename" << std::endl;return 1;}std::string filename = argv[1];// 首先需要打开一个文件,要成功写入文件映射,这里打开文件的模式必须是:O_RWDRint fd = ::open(filename.c_str(), O_RDONLY);if (fd < 0){std::cerr << "open failed!!!" << std::endl;return 2;}// 获取真实文件大小struct stat st;::fstat(fd, &st);char *mmap_addr = (char *)::mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0);if (mmap_addr == MAP_FAILED){perror("mmap error");return 3;}std::cout << mmap_addr << std::endl;// 取消文件映射::munmap(mmap_addr, st.st_size);// 关闭文件::close(fd);return 0;
}
mmap的使用细节
- 使用
mmap
需要注意的一个关键点是,mmap
映射区域大小必须是物理页大小(page_size
)的整倍数(32位系统中通常是4k字节)。原因是,内存的最小粒度是页,而进程虚拟地址空间和内存的映射也是以页为单位。为了匹配内存的操作,mmap
从磁盘到虚拟地址空间的映射也必须是页; - 内核可以跟踪被内存映射的底层对象(文件)的大小,进程可以合法的访问在当前文件大小以内又在内存映射区以内的那些字节。也就是说,如果文件的大小一直在扩张,只要在映射区域范围内的数据,进程都可以合法得到,这和映射建立时文件的大小无关;
- 映射建立之后,即使文件关闭,映射依然存在。因为映射的是磁盘的地址,不是文件本身,和文件句柄无关。同时可用于进程间通信的有效地址空间不完全受限于被映射文件的大小,因为是按页映射。
场景一:一个文件的大小是 5000 字节,mmap函数从一个文件的起始位置开始,映射 5000 字节到虚拟内存中。
在32位系统下,一个物理页面所占的大小是 4KB,也就是 4096 字节,如果 mmap 需要将这5000字节的数据映射到虚拟内存当中,就需要映射 8KB 大小,也就是 8192 字节大小,也就是说,在 mmap 函数执行以后,实际上映射到虚拟内存当中大小为 8192 字节大小,对于第5000 ~ 8191字节的数据是以0来进行填充的。
此时:
- 读 / 写前 5000 个字节,也就是0 ~ 4999会返回操作文件的内容;
- 读 5000 ~ 8191 的数据,返回的是0,写 5000 ~ 8191 的数据,程序不会有任何报错,但是不会将数据写入到原文件当中;
- 读 / 写 8191 以外的部分,就会返回一个 SIGSEGV 信号。
场景二:一个文件的大小是 5000 字节,mmap函数从一个文件的起始位置开始,映射 15000 字节到虚拟内存中,即映射大小超过了原始文件的大小。
由于原文件大小为 5000 字节,在 0 ~ 8191 之间跟场景一一样,但是系统要求 mmap 映射 15000 字节大小,而文件大小只占2个物理页,所以在 8191 ~ 15000 之间的字节不能读写,会返回信号异常的错误。
此时:
- 对于 0 ~ 8191 字节之间数据操作跟场景一相同;
- 因为原文件只占两个物理页,所以对 8191 ~ 15000 字节之间的不能进行读写,否则就会返回SIGBUS信号,同样,对于 15000 字节以外的进行读写,会返回一个 SIGSEGV 信号。
场景三:一个文件初始大小为0,使用mmap操作映射了1000*4K的大小,即1000个物理页大约4M字节空间,mmap返回指针ptr
- 如果在文件建立映射之初,就直接对文件进行读写操作,因为此时文件大小为0,没有映射对应合理的物理页,就会如图场景二一样返回一个SIGBUS信号;
- 但是当映射建立完成以后,已经返回一个 ptr 指针,此时每次操作 ptr 读写之前,先增加文件的大小,那么 ptr 在文件内部操作就是合法的,比如文件扩充 4096 个字节,那么此时 ptr 就能操作
([ptr ~ (char*)ptr + 4095])
之间的数据,只要访问操作最终实在 1000 个映射空间大小范围内的。
mmap和常规文件操作的区别
- 常规的文件操作(
read / write
)这些操作,是使用了页缓存机制的,也就是说,我们在调取read
函数时,首先是会将磁盘的数据给写到页缓存当中的(内核识别到缺页异常才会有),此时就完成了一次拷贝,但是页缓存是处于内核当中的,用户又不能直接进行访问,所以又需要将这部分数据拷贝到用户空间当中,这就进行了两次拷贝;同样,write
函数也是一样的道理,首先会将数据写入到buffer当中,但是待写入的buffer并不能直接访问,所以就会将数据先写入到对应的主存当中,这就会造成一次拷贝,然后内核在选择恰当的时机将数据写入到磁盘当中,进行两次拷贝。 - 对于
mmap
来说,我们在调用mmap
函数以后,创建新的虚拟内存区域和创建虚拟内存区域与磁盘文件之间的映射关系这两步并没有进行任何的拷贝操作,而是当访问对应的的内存区域发现没有可以访问的数据时,此时会触发缺页异常,就会将对应的数据从磁盘拷贝到内存当中,然后根据对应的映射关系去进行访问即可,这期间其实也就进行了一次数据的拷贝工作,提高了对应的效率。
相关文章:
mmap详解
mmap详解 mmap基础概念mmap内存映射原理mmap相关函数调用mmap的使用细节mmap和常规文件操作的区别 mmap基础概念 mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一…...
基于大模型底座重构司法信息系统
前置篇章:法律智能体所需的基础知识 构建一个高效的法律智能体,特别是在基于RAG(Retrieval-Augmented Generation)架构的背景下,需要融合多种学科和领域的知识。以下是对法律智能体开发和应用所需核心基础知识的简要介…...
如何判断你的PyTorch是GPU版还是CPU版?
如何判断你的PyTorch是GPU版还是CPU版? PyTorch作为当前最流行的深度学习框架之一,支持在CPU和GPU(NVIDIA CUDA)上运行。对于深度学习开发者来说,正确识别PyTorch版本至关重要,因为GPU版本可以带来10-100倍的性能提升。本文将全面…...
Leetcode刷题记录19——无重复字符的最长子串
题源:https://leetcode.cn/problems/longest-substring-without-repeating-characters/description/?envTypestudy-plan-v2&envIdtop-100-liked 题目描述: 思路一: 通过两个指针,第一个指针指向字串的开头,第二…...
SpringBoot程序的创建以及特点,配置文件,LogBack记录日志,配置过滤器、拦截器、全局异常
一、创建一个SpringBoot程序 在之前写过一篇如何创建SpringBoot程序,两种方式,方法1:通过maven创建SpringBoot项目 方法2:使用Spring Initialzr创建一个SpringBoot项目(缺点:当创建项目时网络中断&#x…...
Ubuntu20.04 Ollama 配置相关
Ubuntu20.04 Ollama 配置相关 Ubuntu20.04 Ollama 配置相关ollama修改配置文件常用命令修改端口局域网访问 Ubuntu20.04 Ollama 配置相关 ollama修改配置文件常用命令 sudo gedit /etc/systemd/system/ollama.service systemctl daemon-reload systemctl restart ollama sys…...
python调用ffmpeg对截取视频片段,可批量处理
本文完全免费,非VIP文章,如果您发现需VIP可看全文,请邮箱联系我:openwebsitefoxmail.com 文章目录 python调用ffmpeg对截取视频片段,可批量处理用到的ffmpeg命令python调用bash指令的方法python处理代码准备函数python…...
【WLAN】华为无线AC双机热备负载分担—双链路热备份
配套实验拓扑可以下载学习交流:【WLAN】华为无线AC双机负载分担—双链路热备份 双链路备份的传统配置方式是在主、备AC上为AP指定对方AC的IP地址,并分别配置优先级,通过比较优先级的方式来确定主、备AC。为简化配置逻辑,新配置方式…...
学习笔记——《Java面向对象程序设计》-内部类、匿名类、异常类
参考教材: Java面向对象程序设计(第3版)微课视频版 清华大学出版社 1、内部类 类中可以有两种重要的成员:成员变量和方法。实际上Java还允许类可以有一种成员:内部类。 内部类可以使用其外嵌类中的成员变量&#x…...
BS架构与CS架构的对比分析:了解两种架构的不同特点与应用
目录 前言1. BS架构概述1.1 什么是BS架构?1.2 BS架构的主要特点 2. CS架构概述2.1 什么是CS架构?2.2 CS架构的主要特点 3. BS架构与CS架构的对比3.1 用户体验3.2 安全性3.3 适用场景 4. 结语 前言 在现代软件开发中,架构设计决定了应用的性能…...
ARM架构的微控制器总线矩阵优先级与配置
在 ARM 架构的微控制器中,总线矩阵的优先级与配置是确保多主设备(如 CPU、DMA 等)高效协同工作的关键。总线矩阵通过仲裁逻辑(Arbiter)管理主设备对共享资源的访问冲突,优先级配置直接影响系统的实时性、带宽利用率和任务响应速度。以下是总线矩阵优先级机制及配置的详细…...
高速系统设计理论基础
如前一章所述,在进行高速系统设计时,最重要的是要从基本理论出发,只有掌握了基本理论,然后才能谈到其他的设计技术和技巧。本章主要介绍微波电磁理论基础,及其在高速系统设计中的工程化分析方法和应用。通过对微波信号…...
Python-MCPServer开发
Python-MCPServer开发 使用FastMCP开发【SSE模式的MCPServer】,熟悉【McpServer编码过程】【McpServer调试方法】 1-核心知识点 1-熟悉【SSE模式的MCPServer】开发2-熟悉【stdio模式的MCPServer】开发3-熟悉【启动MCPServer】的三种方式 3.1-直接启动:python mcp_s…...
Springboot集成SSE实现消息推送+RabbitMQ解决集群环境下SSE通道跨节点事件推送问题
SSE连接介绍,SSE对比WebSocket Server-Sent Events (SSE) 是一种基于 HTTP 协议的轻量级实时通信技术,允许服务器向客户端推送数据。以下是 SSE 的主要特点: 单向通信:SSE 仅支持服务器向客户端推送数据,客户端不能通…...
Kettle学习
一、Kettle 简介 Kettle(现称为 Pentaho Data Integration)是一款开源ETL工具,支持从多种数据源抽取、转换和加载数据,广泛应用于数据仓库构建、数据迁移和清洗。其核心优势包括: 可视化操作:通过拖拽组件设计数据处理流程(转换和作业)。多数据源支持:数据库(MySQL/…...
科学养生,开启健康生活新方式
在快节奏的现代生活中,健康养生已成为人们关注的焦点。科学的养生方式不仅能增强体质,还能有效预防疾病,提升生活质量。 合理饮食是健康养生的基础。日常饮食应遵循均衡原则,保证蛋白质、碳水化合物、脂肪、维生素和矿物质的合…...
brew 安装openjdk查看其版本
使用brew(如果你使用Homebrew安装) 如果你通过Homebrew安装了OpenJDK,可以使用以下命令来查看安装的版本,: brew list --versions openjdk8 这将会列出所有通过Homebrew安装的OpenJDK版本及其版本号。 3. 查看/usr/libexec/ja…...
基于大模型对先天性幽门肥厚性狭窄预测及临床方案的研究报告
目录 一、引言 1.1 研究背景与目的 1.2 国内外研究现状 1.3 研究方法与创新点 二、先天性幽门肥厚性狭窄概述 2.1 定义与发病机制 2.2 流行病学特征 2.3 病理与解剖特点 三、大模型预测原理及构建 3.1 大模型简介 3.2 数据收集与预处理 3.3 模型训练与优化 四、大…...
【开源】基于51单片机的温湿度检测报警系统
项目说明 该设计是一个简易的基于51单片机的温湿度检测报警系统,功能说明: 使用LCD1602实时显示当前的温湿度。读取DHT11的温湿度值,如果温度大于最大设定值,LED1亮,如果温度小于最小设定值,LED2亮。如果…...
2025年4月25日第一轮
1.作文 The increasing reliance on onlineshopping has brought both convince and challenge to consumsers.It is of great nesscity for poeple to adapt better strategies to cope with these challenges.Resons and concrete evidence to support my view are as fello…...
AutoSAR从概念到实践系列之MCAL篇(二)——Mcu模块配置及代码详解(下)
欢迎大家学习我的《AutoSAR从概念到实践系列之MCAL篇》系列课程,我是分享人M哥,目前从事车载控制器的软件开发及测试工作。 学习过程中如有任何疑问,可底下评论! 如果觉得文章内容在工作学习中有帮助到你,麻烦点赞收藏评论+关注走一波!感谢各位的支持! 上一篇内容主要为…...
A. Ideal Generator
time limit per test 1 second memory limit per test 256 megabytes We call an array aa, consisting of kk positive integers, palindromic if [a1,a2,…,ak][ak,ak−1,…,a1][a1,a2,…,ak][ak,ak−1,…,a1]. For example, the arrays [1,2,1][1,2,1] and [5,1,1,5][5,…...
微信小程序核心技术栈
微信小程序核心技术栈 WXML一.WXML基础概念1.本质与特点2.与HTML的主要差异 二.数据绑定1.基础绑定2.高级绑定模式3.数据绑定限制 三.条件渲染1.基础条件判断2.多条件渲染优化3.条件渲染性能建议 四.列表渲染1.基础列表2.进阶用法3.wx:key的深度解析 五.模版系统1.定义模版2.使…...
系统架构设计中的ATAM方法:理论、实践与深度剖析
引言 在复杂系统架构设计中,如何在性能、安全性、可维护性等质量属性之间实现平衡,是每一位资深架构师必须面对的终极挑战。传统的架构评审往往依赖经验直觉或局部优化,而ATAM(Architecture Tradeoff Analysis Method,架构权衡分析方法)通过结构化分析框架,系统性解…...
模板引擎语法-过滤器
模板引擎语法-过滤器 文章目录 模板引擎语法-过滤器[toc]1.default过滤器2.default_if_none过滤器3.length过滤器4.addslashes过滤器5.capfirst过滤器6.cut过滤器7.date过滤器8.dictsort过滤器 1.default过滤器 default过滤器用于设置默认值。default过滤器对于变量的作用&…...
关于GoWeb(1)
Go Web (1) 一、网络通信与 Socket 编程 (一)Socket 编程基础 Socket 是网络通信的核心,它允许程序之间通过网络进行数据交换。在 Go 中,可以使用标准库 net 来实现 Socket 编程。 (二&…...
实现从一个微信小程序跳转到另一个微信小程序
前言: 最近在公司完成了一个两个小程序之间进行跳转的需求,将跳转方式与携带参数的方式分享给伙伴们: 代码展示: wx.navigateToMiniProgram({// 另一个程序的appIdappId: "wxbbd...",//你希望跳转到另一个小程序的目标路径&#…...
【教程】Docker运行gitlab容器
Docker运行gitlab容器 前言1.拉取gitlab镜像2.创建挂载数据卷3.运行镜像4 登录Gitalb5 访问linux的gitlab地址,输入用户名与密码 前言 在linux系统中安装gitlab的教程 1.拉取gitlab镜像 不指定 默认拉取最新版镜像 docker pull gitlab/gitlab-ce 2.创建挂载数据…...
微信小程序鲜花销售系统设计与实现
概述 在鲜花电商行业快速发展的背景下,移动端销售平台成为花店拓展业务的重要渠道。幽络源平台今日分享一款功能完善的微信小程序鲜花销售系统,该系统实现了多角色管理、在线订购、会员服务等核心功能,为鲜花行业提供了完整的电商解决方案。…...
嵌入式C设计模式---策略模式
目录 1.策略设计模式动漫详解 2.LVGL策略模式实现详解与应用 3.嵌入式中策略模式应用的优缺点 4.大话设计模式C语言实现 1.策略设计模式动漫详解 2.LVGL策略模式实现详...
打开canoe--点击capl Brower弹出错误,capl打不开
打开canoe–点击capl Brower弹出下图错误,capl打不开,友友们遇到过吗?怎么破?...
ultralytics 目标检测 混淆矩阵 背景图像 没被记录
修改 utils/metrics.py ConfusionMatrix def process_batch(self, detections, gt_bboxes, gt_cls):"""Update confusion matrix for object detection task.Args:detections (Array[N, 6] | Array[N, 7]): Detected bounding boxes and their associated inf…...
C++之map
因为前些天做了一道题:PTA:查询首都或国名-CSDN博客 这道题我和朋友的实现方式不同,想要学习学习她的这种方式,于是乎有了这篇研究 map 的文章。 先学习一下 map 的基本定义吧: map 是标准模板库(STL)中…...
【每天一个知识点】点乘(Dot Product)
点乘(Dot Product)在很多机器学习和图神经网络(GNN)中都有广泛应用,尤其在图结构重构中,它通常用来衡量节点之间的相似性或者关联性。让我们逐步深入理解点乘,尤其是在图结构重构中的应用。 1.…...
2025上海车展| 和芯星通发布覆盖车载全场景的产品方案
2025上海车展充满科技范儿,更加聚焦用户价值与安全性。智能化、电动化进一步深入融合,呈现辅助驾驶成熟量产化、舱驾融合一体化、产业链创新本土化、跨界融合生态化的趋势。 与其他辅助驾驶系统传感器相比,GNSS卫星定位能够提供独立于外部地…...
【Linux网络】构建HTTP响应与请求处理系统 - HttpResponse从理解到实现
📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…...
识破养生假象,拥抱科学健康
在这个全民热衷养生的时代,各种养生理念与方法如潮水般涌来。但其中不少是迷惑大众的 “烟雾弹”,只有识破这些养生假象,我们才能踏上真正的健康之路。 不少人觉得,养生就得靠保健品 “撑腰”。市面上,各类宣称具有…...
设计一个关键字统计程序:利用HashMap存储关键字统计信息,对用户输入的关键字进行个数统计。
思路分析 首先,在KeywordCounter类中,定义了一个包含所有Java关键字的字符串数组KEYWORDS,用于存储所有关键字。然后创建了一个Scanner对象input,用于从标准输入读取用户的输入。接下来创建了一个StringBuilder对象sb,…...
Python文件操作及数据库交互(Python File Manipulation and Database Interaction)
Python文件操作及数据库的交互 在实际开发中,文件操作与数据库交互是非常常见的任务。Python作为一种高效且灵活的编程语言,不仅提供了丰富的文件操作功能,还通过多种库与数据库进行高效交互。本文将详细介绍如何使用Python进行文件操作&…...
Eigen稀疏矩阵类 (SparseMatrix)
1. SparseMatrix 核心属性与初始化 模板参数 cpp SparseMatrix<Scalar, Options, StorageIndex> Scalar:数据类型(如 double, float)。 Options:存储格式(默认 ColMajor,可选 RowMajor࿰…...
【运维】Windows 与 Linux 中实时查看日志的命令对比详解(tail -f)
🔍 Windows 与 Linux 中实时查看日志的命令对比详解(tail -f) 在日常开发、调试、部署过程中,实时查看日志文件的更新内容是非常常见的需求。尤其是在排查后端服务问题、守护进程行为、系统异常等场景下,查看实时日志…...
巧用 Element - UI 实现图片上传按钮的智能隐藏
引言 在前端开发中,使用 Element - UI 组件库来构建用户界面是非常常见的操作。其中图片上传功能更是在许多项目中频繁出现,比如用户头像上传、商品图片上传等场景。有时候,我们会有这样的需求:当上传图片达到一定数量后…...
高级 SQL 技巧:提升数据处理能力的实用方法
在数据驱动的时代,SQL 作为操作和管理关系型数据库的标准语言,其重要性不言而喻。基础的 SQL 语句能满足日常的数据查询需求,但在处理复杂业务逻辑、进行数据分析和优化数据库性能时,就需要掌握一些高级 SQL 技巧。这些技巧不仅能提高查询效率,还能实现复杂的数据处理任务…...
2.4.5goweb项目上传到csdn的git仓库
在开始使用 Git 之前,你需要先安装它。不同操作系统的安装方法不同: (git先实战,能从仓库上传,下载之后,在听课程,记住大致流程。以后使用就知道往哪里查了) Windows:可以从Git 官方网站下载安…...
Eclipse 插件开发 3 菜单栏
Eclipse 插件开发 3 菜单栏 1 增加菜单2 指定位置3 点击事件4 二级菜单 (静态)5 二级菜单 (动态) 位置locationURI备注菜单栏menu:org.eclipse.ui.main.menu添加到传统菜单工具栏toolbar:org.eclipse.ui.main.toolbar添加到工具栏 1 增加菜单 <?xml version"1.0&quo…...
win11右键菜单改回win10模式
win11右键菜单非常不好用,经常需要点击最下方的“显示更多选项”,下面是win11右键菜单改回win10模式的方法。 按下 Win R 组合键,打开“运行”窗口,输入 cmd,此时不要急着按Enter,按 Ctrl Shift Enter …...
SpringBoot 常用注解通俗解释
SpringBoot 常用注解通俗解释 一、启动类相关 1. SpringBootApplication • 作用:这是SpringBoot项目的"总开关",放在主类上 • 通俗理解:相当于对电脑说:"开机!我要用SpringBoot了!…...
26 Arcgis软件常用工具有哪些
一、画图改图工具(矢量编辑) 挪位置工具(移动工具) 干哈的?选中要素(比如地块、道路)直接拖到新位置,或者用坐标X/Y偏移批量移动,适合“整体搬家”。 磁…...
OpenCV --- 图像预处理(七)
OpenCV — 图像预处理(七) 文章目录 OpenCV --- 图像预处理(七)十七,图像轮廓特征查找17.1 外接矩形17.2 最小外接矩形17.3 最小外接圆 十七,图像轮廓特征查找 图像轮廓特征查找其实就是他的外接轮廓。应用…...
【计算机网络】信息时代的数字神经系统
目录 前言技术背景与价值当前技术痛点解决方案概述目标读者说明 一、技术原理剖析核心概念图解核心作用讲解关键技术模块说明技术选型对比 二、实战演示环境配置要求核心代码实现案例1:Python TCP服务器案例2:网络带宽测试 运行结果验证 三、性能对比测试…...