【问题标题】:Walking page tables of a process in LinuxLinux中进程的遍历页表
【发布时间】:2012-01-23 23:38:25
【问题描述】:

我正在尝试为 linux 中的进程导航页表。在一个内核模块中,我实现了以下功能:

static struct page *walk_page_table(unsigned long addr)
{
    pgd_t *pgd;
    pte_t *ptep, pte;
    pud_t *pud;
    pmd_t *pmd;

    struct page *page = NULL;
    struct mm_struct *mm = current->mm;

    pgd = pgd_offset(mm, addr);
    if (pgd_none(*pgd) || pgd_bad(*pgd))
        goto out;
    printk(KERN_NOTICE "Valid pgd");

    pud = pud_offset(pgd, addr);
    if (pud_none(*pud) || pud_bad(*pud))
        goto out;
    printk(KERN_NOTICE "Valid pud");

    pmd = pmd_offset(pud, addr);
    if (pmd_none(*pmd) || pmd_bad(*pmd))
        goto out;
    printk(KERN_NOTICE "Valid pmd");

    ptep = pte_offset_map(pmd, addr);
    if (!ptep)
        goto out;
    pte = *ptep;

    page = pte_page(pte);
    if (page)
        printk(KERN_INFO "page frame struct is @ %p", page);

 out:
    return page;
}

这个函数是从ioctl调用的,addr是进程地址空间中的一个虚拟地址:

static int my_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long addr)
{
   struct page *page = walk_page_table(addr);
   ...
   return 0;
}

奇怪的是在用户空间进程中调用ioctl,这个段错误......但我寻找页表条目的方式似乎是正确的,因为我获得了dmesg 例如每个ioctl调用:

[ 1721.437104] Valid pgd
[ 1721.437108] Valid pud
[ 1721.437108] Valid pmd
[ 1721.437110] page frame struct is @ c17d9b80

那么为什么进程不能正确完成 `ioctl' 调用呢?也许我必须在导航页表之前锁定一些东西?

我正在使用内核 2.6.35-22 和三级页表。

谢谢大家!

【问题讨论】:

  • 是否有可能ioctl syscall成功返回,之后代码出现segfaulting?
  • 否,因为 ioctl 系统调用是 mainreturn 之前的最后一条指令。如果我评论 ioctl 该过程不会出现段错误。
  • 为什么隐藏了使用struct page地址的部分?你确定你的段错误不是来自这里吗?你试过在 qemu 上调试这个吗?
  • 在调用walk-page_table 之后,如果pageNULL,我只执行printk。我还尝试只保留对walk_page_table 的调用,但该过程仍然存在段错误。也许是的,发现问题的最快方法是调试。谢谢昆汀。
  • 通过调试编译代码并在转储期间强制进行堆栈跟踪,以便您绝对知道发生了什么。或者使用kgdb。此外,您确定您没有使用最新内核的新 unlocked_ioctl 功能吗?

标签: linux linux-kernel kernel


【解决方案1】:
pte_unmap(ptep); 

在标签出来之前丢失。试试这样改代码:

    ...
    page = pte_page(pte);
    if (page)
        printk(KERN_INFO "page frame struct is @ %p", page);

    pte_unmap(ptep); 

out:

【讨论】:

  • 谢谢。我确信内核是在没有 CONFIG_HIGHPTE 的情况下编译的,而是设置了定义,所以pte_offset_map 做了一个kmap
  • 谢谢!我不断收到诸如“ 因抢占不平衡而返回”之类的消息而崩溃。最后追踪 preempt_count() 增量到 pte_offset_map() !添加 pte_unmap 减少了它,一切都很好。
【解决方案2】:

查看/proc/<pid>/smaps文件系统,可以看到用户空间内存:

cat smaps 
bfa60000-bfa81000 rw-p 00000000 00:00 0          [stack]
Size:                136 kB
Rss:                  44 kB

它的打印方式是通过fs/proc/task_mmu.c(来自内核源代码):

http://lxr.linux.no/linux+v3.0.4/fs/proc/task_mmu.c

   if (vma->vm_mm && !is_vm_hugetlb_page(vma))
               walk_page_range(vma->vm_start, vma->vm_end, &smaps_walk);
               show_map_vma(m, vma.....);
        seq_printf(m,
                   "Size:           %8lu kB\n"
                   "Rss:            %8lu kB\n"
                   "Pss:            %8lu kB\n"

而且你的函数有点像walk_page_range()。查看 walk_page_range() 你可以看到 smaps_walk 结构在行走时不应该改变:

http://lxr.linux.no/linux+v3.0.4/mm/pagewalk.c#L153

For eg:

                }
 201                if (walk->pgd_entry)
 202                        err = walk->pgd_entry(pgd, addr, next, walk);
 203                if (!err &&
 204                    (walk->pud_entry || walk->pmd_entry || walk->pte_entry

如果内存内容发生变化,那么上述所有检查可能会不一致。

所有这些只是意味着你必须在遍历页表时锁定 mmap_sem:

   if (!down_read_trylock(&mm->mmap_sem)) {
            /*
             * Activate page so shrink_inactive_list is unlikely to unmap
             * its ptes while lock is dropped, so swapoff can make progress.
             */
            activate_page(page);
            unlock_page(page);
            down_read(&mm->mmap_sem);
            lock_page(page);
    }

然后接着解锁:

up_read(&mm->mmap_sem);

当然,当您在内核模块中发出 pagetable 的 printk() 时,内核模块正在您的 insmod 进程的进程上下文中运行(只需 printk “comm”,您就可以看到“insmod”)的含义mmap_sem 是锁定的,这也意味着进程没有运行,因此在进程完成之前没有控制台输出(所有 printk() 输出只进入内存)。

听起来合乎逻辑?

【讨论】:

  • 谢谢彼得,我试图在读取页表但不起作用的第一条指令之前持有mmap_sem...同样的分段错误错误。但是,当我调用walk_page_table 时,我不在进程insmod 的上下文中:我在my_ioctl 内部调用它,所以我在调用ioctl 系统调用的进程上下文中。这会有所作为吗?
  • 是的,它有所作为。因为不同的进程有不同的每个进程的页表——如果你走的是非内核部分。但是当涉及到内核地址时,所有进程共享相同的页表。
猜你喜欢
  • 2018-03-28
  • 1970-01-01
  • 1970-01-01
  • 2018-04-22
  • 1970-01-01
  • 1970-01-01
  • 2015-08-16
  • 2014-01-19
  • 1970-01-01
相关资源
最近更新 更多