Chinaunix首页 | 论坛 | 博客
  • 博客访问: 572426
  • 博文数量: 168
  • 博客积分: 62
  • 博客等级: 民兵
  • 技术积分: 442
  • 用 户 组: 普通用户
  • 注册时间: 2011-04-30 11:45
文章分类

全部博文(168)

文章存档

2016年(2)

2015年(19)

2014年(98)

2013年(22)

2012年(6)

2011年(21)

分类:

2012-05-13 20:34:19

通常,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的再进行非线性映射,然后把映射后的值读出跟没映射前写入的值来比较是否正确。







阅读(2207) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~