linux非线性存储,Nonlinear file mapping on Linux kernel 非线性映射分析

通常,Linux总是将文件中某个连续的部分也连续的映射到virtual memory。所以如果想进行非连续的映射,就得使用更多的资源,即分配更多的vm_area_struct。为此kernel引入了一个独立的system call,sys_remap_file_pages去简化。

mm/fremap.c

------

SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size,

unsigned long, prot, unsigned long, pgoff, unsigned long, flags)

它将现存的映射(pgoff,size)移到start,显然这个start必须属于已有的现存映射。所有建立的nonlinear mapping的vm_area_struct在一个链表中,属于address_apce的i_mmap_nonlinear。

最重要的是pte必须被标识为非线性映射的,这样在访问时被正确处理得到正确的页。这个标志位就是_PAGE_FILE,是pte的bit1。pte的bit0是_PAGE_PRESENT,这时!_PAGE_PRESENT。

handle_mm_fault() -> handle_pte_fault():

-------

static inline int handle_pte_fault(struct mm_struct *mm,

struct vm_area_struct *vma, unsigned long address,

pte_t *pte, pmd_t *pmd, unsigned int flags)

{

......

if (!pte_present(entry)) {

if (pte_none(entry)) {

可见非线性映射的页看似不存在,但实际上区别与此。通常pte_none利用_PTE_NONE_MASK去判断pte是否有效,所以必须注意一些特殊的页。比如pte_val无效的页,正常情况下都是pte_none。但有可能用于非线性映射,所以必须确保_PTE_NONE_MASK不会mask掉非线性映射的页,即_PAGE_FILE。

我在fsl P4080的powerpc geust OS遇到这个问题。因为e500mc是32bit的,默认pte也是32bit。但是paravirt后的virtual MMU (hypervisor接管真实的MMU) 采用64bit pte,所以必须update _PTE_NONE_MASK和pte_to_pgoff和pgoff_to_pte按照64bit pte的format。

if (vma->vm_ops) {

if (likely(vma->vm_ops->fault))

return do_linear_fault(mm, vma, address,

pte, pmd, flags, entry);

}

return do_anonymous_page(mm, vma, address,

pte, pmd, flags);

}

if (pte_file(entry))

return do_nonlinear_fault(mm, vma, address,

pte, pmd, flags, entry);

......

而sys_remap_file_pages()主要在检查一些标志后,如果认定vma_area_struct还不是非线性的就需要设置VM_NONLINEAR。同时调用vma_prio_tree_remove()去从优先树移出,再调用vma_nonlinear_insert()插入非线性映射列表(list_add_tail(&vma->shared.vm_set.list, list))中。

更重要的是更新pte,这个由populate_range()安页大小去调用install_file_pte()

-------

static int populate_range(struct mm_struct *mm, struct vm_area_struct *vma,

unsigned long addr, unsigned long size, pgoff_t pgoff)

{

int err;

do {

err = install_file_pte(mm, vma, addr, pgoff, vma->vm_page_prot);

if (err)

return err;

size -= PAGE_SIZE;

addr += PAGE_SIZE;

pgoff++;

} while (size);

.......

接着:

static int install_file_pte(struct mm_struct *mm, struct vm_area_struct *vma,

unsigned long addr, unsigned long pgoff, pgprot_t prot)

{

if (!pte_none(*pte))

zap_pte(mm, vma, addr, pte);

install_file_pte()首先调用zap_pte清除那些存在的pte.主要对在内存的页flush cache和TLB,这是容易理解的.

set_pte_at(mm, addr, pte, pgoff_to_pte(pgoff)

构建新的pte。

如果一切顺利,我们可以利用make_pages_present()换入映射好的页了。

make_pages_present() -> get_user_pages() -> __get_user_pages() -> handle_mm_fault() ->    handle_pte_fault() ->

这里就回到了正常的page fault的路径,

entry = *pte;

if (!pte_present(entry)) {

if (pte_none(entry)) {

if (vma->vm_ops) {

if (likely(vma->vm_ops->fault))

return do_linear_fault(mm, vma, address,

pte, pmd, flags, entry);

}

return do_anonymous_page(mm, vma, address,

pte, pmd, flags);

}

if (pte_file(entry))

return do_nonlinear_fault(mm, vma, address,

pte, pmd, flags, entry);

最终到__do_fault()。

ltp里面有对次系统调用的测试用例:

#> /opt/ltp-full/testcases/bin/remap_file_pages01

remap_file_pages01    1  PASS  :  Non-Linear shm file OK

remap_file_pages01    2  PASS  :  Non-Linear /tmp/ file OK

主用把事先mmap的再进行非线性映射,然后把映射后的值读出跟没映射前写入的值来比较是否正确。

阅读(2793) | 评论(0) | 转发(1) |

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值