Linux 6.6内核内存管理深度解析(三·补):页表项(PTE)结构与地址翻译

〇、全景:页表项的"最后一跳"

#3 讲了多级页表(PGD/P4D/PUD/PMD/PTE)和缺页处理,但每一级的页表项(page table entry)本身长什么样 没展开------它才是"虚拟地址 → 物理地址"翻译的核心载体。本篇补上这块:一个 PTE 是一个 64 位的值,拆成"物理页帧号(PFN)+ 一堆属性位"两部分------高位存"指向哪个物理页",低位存"这个页能不能写、能不能执行、被访问过没"。
#mermaid-svg-Jpa9RWUY5ZHwr4Ja{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .error-icon{fill:#552222;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .marker.cross{stroke:#333333;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja p{margin:0;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .cluster-label text{fill:#333;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .cluster-label span{color:#333;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .cluster-label span p{background-color:transparent;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .label text,#mermaid-svg-Jpa9RWUY5ZHwr4Ja span{fill:#333;color:#333;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .node rect,#mermaid-svg-Jpa9RWUY5ZHwr4Ja .node circle,#mermaid-svg-Jpa9RWUY5ZHwr4Ja .node ellipse,#mermaid-svg-Jpa9RWUY5ZHwr4Ja .node polygon,#mermaid-svg-Jpa9RWUY5ZHwr4Ja .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .rough-node .label text,#mermaid-svg-Jpa9RWUY5ZHwr4Ja .node .label text,#mermaid-svg-Jpa9RWUY5ZHwr4Ja .image-shape .label,#mermaid-svg-Jpa9RWUY5ZHwr4Ja .icon-shape .label{text-anchor:middle;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .rough-node .label,#mermaid-svg-Jpa9RWUY5ZHwr4Ja .node .label,#mermaid-svg-Jpa9RWUY5ZHwr4Ja .image-shape .label,#mermaid-svg-Jpa9RWUY5ZHwr4Ja .icon-shape .label{text-align:center;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .node.clickable{cursor:pointer;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .arrowheadPath{fill:#333333;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .cluster text{fill:#333;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .cluster span{color:#333;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja rect.text{fill:none;stroke-width:0;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .icon-shape,#mermaid-svg-Jpa9RWUY5ZHwr4Ja .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .icon-shape p,#mermaid-svg-Jpa9RWUY5ZHwr4Ja .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .icon-shape .label rect,#mermaid-svg-Jpa9RWUY5ZHwr4Ja .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Jpa9RWUY5ZHwr4Ja :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 64 位 PTE
PFN(物理页帧号)

bits 12~51,40 位
属性位

present/RW/US/NX/accessed/dirty/...
翻译:物理地址 = PFN << 12 | offset
MMU 权限检查

present? RW? US? NX?

一句话主线:PTE 是 64 位值,高 40 位(bits 12~51)存物理页帧号 PFN,低 12 位 + 第 63 位存属性位(present/RW/US/accessed/dirty/NX 等);MMU 翻译时先逐级查页表、再按 PTE 权限位检查(可写?用户态?可执行?),最后 物理地址 = PFN << 12 | 页内偏移。其中 accessed/dirty 是 CPU 硬件自动置位的"使用痕迹"(内核靠它做 LRU 回收/回写),bit 9~11 是内核复用的软件位,大页 PTE 用 PSE 标志让 PFN 从更高的位开始。


一、PTE 位字段布局:一个 64 位值的完整分解

x86-64 的 PTE 位定义在 arch/x86/include/asm/pgtable_types.h,逐位看:

位 宏 含义 谁置位
0 _PAGE_BIT_PRESENT present,页在内存里 内核
1 _PAGE_BIT_RW writable,可写 内核
2 _PAGE_BIT_USER userspace,用户态可访问(清 0 = 仅内核) 内核
3 _PAGE_BIT_PWT write-through(写穿缓存) 内核
4 _PAGE_BIT_PCD cache disabled(禁用缓存) 内核
5 _PAGE_BIT_ACCESSED 被访问过 CPU 硬件
6 _PAGE_BIT_DIRTY 被写过 CPU 硬件
7 _PAGE_BIT_PSE(大页)/ _PAGE_BIT_PAT(4KB) 大页标志 / 页表属性位 内核
8 _PAGE_BIT_GLOBAL 全局 TLB 项(切换进程不刷) 内核
9~11 _PAGE_BIT_SOFTW1/2/3 软件位,内核复用 内核
12 _PAGE_BIT_PAT_LARGE(大页) 大页 PAT 位 内核
12~51 PFN(物理页帧号) 指向哪个物理页 内核
57~58 _PAGE_BIT_SOFTW4/5 软件位 内核
59~62 _PAGE_BIT_PKEY_BIT0~3 保护键(Protection Keys) 内核
63 _PAGE_BIT_NX No execute,不可执行 内核

源码就是这一串:

c 复制代码
// arch/x86/include/asm/pgtable_types.h (v6.6, line 10)
#define _PAGE_BIT_PRESENT	0	/* is present */
#define _PAGE_BIT_RW		1	/* writeable */
#define _PAGE_BIT_USER		2	/* userspace addressable */
#define _PAGE_BIT_PWT		3	/* page write through */
#define _PAGE_BIT_PCD		4	/* page cache disabled */
#define _PAGE_BIT_ACCESSED	5	/* was accessed (raised by CPU) */
#define _PAGE_BIT_DIRTY		6	/* was written to (raised by CPU) */
#define _PAGE_BIT_PSE		7	/* 4 MB (or 2MB) page */
#define _PAGE_BIT_GLOBAL	8	/* Global TLB entry PPro+ */
#define _PAGE_BIT_SOFTW1	9	/* available for programmer */
#define _PAGE_BIT_SOFTW2	10	/* " */
#define _PAGE_BIT_SOFTW3	11	/* " */
// ... 57~58 SOFTW4/5、59~62 PKEY、63 NX
#define _PAGE_BIT_NX		63	/* No execute: only valid after cpuid check */

关键结构:低 12 位全是"属性 + 状态",高 40 位(bits 12~51)才是真正的物理地址信息(PFN)。下面逐个拆开讲。


二、PFN:物理页帧号怎么编码进 PTE

2.1 40 位 PFN,物理地址 >> 12

物理地址 = 页帧号(PFN)× 页大小 + 页内偏移。x86-64 物理地址 52 位(__PHYSICAL_MASK_SHIFT = 52,page_64_types.h:54),页大小 4KB = 2^12,所以 PFN 占 52 - 12 = 40 位,正好是 PTE 的 bits 12~51。低 12 位被属性占用,物理地址天然 4KB 对齐(低 12 位一定是 0),所以 PTE 里不需要存低 12 位。

c 复制代码
// arch/x86/include/asm/pgtable_types.h (v6.6, line 284)
#define PTE_PFN_MASK		((pteval_t)PHYSICAL_PAGE_MASK)  // = bits 12~51
#define PTE_FLAGS_MASK		(~PTE_PFN_MASK)                  // = 低 12 位 + NX 等

提取和设置 PFN 就是对这两段做位运算:

c 复制代码
// arch/x86/include/asm/pgtable.h (v6.6, line 223 / 763)
static inline unsigned long pte_pfn(pte_t pte)
{
	phys_addr_t pfn = pte_val(pte);
	pfn ^= protnone_mask(pfn);                       // 处理 PROTNONE(NUMA)
	return (pfn & PTE_PFN_MASK) >> PAGE_SHIFT;       // 取 PFN 段,右移 12
}

static inline pte_t pfn_pte(unsigned long page_nr, pgprot_t pgprot)
{
	return __pte(((phys_addr_t)page_nr << PAGE_SHIFT) | pgprot_val(pgprot));
}

pte_pfn(提取)和 pfn_pte(构造)一进一出,PTE_PFN_MASK 就是 PFN 段、PAGE_SHIFT 就是 12。

2.2 页大小决定 PFN 从哪一位开始

这是大页的关键:PFN 的起始位随页大小变化------因为"页内偏移"的位数 = log2(页大小),页越大、偏移越宽、PFN 越短、起始位越高:

页大小 页内偏移 PFN 占位 说明
4KB 12 位 bits 12~51(40 位) 普通页,PTE 层
2MB 21 位 bits 21~51(31 位) 大页,PMD 层
1GB 30 位 bits 30~51(22 位) 巨页,PUD 层

所以同一套"PFN << shift | offset"翻译,对大页/巨页也成立,只是 shift 分别是 12/21/30------这正是 #4 大页里"在 PMD/PUD 层提前终止"的位级体现。


三、权限位:MMU 翻译时做硬检查

PTE 里的权限位,MMU 每次访问都会硬件检查,不通过就触发 #PF(缺页/保护违例,见 #3):

  • present(bit 0) :页在不在物理内存。清 0 → 访问触发缺页(#3 的 do_pte_missing 按需分配),或表示页被换出(do_swap_page);
  • RW(bit 1) :可写。页只读(写时复制 COW)时,写访问触发 do_wp_page;
  • USER(bit 2):用户态可访问。清 0 表示内核页,用户态代码访问会触发保护违例(这就是内核地址空间对用户不可见的硬件机制);
  • NX(bit 63) :不可执行。置位后该页不能取指执行(防在数据页上执行代码,是 DEP/W^X 的硬件基础)。注意注释 "only valid after cpuid check"------要先确认 CPU 支持 NX(X86_FEATURE_NX)。

这四个位是"权限"的核心,present/RW/US/NX 的检查全在 MMU 硬件翻译的那一跳里完成。


四、accessed / dirty:CPU 硬件自动置位的"使用痕迹"

accessed(bit 5)和 dirty(bit 6)是两个特殊的位------不是软件设的,是 CPU 硬件在访存时自动置 1 的 (注释 "raised by CPU"):

  • accessed :页被读或写过,CPU 自动置 1。内核用 pte_mkold 清除它,观察"最近有没有被访问",这是 LRU 页回收(决定先回收哪些页)的硬件基础;
  • dirty:页被写过,CPU 自动置 1。文件页被写后 dirty 置位,内核据此知道"这页要回写磁盘"(呼应 #5 的 writeback)。

这两个位让内核不用软件插桩就能知道页的使用情况------硬件在每次访存时顺带记账,内核定期读、用完清,形成"使用 → 观察 → 回收"的闭环。


五、软件位:内核复用的"私有"位

bit 9~11(SOFTW1/2/3)和 57~58(SOFTW4/5)硬件不管,内核复用它们做自己的标记:

c 复制代码
// arch/x86/include/asm/pgtable_types.h (v6.6, line 32)
#define _PAGE_BIT_SPECIAL	_PAGE_BIT_SOFTW1   // 标记"特殊页"(如 zero page、PFN MAP)
#define _PAGE_BIT_UFFD_WP	_PAGE_BIT_SOFTW2   // userfaultfd 写保护
#define _PAGE_BIT_SOFT_DIRTY	_PAGE_BIT_SOFTW3   // 软件脏页跟踪(CGROUP 内存回收)
#define _PAGE_BIT_DEVMAP	_PAGE_BIT_SOFTW4   // 设备映射(devmap,GPU 等)
#define _PAGE_BIT_SAVED_DIRTY	_PAGE_BIT_SOFTW5   // 保存的 dirty 位(暂停时)

SPECIAL 最典型------do_anonymous_page 里读匿名页时,用 pte_mkspecial 把零页映射成 special 页,表示"这不是普通物理页、是共享的特殊页"(#3 §5.1 的零页)。这些位"硬件无视、软件专用",是 PTE 给内核留的私有空间。


六、大页 PTE:PSE 标志 + 更宽的页内偏移

大页(#4)在 PMD/PUD 层"提前终止",靠的是 PSE(Page Size Extension,bit 7):

c 复制代码
#define _PAGE_BIT_PSE		7	/* 4 MB (or 2MB) page */
#define _PAGE_BIT_PAT_LARGE	12	/* On 2MB or 1GB pages */
  • 普通 4KB 页的 PTE 里,bit 7 是 PAT(缓存类型位);
  • 2MB 大页的 PMD 里,bit 7 = PSE 置位 ,表示"这个 PMD 直接指向一个 2MB 物理大页,不再往下走 PTE";此时 PFN 从 bit 21 开始(§2.2),bit 12 变成 PAT_LARGE;
  • 1GB 大页同理在 PUD 层,PFN 从 bit 30 开始。

所以"大页"在 PTE 层面就是:PSE 标志 + PFN 起始位后移 + 省掉下一级页表,三个动作一次完成。


七、缓存类型:PAT + MTRR 决定最终内存类型

PTE 的 bit 3(PWT)、bit 4(PCD)、bit 7(PAT,4KB 页)、bit 12(PAT_LARGE,大页)合起来是页表里的内存类型编码 (PAT = Page Attribute Table)。它和上一章 CPU 系列刚讲的 MTRR 是两套并行机制:

  • MTRR(CPU 寄存器):按物理地址范围定缓存类型(粗粒度,BIOS 设置,全 CPU 一致);
  • PAT(页表项位):按页定缓存类型(细粒度,内核设置,per-PTE)。

最终某个物理地址的缓存类型 = PAT 和 MTRR 结合的结果(两者都指定时按 Intel 的规则取"更保守"的那个)。现代 x86 主要靠 PAT 做细粒度控制(如标记 MMIO 为 uncacheable),MTRR 保留做 BIOS 兜底。这正是 PTE 属性位里 PWT/PCD/PAT 的用途。


八、MMU 翻译全景:虚拟地址怎么变成物理地址

把 #3 的层级 + 本篇的位字段串起来,一次完整翻译:

复制代码
虚拟地址(48 位 / LA48):| PGD(9) | PUD(9) | PMD(9) | PTE(9) | offset(12) |
                                    │
        ┌───────────────────────────┘ 逐级用"当前级索引"查表
        ▼
   PGD → PUD → PMD → PTE(每一级页表项 = 下一级表的物理地址 + 权限位)
                                    │
        ┌───────────────────────────┘ 最后一级 PTE 指向真实物理页
        ▼
   PTE = PFN(40 位) | 属性位
   物理地址 = PFN << 12 | offset      ← 这一跳就是本篇讲的"PFN 编码"

每一级查表时,MMU 都先检查该级页表项的 present/权限位,不通过就 #PF(#3);最后一级 PTE 里取出 PFN,PFN << 12 | offset 拼出物理地址,再交给 cache(按 §七 的 PAT/MTRR 决定缓存类型)去取数。


九、为什么这样设计(Why 层)

9.1 为什么 PTE 要"高位 PFN + 低位属性"这种布局

因为物理地址天然页对齐 (低 12 位一定是 0),这 12 位"空出来"正好塞属性位------不额外占空间就同时表达了"指向哪"(PFN)和"怎么用"(权限/状态)。把必然为 0 的位复用成属性,是位字段设计的经典做法。

9.2 为什么 accessed/dirty 要 CPU 硬件置位,而不是软件跟踪

软件跟踪"哪页被访问/写过"需要在每次访存插桩,代价不可接受。而 CPU 在翻译后写入页表项时顺带 置这两个位(MMU 本来就写了页表项),几乎零成本。"把使用痕迹记在本来就动的页表项上",是硬件配合内存管理的默契。

9.3 为什么 PFN 起始位随页大小变化(而不是固定)

因为"页内偏移"的宽度 = log2(页大小),物理地址 = PFN × 页大小 + 偏移,页越大偏移越宽、PFN 越窄。这个统一的 PFN << shift | offset 公式让 4KB/2MB/1GB 三级页共享同一套翻译逻辑,只是 shift 不同------大页的"省层级"就落在"shift 变大"上。

9.4 为什么要留软件位(bit 9~11 等)

硬件定义的位(present/RW/US/NX)是 x86 架构标准,但内核有自己的记账需求(特殊页、软脏页、userfaultfd 写保护),这些架构标准里没有对应位 。留几个"硬件无视、软件专用"的位,内核就能在不碰硬件语义的前提下扩展自己的标记------"架构位"和"软件位"分层,让内核能演进而不受限于硬件。

9.5 为什么缓存类型要 PAT + MTRR 两套

MTRR 是早期的粗粒度机制(按地址范围),BIOS 用它设显存等大块;但它粒度粗、且全 CPU 要一致。PAT 是后来的细粒度机制(按页),能精确到每一页。两者并存是历史演进:MTRR 保留兼容、PAT 承担细粒度------取"两者更保守"保证正确性。


十、与已有博客的交叉引用

已有博客 本篇覆盖的关系
内存 #3 页表缺页 #3 讲层级和"状态位决定缺页行为",本篇把"状态位"落到每一位:present→do_pte_missing、!present→do_swap_page、!RW→do_wp_page(COW)、NX→执行保护违例
内存 #4 大页 大页 = PSE 标志 + PFN 起始位后移(bit 7 置位、PFN 从 21/30 位开始),是 #4"PMD/PUD 提前终止"的位级实现
CPU #13 特性位 / #2 MTRR 本篇 §七 的 PAT 和 CPU 系列的 MTRR 是"定缓存类型"的两套机制;PTE 的 NX 位要先 X86_FEATURE_NX 确认 CPU 支持
IOMMU #8 页表填充 IOMMU 页表项也是"PFN + 属性位"结构(见 \[blog-iommu-expansion]),和 MMU 的 PTE 是两套独立页表、同一套"PFN<<shift|offset"思想

附:本篇关键源码索引

符号 位置
_PAGE_BIT_PRESENT ~ _PAGE_BIT_NX(PTE 位定义) arch/x86/include/asm/pgtable_types.h:10 ~ :30
软件位复用(SPECIAL/UFFD_WP/SOFT_DIRTY/DEVMAP) arch/x86/include/asm/pgtable_types.h:32 / :34 / :35 / :36
_PAGE_PRESENT / _PAGE_RW 等位掩码 arch/x86/include/asm/pgtable_types.h:49 ~ :69
PTE_PFN_MASK / PTE_FLAGS_MASK arch/x86/include/asm/pgtable_types.h:284 / :290
__PHYSICAL_MASK_SHIFT(52 位物理地址) arch/x86/include/asm/page_64_types.h:54
pte_pfn(提取 PFN) arch/x86/include/asm/pgtable.h:223
pfn_pte(构造 PTE) arch/x86/include/asm/pgtable.h:763
pte_present / pte_mkspecial arch/x86/include/asm/pgtable.h:966 / :483
相关推荐
阿狸猿1 小时前
AI Agent自动写自动化脚本
运维·人工智能·自动化
五块钱的方便面1 小时前
ubuntu下networkmanager不让管理解决办法
linux·运维·ubuntu
云飞云共享云桌面1 小时前
苏州半导体工厂研发部门多个SolidWorks 如何共享服务器来进行研发设计
运维·服务器·3d·自动化·制造
呉師傅1 小时前
Win7剪切文件变成复制文件如何修改【资源管理器异常类故障】
运维·网络·windows·电脑
honsor1 小时前
机房动环实战:H-THDRJ45 双网口温湿度变送器 Modbus RTU RS485 冗余组网方案
运维·服务器·网络
快乐的韭菜根根1 小时前
Slurm 指定 GPU 卡:`File=/dev/nvidiaX` 映射的坑与修复
java·服务器·网络
叼烟扛炮1 小时前
Linux 学习笔记(三十四):线程同步——条件变量与生产消费者模型
linux·运维·服务器·笔记·学习·1024程序员节
L0208241 小时前
Kubernetes Service 与 kube‑proxy 深度详解
运维·算法·云原生·容器·贪心算法·kubernetes