.. include:: ../disclaimer-zh_CN.rst :Original: Documentation/core-api/cachetlb.rst :翻译: å¸å»¶è…¾ Yanteng Si <siyanteng@loongson.cn> 周彬彬 Binbin Zhou <zhoubinbin@loongson.cn> :æ ¡è¯‘: å´æƒ³æˆ Wu XiangCheng <bobwxc@email.cn> .. _cn_core-api_cachetlb: ====================== Linux下的缓å˜å’ŒTLB刷新 ====================== :作者: David S. Miller <davem@redhat.com> *译注:TLB,Translation Lookaside Buffer,页表缓å˜/å˜æ¢æ—查缓冲器* 本文æ述了由Linux虚拟内å˜å系统调用的缓å˜/TLB刷新接å£ã€‚它列举了æ¯ä¸ªæŽ¥ å£ï¼Œæ述了它的预期目的,以åŠæŽ¥å£è¢«è°ƒç”¨åŽçš„预期副作用。 下é¢æ述的副作用是针对å•å¤„ç†å™¨çš„实现,以åŠåœ¨å•ä¸ªå¤„ç†å™¨ä¸Šå‘生的情况。若 为SMP,则åªéœ€å°†å®šä¹‰ç®€å•åœ°æ‰©å±•ä¸€ä¸‹ï¼Œä½¿å‘生在æŸä¸ªç‰¹å®šæŽ¥å£çš„副作用扩展到系 统的所有处ç†å™¨ä¸Šã€‚ä¸è¦è¢«è¿™å¥è¯å“到,以为SMP的缓å˜/tlb刷新一定是很低 效的,事实上,这是一个å¯ä»¥è¿›è¡Œå¾ˆå¤šä¼˜åŒ–的领域。例如,如果å¯ä»¥è¯æ˜Žä¸€ä¸ªç”¨ 户地å€ç©ºé—´ä»Žæœªåœ¨æŸä¸ªcpu上执行过(è§mm_cpumask()),那么就ä¸éœ€è¦åœ¨è¯¥ cpu上对这个地å€ç©ºé—´è¿›è¡Œåˆ·æ–°ã€‚ 首先是TLB刷新接å£ï¼Œå› 为它们是最简å•çš„。在Linux下,TLB被抽象为cpu 用æ¥ç¼“å˜ä»Žè½¯ä»¶é¡µè¡¨èŽ·å¾—的虚拟->物ç†åœ°å€è½¬æ¢çš„东西。这æ„味ç€ï¼Œå¦‚果软件页 表å‘生å˜åŒ–,这个“TLBâ€ç¼“å˜ä¸å°±æœ‰å¯èƒ½å‡ºçŽ°è¿‡æ—¶ï¼ˆè„ï¼‰çš„ç¿»è¯‘ã€‚å› æ¤ï¼Œå½“软件页表 å‘生å˜åŒ–æ—¶ï¼Œå†…æ ¸ä¼šåœ¨é¡µè¡¨å‘生 *å˜åŒ–åŽ* 调用以下一ç§åˆ·æ–°æ–¹æ³•ï¼š 1) ``void flush_tlb_all(void)`` æœ€ä¸¥æ ¼çš„åˆ·æ–°ã€‚åœ¨è¿™ä¸ªæŽ¥å£è¿è¡ŒåŽï¼Œä»»ä½•ä»¥å‰çš„页表修改都会对cpuå¯è§ã€‚ è¿™é€šå¸¸æ˜¯åœ¨å†…æ ¸é¡µè¡¨è¢«æ”¹å˜æ—¶è°ƒç”¨çš„ï¼Œå› ä¸ºè¿™ç§è½¬æ¢åœ¨æœ¬è´¨ä¸Šæ˜¯â€œå…¨å±€â€çš„。 2) ``void flush_tlb_mm(struct mm_struct *mm)`` 这个接å£ä»ŽTLBä¸åˆ·æ–°æ•´ä¸ªç”¨æˆ·åœ°å€ç©ºé—´ã€‚在è¿è¡ŒåŽï¼Œè¿™ä¸ªæŽ¥å£å¿…é¡»ç¡®ä¿ ä»¥å‰å¯¹åœ°å€ç©ºé—´â€˜mm’的任何页表修改对cpuæ¥è¯´æ˜¯å¯è§çš„。也就是说,在 è¿è¡ŒåŽï¼ŒTLBä¸ä¸ä¼šæœ‰â€˜mm’的页表项。 这个接å£è¢«ç”¨æ¥å¤„ç†æ•´ä¸ªåœ°å€ç©ºé—´çš„页表æ“作,比如在forkå’Œexec过程 ä¸å‘生的事情。 3) ``void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)`` 这里我们è¦ä»ŽTLBä¸åˆ·æ–°ä¸€ä¸ªç‰¹å®šèŒƒå›´çš„(用户)虚拟地å€è½¬æ¢ã€‚在è¿è¡ŒåŽï¼Œ 这个接å£å¿…须确ä¿ä»¥å‰å¯¹â€˜start’到‘end-1’范围内的地å€ç©ºé—´â€˜vma->vm_mm’ 的任何页表修改对cpuæ¥è¯´æ˜¯å¯è§çš„。也就是说,在è¿è¡ŒåŽï¼ŒTLBä¸ä¸ä¼šæœ‰ ‘mm’的页表项用于‘start’到‘end-1’范围内的虚拟地å€ã€‚ “vmaâ€æ˜¯ç”¨äºŽè¯¥åŒºåŸŸçš„备份å˜å‚¨ã€‚主è¦æ˜¯ç”¨äºŽmunmap()类型的æ“作。 æ供这个接å£æ˜¯å¸Œæœ›ç«¯å£èƒ½å¤Ÿæ‰¾åˆ°ä¸€ä¸ªåˆé€‚的有效方法æ¥ä»ŽTLBä¸åˆ 除多 个页é¢å¤§å°çš„转æ¢ï¼Œè€Œä¸æ˜¯è®©å†…æ ¸ä¸ºæ¯ä¸ªå¯èƒ½è¢«ä¿®æ”¹çš„页表项调用 flush_tlb_page(è§ä¸‹æ–‡)。 4) ``void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)`` 这一次我们需è¦ä»ŽTLBä¸åˆ 除PAGE_SIZE大å°çš„转æ¢ã€‚‘vma’是Linux用æ¥è·Ÿ 踪进程的mmap区域的支æŒç»“构体,地å€ç©ºé—´å¯ä»¥é€šè¿‡vma->vm_mmèŽ·å¾—ã€‚å¦ å¤–ï¼Œå¯ä»¥é€šè¿‡æµ‹è¯•ï¼ˆvma->vm_flags & VM_EXEC)æ¥æŸ¥çœ‹è¿™ä¸ªåŒºåŸŸæ˜¯å¦æ˜¯ å¯æ‰§è¡Œçš„ï¼ˆå› æ¤åœ¨split-tlb类型的设置ä¸å¯èƒ½åœ¨â€œæŒ‡ä»¤TLBâ€ä¸ï¼‰ã€‚ 在è¿è¡ŒåŽï¼Œè¿™ä¸ªæŽ¥å£å¿…须确ä¿ä¹‹å‰å¯¹ç”¨æˆ·è™šæ‹Ÿåœ°å€â€œaddrâ€çš„地å€ç©ºé—´ “vma->vm_mmâ€çš„页表修改对cpuæ¥è¯´æ˜¯å¯è§çš„。也就是说,在è¿è¡ŒåŽï¼ŒTLB ä¸ä¸ä¼šæœ‰è™šæ‹Ÿåœ°å€â€˜addr’的‘vma->vm_mm’的页表项。 这主è¦æ˜¯åœ¨æ•…障处ç†æ—¶ä½¿ç”¨ã€‚ 5) ``void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t *ptep)`` 在æ¯ä¸ªç¼ºé¡µå¼‚常结æŸæ—¶ï¼Œè¿™ä¸ªç¨‹åºè¢«è°ƒç”¨ï¼Œä»¥å‘Šè¯‰ä½“系结构特定的代ç ,在 软件页表ä¸ï¼Œåœ¨åœ°å€ç©ºé—´â€œvma->vm_mmâ€çš„虚拟地å€â€œåœ°å€â€å¤„,现在å˜åœ¨ 一个翻译。 å¯ä»¥ç”¨å®ƒæ‰€é€‰æ‹©çš„任何方å¼ä½¿ç”¨è¿™ä¸ªä¿¡æ¯æ¥è¿›è¡Œç§»æ¤ã€‚例如,它å¯ä»¥ä½¿ç”¨è¿™ 个事件æ¥ä¸ºè½¯ä»¶ç®¡ç†çš„TLBé…置预装TLB转æ¢ã€‚ç›®å‰sparc64移æ¤å°±æ˜¯è¿™ä¹ˆå¹² 的。 接下æ¥ï¼Œæˆ‘们有缓å˜åˆ·æ–°æŽ¥å£ã€‚一般æ¥è¯´ï¼Œå½“Linux将现有的虚拟->物ç†æ˜ å°„ 改å˜ä¸ºæ–°çš„值时,其顺åºå°†æ˜¯ä»¥ä¸‹å½¢å¼ä¹‹ä¸€:: 1) flush_cache_mm(mm); change_all_page_tables_of(mm); flush_tlb_mm(mm); 2) flush_cache_range(vma, start, end); change_range_of_page_tables(mm, start, end); flush_tlb_range(vma, start, end); 3) flush_cache_page(vma, addr, pfn); set_pte(pte_pointer, new_pte_val); flush_tlb_page(vma, addr); 缓å˜çº§åˆ«çš„刷新将永远是第一ä½çš„ï¼Œå› ä¸ºè¿™å…许我们æ£ç¡®å¤„ç†é‚£äº›ç¼“å˜ä¸¥æ ¼ï¼Œ 且在虚拟地å€è¢«ä»Žç¼“å˜ä¸åˆ·æ–°æ—¶è¦æ±‚一个虚拟地å€çš„虚拟->物ç†è½¬æ¢å˜åœ¨çš„系统。 HyperSparc cpuå°±æ˜¯è¿™æ ·ä¸€ä¸ªå…·æœ‰è¿™ç§å±žæ€§çš„cpu。 下é¢çš„缓å˜åˆ·æ–°ç¨‹åºåªéœ€è¦åœ¨ç‰¹å®šçš„cpu需è¦çš„范围内处ç†ç¼“å˜åˆ·æ–°ã€‚大多数 情况下,这些程åºå¿…须为cpu实现,这些cpu有虚拟索引的缓å˜ï¼Œå½“虚拟->物 ç†è½¬æ¢è¢«æ”¹å˜æˆ–ç§»é™¤æ—¶ï¼Œå¿…é¡»è¢«åˆ·æ–°ã€‚å› æ¤ï¼Œä¾‹å¦‚,IA32处ç†å™¨çš„物ç†ç´¢å¼• 的物ç†æ ‡è®°çš„缓å˜æ²¡æœ‰å¿…è¦å®žçŽ°è¿™äº›æŽ¥å£ï¼Œå› 为这些缓å˜æ˜¯å®Œå…¨åŒæ¥çš„,并 且ä¸ä¾èµ–于翻译信æ¯ã€‚ 下é¢é€ä¸ªåˆ—出这些程åº: 1) ``void flush_cache_mm(struct mm_struct *mm)`` 这个接å£å°†æ•´ä¸ªç”¨æˆ·åœ°å€ç©ºé—´ä»Žé«˜é€Ÿç¼“å˜ä¸åˆ·æŽ‰ã€‚也就是说,在è¿è¡ŒåŽï¼Œ 将没有与‘mm’相关的缓å˜è¡Œã€‚ 这个接å£è¢«ç”¨æ¥å¤„ç†æ•´ä¸ªåœ°å€ç©ºé—´çš„页表æ“作,比如在退出和执行过程 ä¸å‘生的事情。 2) ``void flush_cache_dup_mm(struct mm_struct *mm)`` 这个接å£å°†æ•´ä¸ªç”¨æˆ·åœ°å€ç©ºé—´ä»Žé«˜é€Ÿç¼“å˜ä¸åˆ·æ–°æŽ‰ã€‚也就是说,在è¿è¡Œ åŽï¼Œå°†æ²¡æœ‰ä¸Žâ€˜mm’相关的缓å˜è¡Œã€‚ 这个接å£è¢«ç”¨æ¥å¤„ç†æ•´ä¸ªåœ°å€ç©ºé—´çš„页表æ“作,比如在fork过程ä¸å‘生 的事情。 这个选项与flush_cache_mm分开,以å…许对VIPT缓å˜è¿›è¡Œä¸€äº›ä¼˜åŒ–。 3) ``void flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)`` 在这里,我们è¦ä»Žç¼“å˜ä¸åˆ·æ–°ä¸€ä¸ªç‰¹å®šèŒƒå›´çš„(用户)虚拟地å€ã€‚è¿è¡Œ åŽï¼Œåœ¨â€œstartâ€åˆ°â€œend-1â€èŒƒå›´å†…的虚拟地å€çš„“vma->vm_mmâ€çš„缓å˜ä¸ 将没有页表项。 “vmaâ€æ˜¯è¢«ç”¨äºŽè¯¥åŒºåŸŸçš„备份å˜å‚¨ã€‚主è¦æ˜¯ç”¨äºŽmunmap()类型的æ“作。 æ供这个接å£æ˜¯å¸Œæœ›ç«¯å£èƒ½å¤Ÿæ‰¾åˆ°ä¸€ä¸ªåˆé€‚的有效方法æ¥ä»Žç¼“å˜ä¸åˆ 除多个页é¢å¤§å°çš„区域, 而ä¸æ˜¯è®©å†…æ ¸ä¸ºæ¯ä¸ªå¯èƒ½è¢«ä¿®æ”¹çš„页表项调 用 flush_cache_page (è§ä¸‹æ–‡)。 4) ``void flush_cache_page(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn)`` 这一次我们需è¦ä»Žç¼“å˜ä¸åˆ 除一个PAGE_SIZE大å°çš„区域。“vmaâ€æ˜¯ Linux用æ¥è·Ÿè¸ªè¿›ç¨‹çš„mmap区域的支æŒç»“构体,地å€ç©ºé—´å¯ä»¥é€šè¿‡ vma->vm_mm获得。å¦å¤–,我们å¯ä»¥é€šè¿‡æµ‹è¯•ï¼ˆvma->vm_flags & VM_EXEC)æ¥æŸ¥çœ‹è¿™ä¸ªåŒºåŸŸæ˜¯å¦æ˜¯å¯æ‰§è¡Œçš„ï¼ˆå› æ¤åœ¨â€œHarvardâ€ç±» 型的缓å˜å¸ƒå±€ä¸å¯èƒ½æ˜¯åœ¨â€œæŒ‡ä»¤ç¼“å˜â€ä¸ï¼‰ã€‚ “pfnâ€è¡¨ç¤ºâ€œaddrâ€æ‰€å¯¹åº”的物ç†é¡µæ¡†ï¼ˆé€šè¿‡PAGE_SHIFT左移这个 值æ¥èŽ·å¾—物ç†åœ°å€ï¼‰ã€‚æ£æ˜¯è¿™ä¸ªæ˜ 射应该从缓å˜ä¸åˆ 除。 在è¿è¡Œä¹‹åŽï¼Œå¯¹äºŽè™šæ‹Ÿåœ°å€â€˜addr’的‘vma->vm_mm’,在缓å˜ä¸ä¸ä¼š 有任何页表项,它被翻译æˆâ€˜pfn’。 这主è¦æ˜¯åœ¨æ•…障处ç†è¿‡ç¨‹ä¸ä½¿ç”¨ã€‚ 5) ``void flush_cache_kmaps(void)`` åªæœ‰åœ¨å¹³å°ä½¿ç”¨é«˜ä½å†…å˜çš„情况下æ‰éœ€è¦å®žçŽ°è¿™ä¸ªç¨‹åºã€‚它将在所有的 kmaps失效之å‰è¢«è°ƒç”¨ã€‚ è¿è¡ŒåŽï¼Œå†…æ ¸è™šæ‹Ÿåœ°å€èŒƒå›´PKMAP_ADDR(0)到PKMAP_ADDR(LAST_PKMAP) 的缓å˜ä¸å°†æ²¡æœ‰é¡µè¡¨é¡¹ã€‚ 这个程åºåº”该在asm/highmem.hä¸å®žçŽ°ã€‚ 6) ``void flush_cache_vmap(unsigned long start, unsigned long end)`` ``void flush_cache_vunmap(unsigned long start, unsigned long end)`` 在这里,在这两个接å£ä¸ï¼Œæˆ‘们从缓å˜ä¸åˆ·æ–°ä¸€ä¸ªç‰¹å®šèŒƒå›´çš„ï¼ˆå†…æ ¸ï¼‰ 虚拟地å€ã€‚è¿è¡ŒåŽï¼Œåœ¨â€œstartâ€åˆ°â€œend-1â€èŒƒå›´å†…的虚拟地å€çš„å†…æ ¸åœ° å€ç©ºé—´çš„缓å˜ä¸ä¸ä¼šæœ‰é¡µè¡¨é¡¹ã€‚ 这两个程åºä¸çš„第一个是在vmap_range()安装了页表项之åŽè°ƒç”¨çš„。 第二个是在vunmap_range()åˆ é™¤é¡µè¡¨é¡¹ä¹‹å‰è°ƒç”¨çš„。 还有一类cpu缓å˜é—®é¢˜ï¼Œç›®å‰éœ€è¦ä¸€å¥—完全ä¸åŒçš„接å£æ¥æ£ç¡®å¤„ç†ã€‚最大 的问题是处ç†å™¨çš„æ•°æ®ç¼“å˜ä¸çš„虚拟别å。 .. note:: 这段内容有些晦涩,为了å‡è½»ä¸æ–‡é˜…读压力,特作æ¤è¯‘注。 别å(alias)属于缓å˜ä¸€è‡´æ€§é—®é¢˜ï¼Œå½“ä¸åŒçš„虚拟地å€æ˜ 射相åŒçš„ 物ç†åœ°å€ï¼Œè€Œè¿™äº›è™šæ‹Ÿåœ°å€çš„indexä¸åŒï¼Œæ¤æ—¶å°±å‘生了别å现象(多 个虚拟地å€è¢«ç§°ä¸ºåˆ«å)。通俗点æ¥è¯´å°±æ˜¯æŒ‡åŒä¸€ä¸ªç‰©ç†åœ°å€çš„æ•°æ®è¢« åŠ è½½åˆ°ä¸åŒçš„cachelineä¸å°±ä¼šå‡ºçŽ°åˆ«å现象。 常è§çš„解决方法有两ç§ï¼šç¬¬ä¸€ç§æ˜¯ç¡¬ä»¶ç»´æŠ¤ä¸€è‡´æ€§ï¼Œè®¾è®¡ç‰¹å®šçš„cpu电 è·¯æ¥è§£å†³é—®é¢˜ï¼ˆä¾‹å¦‚设计为PIPTçš„cache);第二ç§æ˜¯è½¯ä»¶ç»´æŠ¤ä¸€è‡´æ€§ï¼Œ 就是下é¢ä»‹ç»çš„sparc的解决方案——页é¢æŸ“色,涉åŠçš„技术细节太多, 译者ä¸ä¾¿å±•å¼€ï¼Œè¯·è¯»è€…自行查阅相关资料。 您的移æ¤æ˜¯å¦å®¹æ˜“在其D-cacheä¸å‡ºçŽ°è™šæ‹Ÿåˆ«å?嗯,如果您的D-cache 是虚拟索引的,且cache大于PAGE_SIZE(页大å°ï¼‰ï¼Œå¹¶ä¸”ä¸èƒ½é˜²æ¢åŒä¸€ 物ç†åœ°å€çš„多个cacheè¡ŒåŒæ—¶å˜åœ¨ï¼Œæ‚¨å°±ä¼šé‡åˆ°è¿™ä¸ªé—®é¢˜ã€‚ å¦‚æžœä½ çš„D-cache有这个问题,首先æ£ç¡®å®šä¹‰asm/shmparam.h SHMLBA, å®ƒåŸºæœ¬ä¸Šåº”è¯¥æ˜¯ä½ çš„è™šæ‹Ÿå¯»å€D-cache的大å°ï¼ˆæˆ–者如果大å°æ˜¯å¯å˜çš„, 则是最大的å¯èƒ½å¤§å°ï¼‰ã€‚这个设置将迫使SYSv IPC层åªå…许用户进程在 这个值的å€æ•°çš„地å€ä¸Šå¯¹å…±äº«å†…å˜è¿›è¡Œæ˜ 射。 .. note:: 这并ä¸èƒ½è§£å†³å…±äº«mmaps的问题,请查看sparc64移æ¤è§£å†³ 这个问题的一个方法(特别是 SPARC_FLAG_MMAPSHARED)。 接下æ¥ï¼Œä½ 必须解决所有其他情况下的D-cache别å问题。请记ä½è¿™ä¸ªäº‹ 实,对于一个给定的页é¢æ˜ 射到æŸä¸ªç”¨æˆ·åœ°å€ç©ºé—´ï¼Œæ€»æ˜¯è‡³å°‘è¿˜æœ‰ä¸€ä¸ªæ˜ å°„ï¼Œé‚£å°±æ˜¯å†…æ ¸åœ¨å…¶çº¿æ€§æ˜ å°„ä¸ä»ŽPAGE_OFFSETå¼€å§‹ã€‚å› æ¤ï¼Œä¸€æ—¦ç¬¬ä¸€ä¸ª 用户将一个给定的物ç†é¡µæ˜ 射到它的地å€ç©ºé—´ï¼Œå°±æ„味ç€D-cache的别å 问题有å¯èƒ½å˜åœ¨ï¼Œå› ä¸ºå†…æ ¸å·²ç»å°†è¿™ä¸ªé¡µæ˜ 射到它的虚拟地å€ã€‚ ``void copy_user_page(void *to, void *from, unsigned long addr, struct page *page)`` ``void clear_user_page(void *to, unsigned long addr, struct page *page)`` 这两个程åºåœ¨ç”¨æˆ·åŒ¿å或COW页ä¸å˜å‚¨æ•°æ®ã€‚它å…许一个端å£æœ‰æ•ˆåœ° é¿å…ç”¨æˆ·ç©ºé—´å’Œå†…æ ¸ä¹‹é—´çš„D-cache别å问题。 例如,一个端å£å¯ä»¥åœ¨å¤åˆ¶è¿‡ç¨‹ä¸æŠŠâ€œfromâ€å’Œâ€œtoâ€æš‚æ—¶æ˜ å°„åˆ°å†…æ ¸ 的虚拟地å€ä¸Šã€‚这两个页é¢çš„虚拟地å€çš„选择方å¼æ˜¯ï¼Œå†…æ ¸çš„åŠ è½½/å˜ å‚¨æŒ‡ä»¤å‘生在虚拟地å€ä¸Šï¼Œè€Œè¿™äº›è™šæ‹Ÿåœ°å€ä¸Žç”¨æˆ·çš„页é¢æ˜ å°„æ˜¯ç›¸åŒ çš„â€œé¢œè‰²â€ã€‚例如,Sparc64就使用这ç§æŠ€æœ¯ã€‚ “addrâ€å‚数告诉了用户最终è¦æ˜ 射这个页é¢çš„虚拟地å€ï¼Œâ€œpageâ€å‚ 数给出了一个指å‘ç›®æ ‡é¡µç»“æž„ä½“çš„æŒ‡é’ˆã€‚ 如果D-cache别åä¸æ˜¯é—®é¢˜ï¼Œè¿™ä¸¤ä¸ªç¨‹åºå¯ä»¥ç®€å•åœ°ç›´æŽ¥è°ƒç”¨ memcpy/memset而ä¸åšå…¶ä»–事情。 ``void flush_dcache_page(struct page *page)`` ä»»ä½•æ—¶å€™ï¼Œå½“å†…æ ¸å†™åˆ°ä¸€ä¸ªé¡µé¢ç¼“å˜é¡µï¼Œæˆ–è€…å†…æ ¸è¦ä»Žä¸€ä¸ªé¡µé¢ç¼“å˜ é¡µä¸è¯»å‡ºï¼Œå¹¶ä¸”这个页é¢çš„用户空间共享/å¯å†™æ˜ å°„å¯èƒ½å˜åœ¨æ—¶ï¼Œ 这个程åºå°±ä¼šè¢«è°ƒç”¨ã€‚ .. note:: 这个程åºåªéœ€è¦ä¸ºæœ‰å¯èƒ½è¢«æ˜ 射到用户进程的地å€ç©ºé—´çš„ 页é¢ç¼“å˜è°ƒç”¨ã€‚å› æ¤ï¼Œä¾‹å¦‚,处ç†é¡µé¢ç¼“å˜ä¸vfs符å·é“¾ 接的VFS层代ç æ ¹æœ¬ä¸éœ€è¦è°ƒç”¨è¿™ä¸ªæŽ¥å£ã€‚ â€œå†…æ ¸å†™å…¥é¡µé¢ç¼“å˜çš„页é¢â€è¿™å¥è¯çš„æ„æ€æ˜¯ï¼Œå…·ä½“æ¥è¯´ï¼Œå†…æ ¸æ‰§è¡Œå˜ å‚¨æŒ‡ä»¤ï¼Œåœ¨è¯¥é¡µé¢çš„页é¢->è™šæ‹Ÿæ˜ å°„å¤„å¼„è„该页é¢çš„æ•°æ®ã€‚在这里,通 过刷新的手段处ç†D-cache的别å是很é‡è¦çš„,以确ä¿è¿™äº›å†…æ ¸å˜å‚¨å¯¹ è¯¥é¡µçš„ç”¨æˆ·ç©ºé—´æ˜ å°„æ˜¯å¯è§çš„。 推论的情况也åŒæ ·é‡è¦ï¼Œå¦‚果有用户对这个文件有共享+å¯å†™çš„æ˜ å°„ï¼Œ 我们必须确ä¿å†…æ ¸å¯¹è¿™äº›é¡µé¢çš„读å–会看到用户所åšçš„最新的å˜å‚¨ã€‚ 如果D-cache别åä¸æ˜¯ä¸€ä¸ªé—®é¢˜ï¼Œè¿™ä¸ªç¨‹åºå¯ä»¥ç®€å•åœ°å®šä¹‰ä¸ºè¯¥æž¶æž„上 çš„nop。 在page->flags (PG_arch_1)ä¸æœ‰ä¸€ä¸ªä½æ˜¯â€œæž¶æž„ç§æœ‰â€ã€‚å†…æ ¸ä¿è¯ï¼Œ 对于分页缓å˜çš„页é¢ï¼Œå½“è¿™æ ·çš„é¡µé¢ç¬¬ä¸€æ¬¡è¿›å…¥åˆ†é¡µç¼“å˜æ—¶ï¼Œå®ƒå°†æ¸…除 这个ä½ã€‚ 这使得这些接å£å¯ä»¥æ›´æœ‰æ•ˆåœ°è¢«å®žçŽ°ã€‚如果目å‰æ²¡æœ‰ç”¨æˆ·è¿›ç¨‹æ˜ 射这个 页é¢ï¼Œå®ƒå…许我们“推迟â€ï¼ˆä¹Ÿè®¸æ˜¯æ— é™æœŸï¼‰å®žé™…的刷新过程。请看 sparc64çš„flush_dcache_pageå’Œupdate_mmu_cache实现,以了解如 何åšåˆ°è¿™ä¸€ç‚¹ã€‚ 这个想法是,首先在flush_dcache_page()时,如果page->mapping->i_mmap æ˜¯ä¸€ä¸ªç©ºæ ‘ï¼Œåªéœ€æ ‡è®°æž¶æž„ç§æœ‰é¡µæ ‡å¿—ä½ã€‚之åŽï¼Œåœ¨update_mmu_cache() ä¸ï¼Œä¼šå¯¹è¿™ä¸ªæ ‡å¿—ä½è¿›è¡Œæ£€æŸ¥ï¼Œå¦‚æžœè®¾ç½®äº†ï¼Œå°±è¿›è¡Œåˆ·æ–°ï¼Œå¹¶æ¸…é™¤æ ‡å¿—ä½ã€‚ .. important:: 通常很é‡è¦çš„æ˜¯ï¼Œå¦‚æžœä½ æŽ¨è¿Ÿåˆ·æ–°ï¼Œå®žé™…çš„åˆ·æ–°å‘生在åŒä¸€ä¸ª CPUä¸Šï¼Œå› ä¸ºå®ƒå°†cpuå˜å‚¨åˆ°é¡µé¢ä¸Šï¼Œä½¿å…¶å˜è„。åŒæ ·ï¼Œè¯·çœ‹ sparc64关于如何处ç†è¿™ä¸ªé—®é¢˜çš„例å。 ``void flush_dcache_folio(struct folio *folio)`` 该函数的调用情形与flush_dcache_page()相åŒã€‚它å…许架构针对刷新整个 folio页é¢è¿›è¡Œä¼˜åŒ–,而ä¸æ˜¯ä¸€æ¬¡åˆ·æ–°ä¸€é¡µã€‚ ``void copy_to_user_page(struct vm_area_struct *vma, struct page *page, unsigned long user_vaddr, void *dst, void *src, int len)`` ``void copy_from_user_page(struct vm_area_struct *vma, struct page *page, unsigned long user_vaddr, void *dst, void *src, int len)`` å½“å†…æ ¸éœ€è¦å¤åˆ¶ä»»æ„çš„æ•°æ®è¿›å‡ºä»»æ„的用户页时(比如ptrace()),它将使 用这两个程åºã€‚ 任何必è¦çš„缓å˜åˆ·æ–°æˆ–其他需è¦å‘生的一致性æ“作都应该在这里å‘生。如果 处ç†å™¨çš„指令缓å˜æ²¡æœ‰å¯¹cpuå˜å‚¨è¿›è¡Œçª¥æŽ¢ï¼Œé‚£ä¹ˆä½ 很å¯èƒ½éœ€è¦ä¸º copy_to_user_page()刷新指令缓å˜ã€‚ ``void flush_anon_page(struct vm_area_struct *vma, struct page *page, unsigned long vmaddr)`` å½“å†…æ ¸éœ€è¦è®¿é—®ä¸€ä¸ªåŒ¿å页的内容时,它会调用这个函数(目å‰åªæœ‰ get_user_pages())。注æ„:flush_dcache_page()æ•…æ„对匿å页ä¸èµ·ä½œ 用。默认的实现是nop(对于所有相干的架构应该ä¿æŒè¿™æ ·ï¼‰ã€‚对于ä¸ä¸€è‡´æ€§ 的架构,它应该刷新vmaddr处的页é¢ç¼“å˜ã€‚ ``void flush_icache_range(unsigned long start, unsigned long end)`` å½“å†…æ ¸å˜å‚¨åˆ°å®ƒå°†æ‰§è¡Œçš„地å€ä¸æ—¶ï¼ˆä¾‹å¦‚åœ¨åŠ è½½æ¨¡å—时),这个函数被调用。 如果icacheä¸å¯¹å˜å‚¨è¿›è¡Œçª¥æŽ¢ï¼Œé‚£ä¹ˆè¿™ä¸ªç¨‹åºå°†éœ€è¦å¯¹å…¶è¿›è¡Œåˆ·æ–°ã€‚ ``void flush_icache_page(struct vm_area_struct *vma, struct page *page)`` flush_icache_page的所有功能都å¯ä»¥åœ¨flush_dcache_pageå’Œupdate_mmu_cache ä¸å®žçŽ°ã€‚在未æ¥ï¼Œæˆ‘ä»¬å¸Œæœ›èƒ½å¤Ÿå®Œå…¨åˆ é™¤è¿™ä¸ªæŽ¥å£ã€‚ 最åŽä¸€ç±»API是用于I/Oåˆ°å†…æ ¸å†…ç‰¹æ„设置的别å地å€èŒƒå›´ã€‚è¿™ç§åˆ«å是通过使用 vmap/vmalloc APIè®¾ç½®çš„ã€‚ç”±äºŽå†…æ ¸I/O是通过物ç†é¡µè¿›è¡Œçš„,I/Oå系统å‡å®šç”¨æˆ· æ˜ å°„å’Œå†…æ ¸åç§»æ˜ å°„æ˜¯å”¯ä¸€çš„åˆ«å。这对vmap别åæ¥è¯´æ˜¯ä¸æ£ç¡®çš„ï¼Œæ‰€ä»¥å†…æ ¸ä¸ä»»ä½• 试图对vmap区域进行I/O的东西都必须手动管ç†ä¸€è‡´æ€§ã€‚它必须在åšI/O之å‰åˆ·æ–°vmap 范围,并在I/O返回åŽä½¿å…¶å¤±æ•ˆã€‚ ``void flush_kernel_vmap_range(void *vaddr, int size)`` 刷新vmap区域ä¸æŒ‡å®šçš„虚拟地å€èŒƒå›´çš„å†…æ ¸ç¼“å˜ã€‚这是为了确ä¿å†…æ ¸åœ¨vmap范围 内修改的任何数æ®å¯¹ç‰©ç†é¡µæ˜¯å¯è§çš„。这个设计是为了使这个区域å¯ä»¥å®‰å…¨åœ°æ‰§ è¡ŒI/O。注æ„,这个API并 *没有* 刷新该区域的åç§»æ˜ å°„åˆ«å。 ``void invalidate_kernel_vmap_range(void *vaddr, int size) invalidates`` 在vmap区域的一个给定的虚拟地å€èŒƒå›´çš„缓å˜ï¼Œè¿™å¯ä»¥é˜²æ¢å¤„ç†å™¨åœ¨ç‰©ç†é¡µçš„I/O å‘生时通过投机性地读å–æ•°æ®è€Œä½¿ç¼“å˜å˜è„。这åªå¯¹è¯»å…¥vmap区域的数æ®æ˜¯å¿…è¦çš„。