废话不多说,我们直接看文件~XD
还有一类 cpu 快取问题,目前需要一套完全不同的介面来正确处理。
这问题就是处理器 D-cache 中的虚拟别名(virtual aliasing)。
你的 cpu 架构是否容易在其 D-cache 中出现虚拟别名?
如果你的 D-cache 是虚拟索引的,且快取列大於 PAGE_SIZE(页大小),
并且不能防止同一实体位址同时存在多个快取列,就会遇到这个问题。
如果你的 D-cache 有这个问题,首先正确定义 asm/shmparam.h SHMLBA,
它基本上应该是你的 D-cache 的大小(或者如果大小是可变的,则是最大的可能值)。
这样将迫使 SYSv IPC 层只允许使用者程序在这个值的倍数的位址上对共享记忆体进行 mmap。
.. 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” 暂时映射到核心的虚拟位址。
选择这两个页面的虚拟位址方式使得,核心的 load/store 指令发生在,
和使用者映射分页具有相同 “颜色” 的虚拟位址上。
例如,Sparc64 就使用这种技术。
“addr” 参数代表使用者最终要映射这个分页的虚拟位址,
“page” 参数给出了一个指向目标 struct page 的指标。
如果架构并没有 D-cache 别名问题,
这两个介面可以简单地直接使用 memcpy/memset 而不做其他事情。
``void flush_dcache_page(struct page *page)``
这个介面的使用时机:
a. 核心确实写到一个在分页快取(page cache)中的分页 和/或是 high memory
b. 核心要读取一个分页快取的分页,且该分页在使用者空间是可能存在 共享/可写 的映射。
注意:{get,pin}_user_pages{_fast} 已经在任何使用者定址空间的分页
呼叫了 flush_dcache_page,所以驱动程序的程序码几乎不需考虑这个状况。
.. note::
这个介面只需要运作在有可能被映射到使用者程序定址空间的分页快取分页。
因此,VFS 层程序码处理分页快取中 vfs 符号连结(symlinks)的根本不需要使用这个介面。
“核心写入分页快取的分页” 这句话的意思是,具体来说,
内核执行 store 指令,弄脏(dirty)了在该分页的分页->虚拟映射处的资料。
在这介面中,通过更新快取的手段处理 D-cache 的别名问题是很重要的,
如此才可确保这些核心储存对该分页的使用者空间映射是可见的。
推论的情况也同样重要,如果有使用者对这个文件有共享+可写的映射,
我们必须确保核心对这些分页的读取会看到使用者所做的最新的储存指令。
如果架构并没有 D-cache 别名问题,这个介面可以简单地定义为该架构上的 nop。
在 page->flags (PG_arch_1) 中有一个位元是 “architecture private”。
核心保证,对於分页快取的分页,当这样的分页第一次进入分页快取时,
这个位元将被清除。
这使得这些介面可以有更有效率的实作。
如果目前没有使用者程序映射这个分页,
它允许我们“延迟”(也许是无限期)实际的更新过程。
请看 sparc64 的 flush_dcache_page 和 update_mmu_cache 实现,
以了解如何做到这一点。
概念是首先在 flush_dcache_page() 时,如果 page_file_mapping() 回传了一个映射,
且 mapping_mapped 在该映射上回传 %false,只需标记 architecture private 旗标位元。
其後,在 update_mmu_cache() 中,会对这个旗标位元进行检查,
如果设置了,就进行更新,并清除该旗标位元。
.. important::
很重要的是,如果更新被延迟的话,实际的更新会发生在
和 cpu 储存到的分页,使该分页变脏的同一个 CPU 上。
同样,请参阅 sparc64 关於如何处理这个问题的示例。
``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()),
它将使用这两个介面。
任何必要的快取更新或其他需要的快取一致性操作都应该在这里作用。
如果处理器的 icache 没有对 cpu 存储进行窥探(snoop),
那麽很可能会需要为 copy_to_user_page() 更新 icache。
``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 不对这笔储存进行窥探 (snoop),那麽将需要对其进行更新。
``void flush_icache_page(struct vm_area_struct *vma, struct page *page)``
flush_icache_page 的所有功能都可以在 flush_dcache_page
和 update_mmu_cache 中实现。希望在未来能够完全移除这个介面。
今天阅读的内容,是目前阅读起来最难理解的一个部分,只大致上了解这些介面是为了在不同的情境下,正确解决 D-cache aliasing 的问题,但提到解决的方法都相当晦涩难懂,而且英文原文读起来也是相当绕口,感觉需要对核心分页管理有很深入的了解,才能字字句句理解的透彻。
不免俗的记录一下,自己需要再更理解的部分:
for a given page mapped into some user address space, there is always at least one more mapping, that of the kernel in its linear mapping starting at PAGE_OFFSET
为真?今天先完成这部分的部分翻译,接下来的几天,再把其余翻译,以及试图理解内容的而查询各种资料补上,预祝各位中秋节快乐,我们明天见!
<<: 铁人赛 Day20-- 为我们的登入者介面增加登入功能(PHP & MySql) --完成登入功能
GPIO GPIO全称为General-purpose input/output,通用型之输入输出的...
简介 第一次参加铁人赛,大家好,ID的由来为相信任何一门技术,只要投注心力,与正确的学期方向,就能够...
今天主要介绍VSCode开发Flutter时装哪些扩充插件, 还有一些开发时会用到的小眉角,写起来稍...
rekognition这个词俨然已成AWS视觉分析平台的代名词 -- 好记与独特性的词能够帮你免费...
CSS的选择器分为基础选择器以及复合选择器 本日将将继续说明复合选择器 复合选择器可以更准确更高效的...