211.xv6——3(page tables)

在本實驗室中,您將探索頁表并對其進行修改,以簡化將數據從用戶空間復制到內核空間的函數。

開始編碼之前,請閱讀xv6手冊的第3章和相關文件:

  • kernel/memlayout.h,它捕獲了內存的布局。
  • kernel/vm.c,其中包含大多數虛擬內存(VM)代碼。
  • kernel/kalloc.c,它包含分配和釋放物理內存的代碼。

1.kernel/memlayout.h

這段代碼和注釋描述了QEMU虛擬化環境中的物理內存布局,特別是RISC-V架構下的內存布局。它定義了各種硬件設備和內存區域的物理地址,以及內核如何使用這些內存區域。

// Physical memory layout// qemu -machine virt is set up like this,
// based on qemu's hw/riscv/virt.c:
//
// 00001000 -- boot ROM, provided by qemu
// 02000000 -- CLINT
// 0C000000 -- PLIC
// 10000000 -- uart0 
// 10001000 -- virtio disk 
// 80000000 -- boot ROM jumps here in machine mode
//             -kernel loads the kernel here
// unused RAM after 80000000.// the kernel uses physical memory thus:
// 80000000 -- entry.S, then kernel text and data
// end -- start of kernel page allocation area
// PHYSTOP -- end RAM used by the kernel// qemu puts UART registers here in physical memory.
#define UART0 0x10000000L
#define UART0_IRQ 10// virtio mmio interface
#define VIRTIO0 0x10001000
#define VIRTIO0_IRQ 1// local interrupt controller, which contains the timer.
#define CLINT 0x2000000L
#define CLINT_MTIMECMP(hartid) (CLINT + 0x4000 + 8*(hartid))
#define CLINT_MTIME (CLINT + 0xBFF8) // cycles since boot.// qemu puts programmable interrupt controller here.
#define PLIC 0x0c000000L
#define PLIC_PRIORITY (PLIC + 0x0)
#define PLIC_PENDING (PLIC + 0x1000)
#define PLIC_MENABLE(hart) (PLIC + 0x2000 + (hart)*0x100)
#define PLIC_SENABLE(hart) (PLIC + 0x2080 + (hart)*0x100)
#define PLIC_MPRIORITY(hart) (PLIC + 0x200000 + (hart)*0x2000)
#define PLIC_SPRIORITY(hart) (PLIC + 0x201000 + (hart)*0x2000)
#define PLIC_MCLAIM(hart) (PLIC + 0x200004 + (hart)*0x2000)
#define PLIC_SCLAIM(hart) (PLIC + 0x201004 + (hart)*0x2000)// the kernel expects there to be RAM
// for use by the kernel and user pages
// from physical address 0x80000000 to PHYSTOP.
#define KERNBASE 0x80000000L
#define PHYSTOP (KERNBASE + 128*1024*1024)// map the trampoline page to the highest address,
// in both user and kernel space.
#define TRAMPOLINE (MAXVA - PGSIZE)// map kernel stacks beneath the trampoline,
// each surrounded by invalid guard pages.
#define KSTACK(p) (TRAMPOLINE - ((p)+1)* 2*PGSIZE)// User memory layout.
// Address zero first:
//   text
//   original data and bss
//   fixed-size stack
//   expandable heap
//   ...
//   TRAPFRAME (p->trapframe, used by the trampoline)
//   TRAMPOLINE (the same page as in the kernel)
#define TRAPFRAME (TRAMPOLINE - PGSIZE)

QEMU虛擬機中的物理內存布局

QEMU模擬的機器virt的內存布局如下:

  1. 0x00001000 - 啟動ROM,由QEMU提供。
  2. 0x02000000 - CLINT (Core Local Interruptor),負責管理本地中斷,包括定時器中斷。
  3. 0x0C000000 - PLIC (Platform-Level Interrupt Controller),負責處理外部中斷。
  4. 0x10000000 - uart0,串口控制器。
  5. 0x10001000 - virtio磁盤接口。
  6. 0x80000000 - 啟動ROM會在機器模式下跳轉到這里,內核也會加載到這里。
  7. 0x80000000 以后的內存區域為內核和用戶空間的使用。

內核物理內存使用情況

  • 0x80000000 - 內核的入口點,包含entry.S,以及內核的代碼和數據。
  • end - 內核頁分配區域的開始。
  • PHYSTOP - 內核使用的內存結束位置。

硬件設備的地址定義

以下宏定義了各個硬件設備在物理內存中的地址和中斷號:

UART0UART0_IRQ

#define UART0 0x10000000L
#define UART0_IRQ 10

Virtio磁盤接口

#define VIRTIO0 0x10001000
#define VIRTIO0_IRQ 1

CLINT 和相關寄存器地址:

#define CLINT 0x2000000L
#define CLINT_MTIMECMP(hartid) (CLINT + 0x4000 + 8*(hartid))
#define CLINT_MTIME (CLINT + 0xBFF8) // 啟動以來的時鐘周期數

PLIC 和相關寄存器地址:

#define PLIC 0x0c000000L
#define PLIC_PRIORITY (PLIC + 0x0)
#define PLIC_PENDING (PLIC + 0x1000)
#define PLIC_MENABLE(hart) (PLIC + 0x2000 + (hart)*0x100)
#define PLIC_SENABLE(hart) (PLIC + 0x2080 + (hart)*0x100)
#define PLIC_MPRIORITY(hart) (PLIC + 0x200000 + (hart)*0x2000)
#define PLIC_SPRIORITY(hart) (PLIC + 0x201000 + (hart)*0x2000)
#define PLIC_MCLAIM(hart) (PLIC + 0x200004 + (hart)*0x2000)
#define PLIC_SCLAIM(hart) (PLIC + 0x201004 + (hart)*0x2000)

內核內存布局

  • KERNBASEPHYSTOP

    #define KERNBASE 0x80000000L
    #define PHYSTOP (KERNBASE + 128*1024*1024) // 內核使用的內存大小為128MB
    

    TRAMPOLINE

    #define TRAMPOLINE (MAXVA - PGSIZE)
    

    內核棧的地址計算

    #define KSTACK(p) (TRAMPOLINE - ((p)+1)* 2*PGSIZE)
    

    用戶內存布局

    用戶地址空間從零地址開始,包含以下部分:

  • 文本段
  • 原始數據段和BSS段
  • 固定大小的棧
  • 可擴展的堆
  • TRAPFRAME
#define TRAPFRAME (TRAMPOLINE - PGSIZE)

TRAMPOLINE:與內核中的相同頁面。

2.?kernel/vm.c

這段代碼實現了一個基于RISC-V架構的內核頁表管理模塊,主要用于管理虛擬內存與物理內存之間的映射。下面是對這段代碼中各個函數和宏定義的詳細解釋:

#include "param.h"
#include "types.h"
#include "memlayout.h"
#include "elf.h"
#include "riscv.h"
#include "defs.h"
#include "fs.h"/**該函數創建一個直接映射的內核頁表,并將硬件設備、內核代碼和數據段、以及跳板頁(trampoline)映射 *到內核頁表中。*/
pagetable_t kernel_pagetable;extern char etext[];  // kernel.ld sets this to end of kernel code.extern char trampoline[]; // trampoline.S//該函數創建一個直接映射的內核頁表,并將硬件設備、
//內核代碼和數據段、以及跳板頁(trampoline)映射到內核頁表中。
void
kvminit()
{kernel_pagetable = (pagetable_t) kalloc();memset(kernel_pagetable, 0, PGSIZE);// uart registerskvmmap(UART0, UART0, PGSIZE, PTE_R | PTE_W);// virtio mmio disk interfacekvmmap(VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W);// CLINTkvmmap(CLINT, CLINT, 0x10000, PTE_R | PTE_W);// PLICkvmmap(PLIC, PLIC, 0x400000, PTE_R | PTE_W);// map kernel text executable and read-only.kvmmap(KERNBASE, KERNBASE, (uint64)etext-KERNBASE, PTE_R | PTE_X);// map kernel data and the physical RAM we'll make use of.kvmmap((uint64)etext, (uint64)etext, PHYSTOP-(uint64)etext, PTE_R | PTE_W);// map the trampoline for trap entry/exit to// the highest virtual address in the kernel.kvmmap(TRAMPOLINE, (uint64)trampoline, PGSIZE, PTE_R | PTE_X);
}//該函數切換硬件頁表寄存器到內核頁表,并啟用分頁。
void
kvminithart()
{w_satp(MAKE_SATP(kernel_pagetable));sfence_vma();
}//該函數在頁表中查找虛擬地址va對應的頁表項(PTE),如果alloc非零,則在需要時分配頁表頁
pte_t *
walk(pagetable_t pagetable, uint64 va, int alloc)
{if(va >= MAXVA)panic("walk");for(int level = 2; level > 0; level--) {pte_t *pte = &pagetable[PX(level, va)];if(*pte & PTE_V) {pagetable = (pagetable_t)PTE2PA(*pte);} else {if(!alloc || (pagetable = (pde_t*)kalloc()) == 0)return 0;memset(pagetable, 0, PGSIZE);*pte = PA2PTE(pagetable) | PTE_V;}}return &pagetable[PX(0, va)];
}//該函數查找虛擬地址va對應的物理地址,如果未映射則返回0。只能用于查找用戶頁。
uint64
walkaddr(pagetable_t pagetable, uint64 va)
{pte_t *pte;uint64 pa;if(va >= MAXVA)return 0;pte = walk(pagetable, va, 0);if(pte == 0)return 0;if((*pte & PTE_V) == 0)return 0;if((*pte & PTE_U) == 0)return 0;pa = PTE2PA(*pte);return pa;
}//該函數在內核頁表中添加一個映射。在啟動時使用,不刷新TLB或啟用分頁。
void
kvmmap(uint64 va, uint64 pa, uint64 sz, int perm)
{if(mappages(kernel_pagetable, va, sz, pa, perm) != 0)panic("kvmmap");
}//該函數將內核虛擬地址轉換為物理地址。假設va是頁對齊的。
uint64
kvmpa(uint64 va)
{uint64 off = va % PGSIZE;pte_t *pte;uint64 pa;pte = walk(kernel_pagetable, va, 0);if(pte == 0)panic("kvmpa");if((*pte & PTE_V) == 0)panic("kvmpa");pa = PTE2PA(*pte);return pa+off;
}//這段代碼實現了mappages函數,用于創建頁表條目(PTE),將虛擬地址映射到物理地址。
//函數接受頁表指針、虛擬地址、映射大小、物理地址和權限作為參數,并返回成功或失敗的狀態。
int
mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm)
{uint64 a, last;pte_t *pte;a = PGROUNDDOWN(va);                      // 向下對齊虛擬地址到頁邊界last = PGROUNDDOWN(va + size - 1);        // 向下對齊最后一個虛擬地址到頁邊界for(;;){if((pte = walk(pagetable, a, 1)) == 0)  // 獲取或創建對應虛擬地址的PTEreturn -1;if(*pte & PTE_V)                        // 檢查PTE是否有效,防止重復映射panic("remap");*pte = PA2PTE(pa) | perm | PTE_V;       // 設置PTE,映射到物理地址并賦予權限if(a == last)                           // 如果已經處理完最后一個頁break;a += PGSIZE;                            // 前進到下一個頁pa += PGSIZE;                           // 更新物理地址}return 0;
}//這段代碼實現了uvmunmap函數,用于取消虛擬地址到物理地址的映射。
//函數接受頁表指針、虛擬地址、要取消映射的頁數和一個標志位作為參數,
//標志位決定是否釋放物理內存。
void
uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free)
{uint64 a;pte_t *pte;if((va % PGSIZE) != 0)  // 檢查虛擬地址是否對齊到頁邊界panic("uvmunmap: not aligned");for(a = va; a < va + npages * PGSIZE; a += PGSIZE){  // 遍歷每一個頁if((pte = walk(pagetable, a, 0)) == 0)  // 獲取對應虛擬地址的PTEpanic("uvmunmap: walk");if((*pte & PTE_V) == 0)  // 檢查PTE是否有效panic("uvmunmap: not mapped");if(PTE_FLAGS(*pte) == PTE_V)  // 檢查PTE是否為葉子節點panic("uvmunmap: not a leaf");if(do_free){  // 如果需要釋放物理內存uint64 pa = PTE2PA(*pte);kfree((void*)pa);  // 釋放物理內存}*pte = 0;  // 取消映射}
}//用于創建一個空的用戶頁表。函數通過分配一頁物理內存來存儲頁表,
//并初始化該頁表。如果內存分配失敗,函數返回0
pagetable_t
uvmcreate()
{pagetable_t pagetable;// 分配一頁物理內存用于存儲頁表pagetable = (pagetable_t) kalloc();if(pagetable == 0)return 0;memset(pagetable, 0, PGSIZE);return pagetable;
}//用于將用戶初始化代碼加載到頁表的地址0處。此函數通常在創建第一個
//用戶進程時使用。代碼執行了內存分配、內存映射和數據拷貝的操作
void
uvminit(pagetable_t pagetable, uchar *src, uint sz)
{char *mem;// 檢查大小是否超過一頁if(sz >= PGSIZE)panic("inituvm: more than a page");// 分配一頁物理內存并清零mem = kalloc();memset(mem, 0, PGSIZE);// 將分配的物理內存映射到虛擬地址0mappages(pagetable, 0, PGSIZE, (uint64)mem, PTE_W|PTE_R|PTE_X|PTE_U);// 將初始化代碼拷貝到分配的物理內存memmove(mem, src, sz);
}//用于為進程分配頁表條目和物理內存,以將進程的內存從oldsz
//增長到newsz。如果分配成功,函數返回新大小;如果出錯,則返回0
uint64
uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz)
{char *mem;uint64 a;// 如果newsz小于oldsz,不進行任何操作,返回oldszif(newsz < oldsz)return oldsz;// 將oldsz向上取整到頁邊界oldsz = PGROUNDUP(oldsz);// 從oldsz增長到newsz,按頁分配內存for(a = oldsz; a < newsz; a += PGSIZE){// 分配一頁物理內存mem = kalloc();if(mem == 0){// 分配失敗,釋放之前分配的內存uvmdealloc(pagetable, a, oldsz);return 0;}// 清零已分配的內存memset(mem, 0, PGSIZE);// 將物理內存映射到虛擬地址if(mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0){// 映射失敗,釋放已分配的內存kfree(mem);uvmdealloc(pagetable, a, oldsz);return 0;}}// 分配成功,返回newszreturn newsz;
}//用于釋放進程的用戶頁,使其內存大小從oldsz減少到newsz。無論oldsz是否
//大于實際進程大小,或者newsz是否小于oldsz,函數都會按需要進行內存釋放,
//并返回新的進程大小。
uint64
uvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz)
{// 如果newsz大于等于oldsz,不需要做任何操作,返回oldszif(newsz >= oldsz)return oldsz;// 如果newsz向上取整后的頁數小于oldsz向上取整后的頁數,說明需要釋放一些頁if(PGROUNDUP(newsz) < PGROUNDUP(oldsz)){int npages = (PGROUNDUP(oldsz) - PGROUNDUP(newsz)) / PGSIZE;uvmunmap(pagetable, PGROUNDUP(newsz), npages, 1);}// 返回新的進程大小newszreturn newsz;
}//用于遞歸地釋放頁表頁。該函數假定所有葉子映射(即實際映射到物理內存的頁)
//已經被移除,因此它只需要處理非葉子頁表條目。
void
freewalk(pagetable_t pagetable)
{// 頁表中有2^9 = 512個頁表條目for(int i = 0; i < 512; i++){pte_t pte = pagetable[i];// 如果當前條目有效且不是葉子條目if((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0){// 該PTE指向一個更低級別的頁表uint64 child = PTE2PA(pte);// 遞歸釋放更低級別的頁表freewalk((pagetable_t)child);// 將當前條目清零pagetable[i] = 0;} else if(pte & PTE_V){// 如果當前條目是葉子條目,拋出一個錯誤panic("freewalk: leaf");}}// 釋放當前頁表kfree((void*)pagetable);
}//用于釋放用戶內存頁,然后釋放頁表頁
void
uvmfree(pagetable_t pagetable, uint64 sz)
{if(sz > 0)uvmunmap(pagetable, 0, PGROUNDUP(sz)/PGSIZE, 1);freewalk(pagetable);
}//將父進程的內存復制到子進程的頁表中,包括復制頁表項和物理內存。
//它在成功時返回0,在失敗時返回-1,并在失敗時釋放已經分配的所有資源以避免內存泄漏
int
uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
{pte_t *pte;uint64 pa, i;uint flags;char *mem;for(i = 0; i < sz; i += PGSIZE){if((pte = walk(old, i, 0)) == 0)panic("uvmcopy: pte should exist");if((*pte & PTE_V) == 0)panic("uvmcopy: page not present");pa = PTE2PA(*pte);flags = PTE_FLAGS(*pte);if((mem = kalloc()) == 0)goto err;memmove(mem, (char*)pa, PGSIZE);if(mappages(new, i, PGSIZE, (uint64)mem, flags) != 0){kfree(mem);goto err;}}return 0;err:uvmunmap(new, 0, i / PGSIZE, 1);return -1;
}// 將一個頁表項標記為用戶不可訪問。
// 在執行程序加載時用于用戶棧的保護頁。
void
uvmclear(pagetable_t pagetable, uint64 va)
{pte_t *pte;// 查找給定頁表(pagetable)中虛擬地址 'va' 對應的頁表項(PTE)。pte = walk(pagetable, va, 0);// 如果找不到頁表項(pte為NULL),則發生panic,表示出現了錯誤。if(pte == 0)panic("uvmclear");// 清除頁表項中的用戶訪問位(PTE_U)。// 這樣標記該頁為用戶不可訪問。*pte &= ~PTE_U;
}// 從內核空間復制到用戶空間。
// 將長度為len的數據從src復制到給定頁表中虛擬地址dstva處。
// 成功時返回0,出錯時返回-1。
int
copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
{uint64 n, va0, pa0;while(len > 0){// 對目標虛擬地址進行頁面對齊。va0 = PGROUNDDOWN(dstva);// 獲取va0對應的物理地址。pa0 = walkaddr(pagetable, va0);// 如果物理地址為0,則返回-1,表示出錯。if(pa0 == 0)return -1;// 計算當前頁內剩余空間長度。n = PGSIZE - (dstva - va0);// 如果剩余長度大于要復制的數據長度,取要復制的數據長度。if(n > len)n = len;// 將數據從src復制到物理地址pa0 + (dstva - va0)處,長度為n。memmove((void *)(pa0 + (dstva - va0)), src, n);// 更新剩余數據長度、源地址和目標虛擬地址。len -= n;src += n;dstva = va0 + PGSIZE;}return 0;
}// 從用戶空間復制到內核空間。
// 將長度為len的數據從給定頁表中虛擬地址srcva處復制到目標地址dst。
// 成功時返回0,出錯時返回-1。
int
copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
{uint64 n, va0, pa0;while(len > 0){// 對源虛擬地址進行頁面對齊。va0 = PGROUNDDOWN(srcva);// 獲取va0對應的物理地址。pa0 = walkaddr(pagetable, va0);// 如果物理地址為0,則返回-1,表示出錯。if(pa0 == 0)return -1;// 計算當前頁內剩余空間長度。n = PGSIZE - (srcva - va0);// 如果剩余長度大于要復制的數據長度,取要復制的數據長度。if(n > len)n = len;// 將數據從物理地址pa0 + (srcva - va0)處復制到目標地址dst,長度為n。memmove(dst, (void *)(pa0 + (srcva - va0)), n);// 更新剩余數據長度、目標地址和源虛擬地址。len -= n;dst += n;srcva = va0 + PGSIZE;}return 0;
}// 從用戶空間復制空結尾字符串到內核空間。
// 從給定頁表中虛擬地址srcva處復制最多max字節的數據到目標地址dst,
// 直到遇到'\0'結束,或者達到max字節。
// 成功時返回0,出錯時返回-1。
int
copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
{uint64 n, va0, pa0;int got_null = 0; // 標記是否遇到了'\0'while(got_null == 0 && max > 0){// 對源虛擬地址進行頁面對齊。va0 = PGROUNDDOWN(srcva);// 獲取va0對應的物理地址。pa0 = walkaddr(pagetable, va0);if(pa0 == 0)return -1;// 計算當前頁內剩余空間長度。n = PGSIZE - (srcva - va0);if(n > max)n = max;// 將物理地址轉換為char指針,從中復制數據直到遇到'\0'或者達到max長度。char *p = (char *) (pa0 + (srcva - va0));while(n > 0){if(*p == '\0'){ // 如果遇到了'\0',復制結束。*dst = '\0';got_null = 1;break;} else { // 否則繼續復制字符。*dst = *p;}--n;--max;p++;dst++;}srcva = va0 + PGSIZE; // 更新源虛擬地址為下一頁的起始地址。}if(got_null){return 0; // 復制成功,返回0。} else {return -1; // 復制失敗(未遇到'\0'但已達到max長度),返回-1。}
}

3.kernel/kalloc.c?

這段代碼實現了一個物理內存分配器,用于用戶進程、內核棧、頁表頁以及管道緩沖區。它主要負責分配和釋放4096字節的頁面(頁)。

// Physical memory allocator, for user processes,
// kernel stacks, page-table pages,
// and pipe buffers. Allocates whole 4096-byte pages.#include "types.h"
#include "param.h"
#include "memlayout.h"
#include "spinlock.h"
#include "riscv.h"
#include "defs.h"void freerange(void *pa_start, void *pa_end);extern char end[]; // first address after kernel.// defined by kernel.ld.//run結構體定義了一個單向鏈表節點,用于維護空閑物理內存頁的鏈表。
struct run {struct run *next;
};//kmem結構體包含一個自旋鎖和一個空閑內存頁鏈表的頭指針,
//用于實現線程安全的內存管理。
struct {struct spinlock lock;struct run *freelist;
} kmem;//該函數初始化物理內存分配器。它首先初始化自旋鎖,然后調用freerange函數,
//將從內核結束地址(end)到物理內存頂部(PHYSTOP)之間的內存頁加入空閑列表。
void
kinit()
{initlock(&kmem.lock, "kmem");freerange(end, (void*)PHYSTOP);
}//該函數將從pa_start到pa_end范圍內的內存頁加入空閑列表。
//它首先將pa_start地址向上對齊到頁邊界,然后逐頁調用kfree函數釋放這些內存頁。
void
freerange(void *pa_start, void *pa_end)
{char *p;p = (char*)PGROUNDUP((uint64)pa_start);for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE)kfree(p);
}//該函數釋放一個物理內存頁,將其加入空閑列表。它首先檢查pa是否是頁對齊的,
//并且在合法范圍內。然后用垃圾數據填充該頁,防止懸空引用。最后將該頁加入空閑列表,
//使用自旋鎖確保線程安全。
void
kfree(void *pa)
{struct run *r;if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)panic("kfree");// Fill with junk to catch dangling refs.memset(pa, 1, PGSIZE);r = (struct run*)pa;acquire(&kmem.lock);r->next = kmem.freelist;kmem.freelist = r;release(&kmem.lock);
}//該函數分配一個物理內存頁。它從空閑列表中取出一個頁,
//如果成功分配,則用垃圾數據填充該頁。返回頁的地址,如果分配失敗則返回0。
void *
kalloc(void)
{struct run *r;acquire(&kmem.lock);r = kmem.freelist;if(r)kmem.freelist = r->next;release(&kmem.lock);if(r)memset((char*)r, 5, PGSIZE); // fill with junkreturn (void*)r;
}

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

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

相關文章

代謝組數據分析(十二):嶺回歸、Lasso回歸、彈性網絡回歸構建預測模型

歡迎大家關注全網生信學習者系列: WX公zhong號:生信學習者Xiao hong書:生信學習者知hu:生信學習者CDSN:生信學習者2介紹 在代謝物預測模型的構建中,我們采用了三種主流的回歸分析方法:嶺回歸、Lasso回歸以及彈性網絡回歸。這三種方法各有其獨特的原理和適用場景,因此在…

WPS操作技巧:制作可以打對勾的方框,只需簡單幾步!沈陽wps辦公軟件培訓

日常工作中&#xff0c;我們經常需要在表格中添加復選框&#xff0c;比如【性別選擇】、【任務完成狀態】等等&#xff0c;通過打對勾來確定狀態。今天就分別從WPS的Excel表格和Word文檔2種場景&#xff0c;介紹制作可以打對勾的復選框的方法技巧&#xff0c;掌握技巧&#xff…

25、PHP 實現兩個鏈表的第一個公共結點(含源碼)

題目&#xff1a; PHP 實現兩個鏈表的第一個公共結點 描述&#xff1a; 輸入兩個鏈表&#xff0c;找出它們的第一個公共結點。 <?php /*class ListNode{var $val;var $next NULL;function __construct($x){$this->val $x;} }*/ function FindFirstCommonNode($pHead…

構建zdppy docker鏡像

拉取鏡像 docker pull python:3.8-alpine3.19創建容器 docker run -itd --name zdppy python:3.8-alpine3.19 sh進入容器 docker exec -it zdppy sh配置pip國內源 pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple提交容器為鏡像 docker commit…

游戲AI的創造思路-技術基礎-計算機視覺

讓游戲的AI具備“眼睛”和“視覺”&#xff0c;就是通過計算機視覺的方法進行的。現在&#xff0c;越來越多的游戲&#xff0c;特別是動捕類游戲都在使用這個方法。當然&#xff0c;計算機視覺不僅僅用于游戲&#xff0c;越來越多的應用使用到這個技術 目錄 1. 定義 2. 發展歷…

spring 枚舉、策略模式、InitializingBean初使化組合使用示例

實現一個簡單的文本處理系統。 在這個系統中&#xff0c;我們將定義不同類型的文本處理策略&#xff0c;比如大小寫轉換、添加前綴后綴等&#xff0c;并使用工廠模式來管理這些策略。 1 定義一個枚舉來標識不同的文本處理類型 public enum TextProcessTypeEnum {UPPER_CASE,LO…

騰訊混元文生圖開源模型推出小顯存版本,6G顯存即可運行,并開源caption模型

7月4日&#xff0c;騰訊混元文生圖大模型&#xff08;混元DiT&#xff09;宣布開源小顯存版本&#xff0c;僅需6G顯存即可運行&#xff0c;對使用個人電腦本地部署的開發者十分友好&#xff0c;該版本與LoRA、ControlNet等插件&#xff0c;都已適配至Diffusers庫&#xff1b;并…

探索 Apache Paimon 在阿里智能引擎的應用場景

摘要&#xff1a;本文整理自Apache Yarn && Flink Contributor&#xff0c;阿里巴巴智能引擎事業部技術專家王偉駿&#xff08;鴻歷&#xff09;老師在 5月16日 Streaming Lakehouse Meetup Online 上的分享。內容主要分為以下三個部分&#xff1a; 一、 阿里智能引擎…

【LeetCode】全排列

目錄 一、題目二、解法完整代碼 一、題目 給定一個不含重復數字的數組 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意順序 返回答案。 示例 1&#xff1a; 輸入&#xff1a;nums [1,2,3] 輸出&#xff1a;[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] …

LVS+Nginx高可用集群--基礎篇

1.集群概述 單體部署&#xff1a; 可以將上面內容分別部署在不同的服務器上。 單體架構的優點&#xff1a; 小團隊成型就可完成開發&#xff0c;測試&#xff0c;上線 迭代周期短&#xff0c;速度快 打包方便&#xff0c;運維簡單 單體架構的挑戰&#xff1a;單節點宕機造成…

DVWA sql手注學習(巨詳細不含sqlmap)

這篇文章主要記錄學習sql注入的過程中遇到的問題已經一點學習感悟&#xff0c;過程圖片會比較多&#xff0c;比較基礎和詳細&#xff0c;不存在看不懂哪一步的過程 文章目錄 靶場介紹SQL注入 lowSQL注入 MediumSQL注入 HighSQL注入 Impossible 靶場介紹 DVWA&#xff08;Damn…

必備的 Adobe XD 輔助工具

想要高效便捷的使用 Adobe XD&#xff0c; Adobe XD 插件是必不可少的&#xff0c; Adobe XD 的插件非常多&#xff0c;但 90%都是英文&#xff0c;并且良莠不齊。在這兒挑選 9 個好用的 Adobe XD 插件給大家&#xff0c;這里是我整理的一些實用 Adobe XD 插件&#xff0c;讓你…

大屏開發系列——Echarts的基礎使用

本文為個人近期學習總結&#xff0c;若有錯誤之處&#xff0c;歡迎指出&#xff01; Echarts在vue2中的基礎使用 一、簡單介紹二、基本使用&#xff08;vue2中&#xff09;1.npm安裝2.main.js引入3.使用步驟(1)準備帶有寬高的DOM容器&#xff1b;(2)初始化echarts實例&#xff…

gcc: warning: -Wunused-function;加了選項,為什么就不報警告呢?

文章目錄 問題clang的編譯而使用gcc是就不報問題分析原因如果是非static的函數問題 下面這個代碼段,其中這個函數hton_ext_2byte,在整個程序里就沒有使用。 static inline uint16_t hton_ext_2byte(uint8_t **p) {uint16_t v;******return v;

PHP宜邦家政服務管理系統-計算機畢業設計源碼04426

目 錄 摘要 1 緒論 1.1 選題背景與意義 1.2開發現狀 1.3論文結構與章節安排 2 宜邦家政服務管理系統系統分析 2.1 可行性分析 2.1.1 技術可行性分析 2.1.2 經濟可行性分析 2.1.3 操作可行性分析 2.2 系統功能分析 2.2.1 功能性分析 2.2.2 非功能性分析 2.3 系統用…

國標GB28181視頻匯聚平臺LntonCVS視頻監控安防平臺與國標協議對接解決方案

應急管理部門以“以信息化推動應急管理能力現代化”為總體目標&#xff0c;加快現代信息技術與應急管理業務深度融合&#xff0c;全面支持現代應急管理體系建設&#xff0c;這不僅是國家加強和改進應急管理工作的關鍵舉措&#xff0c;也是應對日益嚴峻的應急管理形勢和滿足公眾…

微信小程序的運行機制與更新機制

1. 小程序運行機制 1.1. 冷啟動與熱啟動 冷啟動為用戶第一次打開小程序時&#xff0c;因為之前沒有打開過&#xff0c;這是第一種冷啟動的情兌。第二種情況為雖然之前用戶打開過&#xff0c;但是小程序被用戶主動的銷毀過&#xff0c;這種情況下我們再次打開小程序&#xff0…

【PALM、WRF-LES】微尺度氣象數值模擬—大渦模擬技術

針對微尺度氣象的復雜性&#xff0c;大渦模擬&#xff08;LES&#xff09;提供了一種無可比擬的解決方案。微尺度氣象學涉及對小范圍內的大氣過程進行精確模擬&#xff0c;這些過程往往與天氣模式、地形影響和人為因素如城市布局緊密相關。在這種規模上&#xff0c;傳統的氣象模…

doc文檔下載

目錄 下載 安裝谷歌瀏覽器(chrome)Microsoft Edge瀏覽器 常見問題 下載 見郵件附件 安裝 谷歌瀏覽器(chrome) 打開瀏覽器&#xff0c;地址欄輸入&#xff1a;chrome://extensions/ 右上角打開開發者模式 點擊如上圖左上角的加載已解壓的拓展程序&#xff0c;并選擇剛剛解壓…

安卓應用開發學習:通過騰訊地圖SDK實現定位功能

一、引言 這幾天有些忙&#xff0c;耽誤了寫日志&#xff0c;但我的學習始終沒有落下&#xff0c;有空我就會研究《 Android App 開發進階與項目實戰》一書中定位導航方面的內容。在我的手機上先后實現了“獲取經緯度及地理位置描述信息”和“獲取導航衛星信息”功能后&#x…