mmap詳解

mmap詳解

  • mmap基礎概念
  • mmap內存映射原理
  • mmap相關函數調用
  • mmap的使用細節
  • mmap和常規文件操作的區別

mmap基礎概念

mmap是一種內存映射文件的方法,即將一個文件或者其它對象映射到進程的地址空間,實現文件磁盤地址和進程虛擬地址空間中一段虛擬地址的一一對映關系。實現這樣的映射關系后,進程就可以采用指針的方式讀寫操作這一段內存,而系統會自動回寫臟頁面到對應的文件磁盤上,即完成了對文件的操作而不必再調用readwrite等系統調用函數。相反,內核空間對這段區域的修改也直接反映用戶空間,從而可以實現不同進程間的文件共享。mmap 還可以用于實現共享內存,允許不同進程間共享數據,如下圖所示:
在這里插入圖片描述
我們知道,在進程虛擬地址空間中,內存映射部分是處于堆棧之間的,linux內核使用vm_area_struct結構來表示一個獨立的虛擬內存區域,由于每個不同質的虛擬內存區域功能和內部機制都不同,因此一個進程使用多個vm_area_struct結構來分別表示不同類型的虛擬內存區域。各個vm_area_struct結構使用鏈表或者樹形結構鏈接,方便進程快速訪問,如下圖所示:
在這里插入圖片描述
mm_struct就是進程用戶空間的抽象,一個進程只有一個mm_struct結構,當一個mm_struct結構卻可以為多個進程所共享,例如當一個進程創建一個子進程時(vforkclone),子進程與父進程共享一個mm_structmm_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,其原型為:int mmap(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 :一個提示地址,表示希望映射區域開始的地址。然?,這個地址可能會被內核忽略,特別是當我們沒有足夠的權限來請求特定的地址時。如果 addrNULL ,則系統會?動選擇?個合適的地址;
  • 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_ANONYMOUSMAP_ANON 標志來指定匿名映射,此時 fd 參數會被忽略);
  • off_t offset : ?件中的起始偏移量,即映射區域的開始位置。 offsetlength 一起定義了映射區域在文件中的位置和大小。

相關函數:int munmap( void * addr, size_t len )

  • 成功執行時,munmap返回0。失敗時,munmap返回-1error返回標志和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來進行填充的。
在這里插入圖片描述
此時:

  1. 讀 / 寫前 5000 個字節,也就是0 ~ 4999會返回操作文件的內容;
  2. 讀 5000 ~ 8191 的數據,返回的是0,寫 5000 ~ 8191 的數據,程序不會有任何報錯,但是不會將數據寫入到原文件當中;
  3. 讀 / 寫 8191 以外的部分,就會返回一個 SIGSEGV 信號。

場景二:一個文件的大小是 5000 字節,mmap函數從一個文件的起始位置開始,映射 15000 字節到虛擬內存中,即映射大小超過了原始文件的大小。

由于原文件大小為 5000 字節,在 0 ~ 8191 之間跟場景一一樣,但是系統要求 mmap 映射 15000 字節大小,而文件大小只占2個物理頁,所以在 8191 ~ 15000 之間的字節不能讀寫,會返回信號異常的錯誤。
在這里插入圖片描述
此時:

  1. 對于 0 ~ 8191 字節之間數據操作跟場景一相同;
  2. 因為原文件只占兩個物理頁,所以對 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函數以后,創建新的虛擬內存區域和創建虛擬內存區域與磁盤文件之間的映射關系這兩步并沒有進行任何的拷貝操作,而是當訪問對應的的內存區域發現沒有可以訪問的數據時,此時會觸發缺頁異常,就會將對應的數據從磁盤拷貝到內存當中,然后根據對應的映射關系去進行訪問即可,這期間其實也就進行了一次數據的拷貝工作,提高了對應的效率。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/78495.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/78495.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/78495.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Vue3的內置組件 -實現過渡動畫 TransitionGroup

Vue3的內置組件 -實現過渡動畫 TransitionGroup 是一個內置組件&#xff0c;用于對 v-for 列表中的元素或組件的插入、移除和順序改變添加動畫效果 支持和 基本相同的 props、CSS 過渡 class 和 JavaScript 鉤子監聽器&#xff0c;但有以下幾點區別&#xff1a; 默認情況下&…

【軟考-架構】14、軟件可靠性基礎

?資料&文章更新? GitHub地址&#xff1a;https://github.com/tyronczt/system_architect 文章目錄 軟件可靠性基本概念軟件可靠性建模軟件可靠性管理軟件可靠性設計N版本程序設計恢復塊設計&#xff08;動態冗余&#xff09;雙機容錯技術、集群技術負載均衡軟件可靠性測試…

使用Python+OpenCV對視頻抽幀保存為JPG圖像

使用PythonOpenCV對視頻抽幀保存為JPG圖像 import os import cv2 import time#視頻文件夾路徑&#xff0c;可修改 videoPath D:\\video\\ #保存的圖片文件夾路徑&#xff0c;可修改 savePath D:\\images\\ videolist os.listdir(videoPath) if not os.path.exists(savePath…

學習整理在centos7上安裝mysql8.0版本教程

學習整理在centos7上安裝mysql8.0版本教程 查看linux系統版本下載mysql數據庫安裝環境檢查解壓mysql安裝包創建MySQL需要的目錄及授權新增用戶組新增組用戶配置mysql環境變量編寫MySQL配置文件初始化數據庫初始化msyql服務啟動mysql修改初始化密碼配置Linux 系統服務工具,使My…

DeepSeek預訓練追求極致的訓練效率的做法

DeepSeek在預訓練階段通過多種技術手段實現了極致的訓練效率,其中包括采用FP8混合精度訓練框架以降低計算和內存需求 ,創新性地引入Multi-head Latent Attention(MLA)壓縮KV緩存以提升推理效率,以及基于Mixture-of-Experts(MoE)的稀疏計算架構以在保證性能的同時顯著降低…

【計算機視覺】CV項目實戰- 深度解析TorchVision_Maskrcnn:基于PyTorch的實例分割實戰指南

深度解析TorchVision_Maskrcnn&#xff1a;基于PyTorch的實例分割實戰指南 技術背景與核心原理Mask R-CNN架構解析項目特點 完整實戰流程環境準備硬件要求軟件依賴 數據準備與標注1. 圖像采集2. 數據標注3. 數據格式轉換 模型構建與訓練1. 模型初始化2. 數據加載器配置3. 訓練優…

x86系列CPU寄存器和匯編指令總結

文章目錄 概要一、寄存器1.1、8086寄存器1.2、通用寄存器1.3、擴展寄存器 二、指令集三、x86指令集常見指令使用說明四、匯編4.1、匯編語法4.2、nsam匯編 五、參考 概要 在對學習Go的過程中&#xff0c;涉及到了匯編&#xff0c;因此對X86系列CPU的背景、寄存器、匯編指令做了一…

戴維斯雙擊選股公式如何編寫?

戴維斯雙擊&#xff0c;指的是營收增長和凈利潤增長同步&#xff0c;并有超預期的財務狀況。 戴維斯雙擊是指在低市盈率&#xff08;P/E&#xff09;時買入股票&#xff0c;待公司盈利增長和市盈率提升后賣出&#xff0c;以獲取雙重收益。以下是一個簡單的通達信選股模型示例&…

前端面試寶典---vue原理

vue的Observer簡化版 class Observer {constructor(value) {if (!value || typeof value ! object) returnthis.walk(value) // 對對象的所有屬性進行遍歷并定義響應式}walk (obj) {Object.keys(obj).forEach(key > defineReactive(obj, key, obj[key]))} } // 定義核心方法…

從“聾啞設備“到超級工廠:EtherCAT轉Modbus協議網關正在重構工業未來

當全球工廠加速邁向工業4.0&#xff0c;您的生產線是否因Modbus設備“拖后腿”而被迫降速&#xff1f;無需百萬改造&#xff01;無需淘汰設備&#xff01;一套EtherCAT從站轉Modbus協議網關&#xff0c;讓30年老機床與智能工廠實時對話&#xff0c;效率飆升300%&#xff01; 一…

Tauri文件系統操作:桌面應用的核心能力(入門系列四)

今天我們來聊聊Tauri中一個超級重要的功能 - 文件系統操作。這可是Web應用和桌面應用最大的區別之一。在瀏覽器里&#xff0c;出于安全考慮&#xff0c;我們對文件系統的訪問被限制得死死的。但在Tauri桌面應用中&#xff0c;我們可以安全地訪問用戶的文件系統&#xff0c;這簡…

Python解析地址中省市區街道

Python解析地址中省市區街道 1、效果 輸入&#xff1a;海珠區沙園街道西基村 輸出&#xff1a; 2、導入庫 pip install jionlp3、示例代碼 import jionlp as jiotext 海珠區沙園街道西基村 res jio.parse_location(text, town_villageTrue) print(res)

基于Node+HeadlessBrowser的瀏覽器自動化方案

基于NodeHeadlessBrowser的瀏覽器自動化方案 什么是無頭瀏覽器(Headless Browser)&#xff1f; 無頭瀏覽器&#xff0c;就像是一個沒有用戶界面的瀏覽器程序。你可以想象它就是一個“隱形”的瀏覽器&#xff0c;只不過它沒有圖形界面&#xff0c;但能做我們用普通瀏覽器所能做…

AEB法規升級后的市場預測與分析:技術迭代、政策驅動與產業變革

文章目錄 一、政策驅動&#xff1a;全球法規升級倒逼市場擴容二、技術迭代&#xff1a;從“基礎防護”到“場景全覆蓋”三、市場格局&#xff1a;競爭加劇與生態重構四、挑戰與未來展望五、投資建議結語 近年來&#xff0c;全球汽車安全法規的加速升級正深刻重塑AEB&#xff08…

【Docker項目實戰】使用Docker部署Caddy+vaultwarden密碼管理工具(詳細教程)

【Docker項目實戰】使用Docker部署vaultwarden密碼管理工具 前言一、vaultwarden介紹1.1 vaultwarden簡介1.2 主要特點二、本次實踐規劃2.1 本地環境規劃2.2 本次實踐介紹三、本地環境檢查3.1 檢查Docker服務狀態3.2 檢查Docker版本3.3 檢查docker compose 版本四、拉取鏡像五、…

第十六屆藍橋杯大賽軟件賽省賽第二場

第十六屆藍橋杯大賽軟件賽省賽第二場 大家好。最近參加了第十六屆藍橋杯大賽軟件賽省賽第二場 Python 大學 B 組的比賽&#xff0c;現在來和大家分享一下我的解題思路和代碼實現。以下內容是我自己寫的&#xff0c;可能對也可能錯&#xff0c;歡迎大家交流討論。 試題 A&…

硬件須知的基本問題2

目錄 1、典型電路 1. DC5V 轉 DC3.3V 電路 2. 通信電路 2、STM32F103RCT6 最小系統如何設計搭建電路 1. 電源電路 2. 復位電路 3. 時鐘電路 4. 下載電路 5. 單片機連接連接 3、請列舉你所知道的二極管型號&#xff1f; 1. 整流二極管 2. 小信號二極管 3. 肖特基二極管 4. 超…

力扣HOT100——102.二叉樹層序遍歷

給你二叉樹的根節點 root &#xff0c;返回其節點值的 層序遍歷 。 &#xff08;即逐層地&#xff0c;從左到右訪問所有節點&#xff09;。 示例 1&#xff1a; 輸入&#xff1a;root [3,9,20,null,null,15,7] 輸出&#xff1a;[[3],[9,20],[15,7]] /*** Definition for a bi…

CSS 定位學習筆記

一、定位概述 CSS 定位是控制 HTML 元素在頁面中位置的核心技術&#xff0c;允許元素脫離正常文檔流&#xff0c;實現復雜布局效果。 二、定位類型對比 定位類型屬性值參考基準是否脫離文檔流常用場景靜態定位static無否默認布局相對定位relative自身原位置否元素微調絕對定…

Threejs中頂視圖截圖

Threejs中頂視圖截圖 一般項目中的每個模型&#xff0c;都需要有一張對應的圖片&#xff0c;一般是頂視圖&#xff0c;在對應的2D場景場景中展示。以下分享一個實現方式&#xff0c;先將清空模型材質的紋理&#xff0c;把顏色設置為白色&#xff0c;使用正交相機截取頂視圖&am…