三、memblock 內存分配器

兩個問題:

1、系統是怎么知道物理內存的?linux內存管理學習(1):物理內存探測

2、在內存管理真正初始化之前,內核的代碼執行需要分配內存該怎么處理?

在Linux內核啟動初期,完整的內存管理系統如Buddy System和Slab分配器尚未初始化完成。

此時,內核通過memblock機制臨時管理物理內存空間。memblock作為早期內存管理器,負責記錄所有可用的DRAM區域,并處理啟動階段的內存分配與保留請求,例如為內核代碼、設備樹或初始化數據分配內存。

當內核繼續初始化并建立起Buddy System和Slab分配器等核心內存管理組件后,會在mem_init()?函數中完成內存管理權的移交。

此時,memblock分配器會將剩余的內存釋放給Buddy System統一管理,后續所有動態內存分配均由Buddy System和Slab分配器等高級機制接管,而memblock僅保留調試或特殊場景下的輔助功能。

分析memblock算法,可以從幾點入手:

1、 memblock算法初始化;

2、 memblock算法管理內存的申請和釋放;

在分析 memblock 之前,我們需要先理清系統內存的總體使用情況。內存按照用途和生命周期可以分為三類:

(1)靜態內存:永久分配給內核的固定內存區域,不可被動態分配或回收。包含內容如:

  • 內核代碼段(_text? ~ _etext?):存放內核的可執行代碼。
  • 內核數據段(_data? ~ _edata?):存放全局變量、靜態變量等。
  • BSS段(__bss_start? ~ __bss_stop?):存放未初始化的靜態變量。
  • 設備樹(FDT, Flattened Device Tree):描述硬件信息,由 Bootloader 傳遞。
  • initramfs/initrd:臨時根文件系統,用于早期用戶空間初始化。

這些區域在系統啟動時就被占用,不會被釋放或重新分配。必須通過 memblock_reserve() 進行保護,防止被錯誤分配。

(2)預留內存:系統預先保留的專用內存,通常用于硬件加速或特殊設備。

  • GPU/Camera/VPU:多媒體處理需要大塊連續物理內存(如 64MB~1GB)
  • DMA 緩沖區:某些外設要求物理連續內存(如 DMA Engine)
  • 安全相關區域:如 TEE(Trusted Execution Environment)占用的內存

這些區域不參與常規內存分配,但可能由驅動按需啟用或釋放。通過 memblock_reserve()? 或設備樹 reserved-memory? 節點預留

(3)動態內存:內核可自由分配和管理的物理內存,是系統最寶貴的資源

  • 啟動初期:由 memblock? 進行簡單分配
  • Buddy System 就緒后:由頁分配器(alloc_pages()?)管理,支持按頁分配
  • SLAB/SLUB 就緒后:提供小塊內存分配(kmalloc()?)

可被用戶空間(通過 mmap)或內核(vmalloc、kmalloc)動態使用,可能因內存碎片或外設占用而面臨大塊連續內存不足的問題

其中memblock_reserved_init_regions主要包括上述靜態內存和預留內存空間


一、MEMBLOCK 內存分配器進行初始化

初始化入口:

start_kernel--> setup_arch--> e820__memblock_setup

內存memblock基本初始化包括兩個部分:

  • 根據硬件實際的物理內存初始化內核內存可見區域
  • 標記內核已經使用的內存 (設備樹、內核鏡像等)
static struct memblock_region memblock_memory_init_regions[INIT_MEMBLOCK_REGIONS] __initdata_memblock;
static struct memblock_region memblock_reserved_init_regions[INIT_MEMBLOCK_RESERVED_REGIONS] __initdata_memblock;
#ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAP
static struct memblock_region memblock_physmem_init_regions[INIT_PHYSMEM_REGIONS];
#endif/** memblock 內存分配器的全局實例初始化* 使用 __initdata_memblock 標記表示該數據結構僅在內核初始化階段使用* 初始化完成后這部分內存可以被釋放*/
struct memblock memblock __initdata_memblock = {/* 可用內存區域(memory)初始化 */.memory.regions = memblock_memory_init_regions,  /* 初始靜態分配的內存區域數組 */.memory.cnt = 1,        /* 初始設為1表示有一個空條目(dummy entry) */.memory.max = INIT_MEMBLOCK_REGIONS,  /* 初始最大區域數,通常為128 */.memory.name = "memory", /* 用于調試識別的名稱 *//* 保留內存區域(reserved)初始化 */ .reserved.regions = memblock_reserved_init_regions, /* 初始靜態分配的保留區域數組 */.reserved.cnt = 1,      /* 初始設為1表示有一個空條目 */.reserved.max = INIT_MEMBLOCK_RESERVED_REGIONS, /* 初始最大保留區域數,通常為128 */.reserved.name = "reserved", /* 用于調試識別的名稱 *//* 全局控制參數初始化 */.bottom_up = false,      /* 默認從高地址向低地址分配策略 */.current_limit = MEMBLOCK_ALLOC_ANYWHERE, /* 初始無分配地址限制 */
};

memblock把物理內存劃分為若干內存區,按使用類型分別放在memory和reserved兩個集合(數組)中,memory即動態內存的集合,reserved集合包括靜態內存和預留內存

?

?

每個數組包含了 128 個內存區域。我們可以在 INIT_MEMBLOCK_REGIONS 宏定義中看到它:

#define INIT_MEMBLOCK_REGIONS			128
#define INIT_PHYSMEM_REGIONS			4#ifndef INIT_MEMBLOCK_RESERVED_REGIONS
# define INIT_MEMBLOCK_RESERVED_REGIONS		INIT_MEMBLOCK_REGIONS
#endif

內核和memblock相關的數據結構:

// 1、struct memblock 結構體描述分配器整體特性
struct memblock {/* 內存分配方向控制 */bool bottom_up;  /* true: 從低地址向高地址分配內存;false: 從高地址向低地址分配(默認) *//* 內存分配的安全邊界 */phys_addr_t current_limit; /* memblock_alloc()能分配的最高物理地址,用于防止分配到未映射或不安全的區域 */struct memblock_type memory;   /* 所有可用RAM區域的連續列表,從固件獲取(如e820/EFI內存映射) */struct memblock_type reserved; /* 所有保留/已分配的區域,包括:* - 內核靜態區域(代碼段、數據段、BSS段)* - 設備保留內存(GPU、DMA緩沖區)* - 早期動態分配的內存 */
};// 2、struct memblock_type 結構體用于維護特定內存類型集合
struct memblock_type {unsigned long cnt;        /* 當前實際存儲的內存區域數量。例如:系統中有3塊可用的物理內存區域 */unsigned long max;        /* regions數組的當前最大容量,當cnt == max時需要動態擴容 */phys_addr_t total_size;   /* 該類型所有內存區域的總大小。例如:所有保留區域加起來共256MB */struct memblock_region *regions; /* 動態分配的內存區域數組。每個元素描述一個連續內存區域 */char *name;              /* 該內存類型的名稱字符串。例如:"memory"、"reserved"等,主要用于調試輸出 */
};// 3、struct memblock_region 結構體代表被管理的內存區塊
struct memblock_region {phys_addr_t base;    /* 內存區域的起始物理地址。例如:0x80000000(2GB處開始)*/phys_addr_t size;    /* 內存區域的長度(字節數)。例如:0x20000000(512MB大小)*/enum memblock_flags flags; /* 區域屬性標志位,可能取值:* MEMBLOCK_NONE       - 默認無特殊屬性* MEMBLOCK_HOTPLUG    - 支持熱插拔的內存* MEMBLOCK_MIRROR     - 鏡像內存區域* MEMBLOCK_NOMAP      - 不映射到內核地址空間 */#ifdef CONFIG_NUMA       /* 僅在NUMA(非統一內存訪問)系統生效 */int nid;            /* NUMA節點ID,表示該內存屬于哪個物理節點。例如:0表示第一個NUMA節點 */
#endif
};
這三個結構體: memblock, memblock_type 和 memblock_region 是 Memblock 的主要組成部分。現在我們可以進一步了解 Memblock 和 它的初始化過程了。

1、e820__memblock_setup

這段代碼是 Linux 內核中用于初始化內存塊(memblock)分配器的函數,主要作用是根據 BIOS 提供的 E820 內存映射表來設置系統的物理內存布局。

// 將BIOS提供的E820內存信息導入memblock分配器
void __init e820__memblock_setup(void)
{... .../** 允許memblock動態擴容:* - 初始靜態分配128個區域(INIT_MEMBLOCK_REGIONS)* - 當E820條目超過128時自動擴容* - 安全原因:此時已知道保留區域,擴容不會覆蓋關鍵內存*/memblock_allow_resize();/* 遍歷所有E820條目 */for (i = 0; i < e820_table->nr_entries; i++) {struct e820_entry *entry = &e820_table->entries[i];/* 檢查地址范圍是否溢出 */end = entry->addr + entry->size;if (end != (resource_size_t)end)continue;  // 跳過非法地址范圍/* 處理特殊保留內存(如調試保留區) */if (entry->type == E820_TYPE_SOFT_RESERVED) {memblock_reserve(entry->addr, entry->size);  // 加入reserved列表continue;}/* 僅處理可用內存類型 */if (entry->type != E820_TYPE_RAM &&       // 普通可用內存entry->type != E820_TYPE_RESERVED_KERN) // 內核保留可用內存continue;/* 將可用內存加入memblock.memory */memblock_add(entry->addr, entry->size);}/** 內存對齊修剪:* - 確保所有區域邊界按PAGE_SIZE(通常4K)對齊* - 避免Buddy System出現部分頁的問題*/memblock_trim_memory(PAGE_SIZE);/* 打印memblock最終狀態(調試用) */memblock_dump_all();
}
  • 遍歷E820內存映射表,將可用內存添加到memblock.memory
  • 將特殊保留內存標記到memblock.reserved
  • 確保所有內存區域按頁對齊,打印最終內存布局信息

接著看memblock_add

1.1 memblock_add
// memblock_add - 添加新的內存區域到memblock管理
int __init_memblock memblock_add(phys_addr_t base, phys_addr_t size)
{// 計算區域結束地址(包含邊界)phys_addr_t end = base + size - 1;memblock_dbg("%s: [%pa-%pa] %pS\n", __func__,&base, &end, (void *)_RET_IP_);return memblock_add_range(&memblock.memory, base, size, MAX_NUMNODES, 0);
}// memblock_add_range - 將新的內存區域添加到memblock管理系統中
static int __init_memblock memblock_add_range(...)
{... .../* 空數組特殊處理(首次添加)*/if (type->regions[0].size == 0) {type->regions[0] = (struct memblock_region){.base = base, .size = size, .flags = flags, .nid = nid};type->total_size = size;return 0;}repeat:/* 兩階段控制:重置基礎參數 */base = obase;int nr_new = 0;  // 需要新增的區域計數/* 遍歷現有區域處理重疊 */for_each_memblock_type(idx, type, rgn) {phys_addr_t rbase = rgn->base;phys_addr_t rend = rbase + rgn->size;/* 跳過無重疊區域 */if (rbase >= end) break;if (rend <= base) continue;/* 處理左側非重疊部分 */if (rbase > base) {nr_new++;if (insert) memblock_insert_region(type, idx++, base, rbase-base, nid, flags);}base = min(rend, end); // 推進處理位置}/* 處理右側剩余部分 */if (base < end) {nr_new++;if (insert)memblock_insert_region(type, idx, base, end-base, nid, flags);}/* 第一階段:數組擴容 */if (!insert) {while (type->cnt + nr_new > type->max)if (memblock_double_array(type, obase, size) < 0)return -ENOMEM;insert = true;goto repeat;  // 跳轉執行第二階段} /* 第二階段:合并區域 */else {memblock_merge_regions(type);return 0;}
}

memblock內存管理機制的核心工作流程可分為四個關鍵步驟:

  • 初始化處理:當檢測到memblock管理的內存區域為空時,直接將當前待添加的內存空間作為首個管理單元插入。
  • 重疊檢測與處理:在非空狀態下,算法會先檢查新區域與現有區域是否存在地址重疊。若發現重疊,則自動剔除重疊部分,僅將有效的非重疊內存段加入管理系統。
  • 動態擴容機制:當預設的128個region管理單元不足時,通過memblock_double_array()函數動態擴展存儲空間,確保能容納更多內存區域信息。
  • 區域合并優化:最終調用memblock_merge_regions()函數,將地址連續且屬性相同的相鄰內存區域合并為更大的連續區塊。

這套機制的核心作用是將BIOS提供的e820內存布局信息,特別是標記為"usable"的可用內存區域,精確轉換為memblock.memory中的規范化管理單元。e820探測到多少個usable內存塊,就對應多少個region,這些region嚴格按地址從低到高排列,且保證沒有重疊。

圖示,詳細見Linux Kernel:啟動時內存管理(MemBlock 分配器)一、Bootmem 與 Memblock 系統初始化 - 掘金

?

?

memblock_trim_memory:將 memblock 中所有內存區域的起始地址(start)和結束地址(end)按照指定的對齊大小(通常為 PAGE_SIZE?)進行邊界調整,確保每個區域滿足以下條件:

  • 起始地址(start):向上對齊到 PAGE_SIZE? 的倍數
  • 結束地址(end):向下對齊到 PAGE_SIZE 的倍數

memblock_dump_all:內存布局信息打印

二、memblock 內存分配與回收
2.1、memblock_alloc

使用默認參數分配內存(自動選擇位置),具體調用流程與內容如下:

--> memblock_alloc--> memblock_alloc_try_nid(--> memblock_alloc_internal(size, align, min_addr, max_addr, nid, false);static void *__init memblock_alloc_internal(phys_addr_t size, phys_addr_t align,phys_addr_t min_addr, phys_addr_t max_addr,int nid, bool exact_nid)
{... ...// 安全檢查:如果slab分配器已就緒(memblock不應再被使用)if (WARN_ON_ONCE(slab_is_available()))return kzalloc_node(size, GFP_NOWAIT, nid); // 降級到slab分配// 確保分配范圍不超過memblock的當前限制if (max_addr > memblock.current_limit)max_addr = memblock.current_limit;// 首次嘗試:在[min_addr, max_addr]范圍內分配alloc = memblock_alloc_range_nid(size, align, min_addr, max_addr, nid,exact_nid);// 若失敗,放寬限制:允許分配低于min_addr的內存if (!alloc && min_addr)alloc = memblock_alloc_range_nid(size, align, 0, max_addr, nid,exact_nid);// 轉換物理地址為虛擬地址if (!alloc)return NULL;return phys_to_virt(alloc);
}// memblock_alloc_range_nid - 在指定范圍和NUMA節點分配啟動內存塊
phys_addr_t __init memblock_alloc_range_nid(phys_addr_t size,phys_addr_t align, phys_addr_t start,phys_addr_t end, int nid,bool exact_nid)
{... ...// 4. 主要分配邏輯(可能多次嘗試)
again:// 4.1 優先嘗試在指定節點和范圍內分配found = memblock_find_in_range_node(size, align, start, end, nid,flags);if (found && !memblock_reserve(found, size))  // 成功則保留該區域goto done;// 4.2 若允許回退且指定了具體節點,嘗試任意節點分配if (nid != NUMA_NO_NODE && !exact_nid) {found = memblock_find_in_range_node(size, align, start,end, NUMA_NO_NODE,flags);if (found && !memblock_reserve(found, size))goto done;}// 4.3 處理內存鏡像情況:首次失敗后嘗試非鏡像區域if (flags & MEMBLOCK_MIRROR) {flags &= ~MEMBLOCK_MIRROR;  // 清除鏡像標志pr_warn("Could not allocate %pap bytes of mirrored memory\n",&size);goto again;  // 重新嘗試分配}// 5. 所有嘗試失敗后返回0return 0;// 6. 分配成功后的處理
done:/* 跳過kasan_init的高頻分配檢測 */if (end != MEMBLOCK_ALLOC_KASAN)/** 設置min_count=0避免kmemleak報告內存泄漏。* 因為這些內存塊通常只通過物理地址引用,kmemleak無法追蹤。*/kmemleak_alloc_phys(found, size, 0, 0);return found;  // 返回分配的內存物理地址
}// 在指定范圍和節點內查找空閑區域
static phys_addr_t __init_memblock memblock_find_in_range_node(phys_addr_t size, phys_addr_t align,phys_addr_t start, phys_addr_t end,int nid, enum memblock_flags flags)
{/* 1. 處理特殊end標志 */if (end == MEMBLOCK_ALLOC_ACCESSIBLE ||end == MEMBLOCK_ALLOC_KASAN)end = memblock.current_limit; // 使用memblock的當前地址限制/* 2. 避免分配第一個物理頁(0x0-0xFFF)*/start = max_t(phys_addr_t, start, PAGE_SIZE); // 至少從PAGE_SIZE開始end = max(start, end); // 確保end >= start/* 3. 根據分配策略選擇搜索方向 */if (memblock_bottom_up())// 自底向上搜索(低地址優先)return __memblock_find_range_bottom_up(start, end, size, align,nid, flags);else// 自頂向下搜索(高地址優先,默認策略)return __memblock_find_range_top_down(start, end, size, align,nid, flags);
}

主要邏輯:從可用內存區中找一塊大小為 size 的物理內存區塊, 然后調用 memblock_reseve() 函數在找到的情況下,將這塊物理內存區塊加入到預留區內

2.2、memblock_free

釋放已分配的內存區域,具體調用流程與內容如下:

/*** memblock_free - 釋放由memblock_alloc_xx()分配的啟動內存塊* 釋放先前分配的內存塊,但不會將內存返還給伙伴系統(Buddy Allocator)。* 僅從memblock.reserved中移除標記。*/
int __init_memblock memblock_free(phys_addr_t base, phys_addr_t size)
{... ...// 1. 通知kmemleak停止追蹤該物理內存范圍kmemleak_free_part_phys(base, size);// 2. 從memblock.reserved中移除該區域return memblock_remove_range(&memblock.reserved, base, size);
}/*** kmemleak_free_part_phys - 解除對物理內存范圍的泄漏追蹤* 將物理地址轉換為虛擬地址后調用標準釋放接口。*/
void __ref kmemleak_free_part_phys(phys_addr_t phys, size_t size)
{// 僅處理低端內存(若未配置HIGHMEM或地址在lowmem范圍內)if (!IS_ENABLED(CONFIG_HIGHMEM) || PHYS_PFN(phys) < max_low_pfn)kmemleak_free_part(__va(phys), size);  // __va轉換為虛擬地址
}// memblock_remove_range - 從指定memblock類型中移除內存區域
static int __init_memblock memblock_remove_range(struct memblock_type *type,phys_addr_t base, phys_addr_t size)
{	... ...// 1. 定位與目標范圍重疊的region區間ret = memblock_isolate_range(type, base, size, &start_rgn, &end_rgn);// 2. 反向遍歷并移除region(避免索引錯位)for (i = end_rgn - 1; i >= start_rgn; i--)memblock_remove_region(type, i);... ...
}// 從memblock類型中移除指定region: 該函數會更新總大小、壓縮region數組,并處理空數組的特殊情況。
static void __init_memblock memblock_remove_region(struct memblock_type *type, unsigned long r)
{// 1. 從總大小中減去被移除region的大小type->total_size -= type->regions[r].size;// 2. 移動后續region填補空缺(內存拷貝)memmove(&type->regions[r], &type->regions[r + 1],(type->cnt - (r + 1)) * sizeof(type->regions[r]));type->cnt--;  // region計數減1/* 3. 處理空數組的特殊情況 */if (type->cnt == 0) {WARN_ON(type->total_size != 0);  // 驗證一致性:總大小應為0// 重置為初始空狀態type->cnt = 1;type->regions[0].base = 0;type->regions[0].size = 0;type->regions[0].flags = 0;memblock_set_region_node(&type->regions[0], MAX_NUMNODES);}
}

memblock_isolate_range() 將要移除的物理內存區從 reserved 內存區中分離出來,將 start_rgn 和 end_rgn(該內存區塊的起始、結束索引號)返回回去

memblock_remove_region() 將這些索引對應的內存區塊從內存區中移除,這里具體做法為調用 memmove 函數將 r 索引之后的內存區塊全部往前挪一個位置,這樣 r 索引對應的內存區塊就被移除了,如果移除之后,內存區不含有任何內存區塊,那么就初始化該內存區

三、memblock釋放和移交管理權

當內核完成關鍵子系統初始化后,內存管理將進入從 memblock 到伙伴系統(Buddy System)的移交階段。這一重要過渡由 mm_init() 函數主導,其核心是通過 memblock_free_all() 實現控制權轉移,具體流程如下:

--> mm_init--> mem_init--> memblock_free_all
四、memblock內存分配API概覽
4.1、初始化與設置
  • memblock_allow_resize()?: 允許動態調整memblock數組大小
  • memblock_set_bottom_up()?: 設置內存分配方向(自底向上或自頂向下)
  • memblock_set_current_limit()?: 設置當前內存分配限制地址
4.2、內存區域管理
  • memblock_add(base, size)?: 添加新的可用內存區域(參數:基址,大小)
  • ?memblock_remove(base, size)?: 移除指定的內存區域
  • memblock_reserve(base, size)?: 保留(預留)指定的內存區域
  • memblock_free(base, size)?: 釋放已分配的內存區域
  • memblock_mark_hotplug()?/clear_hotplug()?: 設置/清除內存區域的熱插拔屬性
  • memblock_mark_nomap()?/clear_nomap()?: 設置/清除內存區域的"不映射到內核"屬性
4.3、內存分配函數
  • memblock_phys_alloc(size, align)?: 分配指定大小和對齊的物理內存
  • ?memblock_alloc(size, align)?: 使用默認參數分配內存(自動選擇位置)
  • memblock_alloc_node(size, align, nid)?: 在指定NUMA節點上分配內存
  • memblock_alloc_from(size, align, min_addr)?: 從指定最小地址開始分配
  • memblock_alloc_low(size, align)?: 分配低端內存(通常低于4GB)
4.4、查詢函數
  • memblock_is_memory(addr)?: 檢查地址是否屬于可用內存區域
  • memblock_is_reserved(addr)?: 檢查地址是否屬于保留區域
  • ?memblock_phys_mem_size()?: 返回所有內存區域的總大小
  • ?memblock_reserved_size()?: 返回所有保留區域的總大小
  • ?memblock_start_of_DRAM()?/end_of_DRAM()?: 獲取DRAM內存的起始/結束地址

五、總結:

memblock 是 Linux 內核在初始化階段使用的臨時物理內存管理器,其核心設計圍繞以下幾點:

(1)分區管理

  • memblock.memory?:記錄所有可用物理內存(由 BIOS/e820 探測到的 usable? 區域)
  • memblock.reserved?:記錄所有已分配或保留的內存(內核代碼、設備預留等)。

(2)內存管理

  • 申請內存:僅將目標區域從 memory? 移到 reserved?,不修改原始 memory? 布局。
  • 釋放內存:極少使用(多數早期內存為永久分配),僅從 reserved? 中移除。

(3)與后續內存管理的銜接

  • memblock 記錄所有物理內存信息。
  • 內核初始化后期,Buddy System 通過 memblock_free_all()? 接管可用內存。
  • Buddy System 直接從 memblock.memory? 提取空閑區域,忽略 reserved? 中的已分配部分
  • memblock 僅保留調試接口,不再參與主動管理。


參考文檔:

(3 封私信 / 79 條消息) Linux Kernel:啟動時內存管理(MemBlock 分配器) - 知乎

linux 內核內存機制之e820(linux啟動時,利用e820讀取物理內存信息) - jinzi - 博客園

Linux內存初始化過程(ZZ) | L&H SITE

Linux內存都去哪了:(1)分析memblock在啟動過程中對內存的影響 - ArnoldLu - 博客園

(3 封私信 / 79 條消息) linux內存管理(一)內存初始化 - 知乎

【計算子系統】內存管理之一:地址映射

Linux內核內存管理(1):內存塊 - memblock-CSDN博客

early manage:memblock - Linux Book

【原創】(二)Linux物理內存初始化 - LoyenWang - 博客園

【linux 內存管理】memblock算法簡單梳理_linux memblock-CSDN博客

3.10.2.4. linux物理內存初始化(memblock) — ywg_dev_doc 0.1 文檔

Linux Kernel:啟動時內存管理(MemBlock 分配器)一、Bootmem 與 Memblock 系統初始化 - 掘金

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

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

相關文章

Python 桌面應用形態后臺管理系統的技術選型與方案報告

下面是一份面向“Python 桌面應用形態的后臺管理系統”的技術選型與方案報告。我把假設前提→總體架構→客戶端技術選型→服務端與數據層→基礎設施與安全→交付與運維→質量保障→里程碑計劃→風險與對策→最小可行棧逐層給出。 一、前置假設 & 非功能目標 業務假設 典型…

Winsows系統去除右鍵文件顯示的快捷列表

前言&#xff1a;今天重做了電腦系統&#xff0c;安裝的是純凈版的系統。然后手動指定D盤安裝了下列軟件。&#xff08;QQ&#xff0c;迅雷&#xff0c;百度網盤&#xff0c;搜狗輸入法&#xff0c;驅動精靈&#xff09;然后我右鍵點擊桌面的軟件快捷方式&#xff0c;出現了一排…

【Go】Gin 超時中間件的坑:fatal error: concurrent map writes

Gin 社區超時中間件的坑&#xff1a;導致線上 Pod 異常重啟 在最近的項目中&#xff0c;我們遇到了因為 Gin 超時中間件&#xff08;timeout&#xff09; 引發的生產事故&#xff1a;Pod 異常退出并重啟。 問題現場 pod無故重啟&#xff0c;抓取標準輸出日志&#xff0c;問題…

數據結構:用數組實現隊列(Implementing Queue Using Array)

目錄 第1步&#xff1a;設計藍圖 (The Struct) 第2步&#xff1a;隊列的誕生 (創建與初始化) 第3步&#xff1a;狀態檢查 (判滿與判空) 第4步&#xff1a;核心操作 (入隊與出隊) 入隊 (Enqueue) 出隊 (Dequeue) 第5步&#xff1a;善后工作 (銷毀隊列) 現在&#xff0c;我…

Boost庫核心組件與應用

一、BOOST 庫簡介&#xff1a;C 開發者的 “擴展工具集” 在 C 編程領域&#xff0c;除了標準庫&#xff08;STL&#xff09;外&#xff0c;BOOST 庫是最具影響力的第三方庫之一。它由全球數百位開發者共同維護&#xff0c;包含超過 160 個高質量的組件&#xff0c;覆蓋從基礎…

機器學習 [白板推導](十二)[卡曼濾波、粒子濾波]

15. 線性動態系統&#xff08;卡曼濾波&#xff0c;Kalman Filter&#xff09; 15.1. 概述 15.1.1. 背景介紹 變量隨時間變化的系統叫做動態系統&#xff0c;其中隱變量取值離散的是隱馬爾可夫模型&#xff08;HMM&#xff09;&#xff0c;而隱變量取值連續的分為線性動態系統…

RH134 訪問網絡附加存儲知識點

1. NFS 的主要功能是什么&#xff1f;答&#xff1a;NFS是一種分布式文件系統協議&#xff0c;主要功能包括&#xff1a;允許遠程計算機通過網絡訪問共享文件。 實現文件系統在客戶端和服務器之間的透明訪問。支持文件的共享、讀取和寫入&#xff0c;使得多個 …

組合模式及優化

組合模式是一種結構型設計模式&#xff0c;其核心思想是將對象組合成樹形結構&#xff0c;以表示“部分-整體”的層次關系&#xff0c;使得用戶對單個對象和組合對象的使用具有一致性。 一、介紹 核心角色 組合模式包含以下3個關鍵角色&#xff1a; 抽象組件&#xff08;Compon…

【wmi異常】關于taskkill命令提示“錯誤:找不到” 以及無法正常獲取設備機器碼的處理辦法

記錄一下我的解決方案。 我先查閱了這篇博客&#xff1a;https://blog.csdn.net/qq_45698181/article/details/138957277 發現他寫的批處理不知怎么執行不了&#xff0c;后來問了ai又可以執行了&#xff0c;估計是csdn防盜版格式問題 這里寫一下我跟ai的對話&#xff0c;大家可…

制造裝配、倉儲搬運、快遞裝卸皆適配!MinkTec 彎曲形變傳感器助力,讓人體工學改變勞動生活

【導語】Minktec 最新實驗顯示&#xff1a;將Minktec 柔性彎曲形變傳感器FlexTail 貼于受試者背部&#xff0c;記錄 1 分鐘內從洗碗機取餐具的動作&#xff0c;結合配套的flexlib -專用Python庫分析&#xff0c;不僅量化出 “越低越傷腰” 的結論&#xff0c;更為制造裝配、物流…

Nginx蜘蛛請求智能分流:精準識別爬蟲并轉發SEO渲染服務

> 一招解決搜索引擎爬蟲無法解析現代前端框架的痛點,提升網站收錄率與SEO排名! **痛點場景**:你的網站采用Vue/React等前端框架構建,頁面內容依賴JavaScript動態渲染。搜索引擎爬蟲訪問時,只能抓取到空HTML骨架,無法獲取真實內容,導致網站收錄率低、SEO效果差。 --…

鏈表。。。

目錄 5.1 鏈表的結點 5.2 插入 5.3 鏈表長度 5.4 查找 5.5 指定位置刪除 5.6 代碼 5.1 鏈表的結點 一個結點包括&#xff1a;值和指向下一個結點的指針。 package com.qcby.鏈表;public class Node {int value;Node next;public Node(int val){valueval;}Overridepublic…

私人AI搜索新突破:3步本地部署Dify+Ollama+QwQ,搜索能力MAX

1.安裝Docker容器 本地部署Dify要先安裝Docker桌面版&#xff0c;跟Ollama一樣簡單&#xff0c;也是去官網下載對應版本文件&#xff0c;直接安裝就OK。 2&#xff1a;安裝Dify 安裝 Dify 簡單的方式就是git clone&#xff0c;復制其github地址github.com/langgenius/dify&am…

(2-10-1)MyBatis的基礎與基本使用

目錄 0.前置小節 1. MyBatis 框架介紹 1.1 軟件開發中的框架 1.2 使用框架的好處 1.3 SSM 開發框架 1.4 什么是 MyBatis 1.5 MyBatis 的開發流程 2. MyBatis 的開發流程 2.0 MyBatis的工作流程 2.1 引入 MyBatis 依賴 00.base(目錄、pom、單元測試、Junit4) 01.Cal…

StarRocks集群部署

Starrocks 是一款基于 MPP 架構的高性能實時分析型數據庫&#xff0c;專為 OLAP&#xff08;聯機分析處理&#xff09;場景 設計&#xff0c;尤其擅長處理海量數據的實時分析、復雜查詢和多維統計。 硬件 CPU&#xff1a;StarRocks依靠AVX2指令集充分發揮其矢量化能力。因此&am…

【CPP】自己實現一個CPP小工具demo,可以擴展其他選項

自己寫CPP腳本小工具1. 思路描述2. 代碼實現2.1 代碼文件CppTool.cpp2.2 CMakeLists.txt3. 工具示例3.1 幫助信息3.2 工具用法3.3 實際使用1. 思路描述 實現一個簡單的命令行工具。內容包括&#xff1a; 命令幫助信息參數檢查&#xff0c;參數解析等功能。執行其他命令。將指…

如何使用嵌入模型創建本地知識庫Demo

為data目錄下的txt文檔用阿里百煉的文本嵌入模型創建一個本地知識庫import os from llama_index.core import ,Settings, SimpleDirectoryReader, VectorStoreIndex from llama_index.core.node_parser import SentenceSplitter from llama_index.llms.dashscope import DashSc…

SpringBoot 整合 Langchain4j:系統提示詞與用戶提示詞實戰詳解

> 掌握提示詞工程的核心技巧,讓你的AI應用效果提升300%! **真實痛點**:為什么同樣的模型,別人的應用精準專業,而你的卻答非所問?關鍵在于提示詞工程!本文將揭秘如何通過系統提示詞與用戶提示詞的巧妙配合,打造專業級AI應用。 --- ### 一、Langchain4j 核心概念…

Sklearn 機器學習 郵件文本分類 加載郵件數據

??親愛的技術愛好者們,熱烈歡迎來到 Kant2048 的博客!我是 Thomas Kant,很開心能在CSDN上與你們相遇~?? 本博客的精華專欄: 【自動化測試】 【測試經驗】 【人工智能】 【Python】 Sklearn 機器學習 郵件文本分類 - 加載郵件數據 在自然語言處理(NLP)中,郵件文本分…

騰訊云開發小程序工具箱使用心得

一、核心優勢與使用體驗 作為首批使用騰訊云開發&#xff08;CloudBase&#xff09;工具箱的開發者&#xff0c;我深刻感受到其通過CloudBase AI與MCP服務重構開發范式的創新價值。結合微信小程序開發場景&#xff0c;該平臺在以下維度表現突出&#xff1a; 1. AI驅動的全棧開發…