Подтвердить что ты не робот

Прогулки таблиц страниц процесса в Linux

Я пытаюсь перемещаться по таблицам страниц для процесса в 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 в пользовательском пространстве процесса, это segfaults... но кажется, что способ, которым я ищу запись в таблице страниц, является правильным, потому что с 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 и тремя страницами таблиц уровней.

Спасибо всем!

4b9b3361

Ответ 1

pte_unmap(ptep); 

отсутствует перед ярлыком. Попробуйте изменить код следующим образом:

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

    pte_unmap(ptep); 

out:

Ответ 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);

И, конечно же, когда вы выдаете printk() из pagetable внутри вашего модуля ядра, модуль ядра работает в контексте процесса вашего процесса insmod (просто printk "comm", и вы можете увидеть "insmod" ), что означает mmap_sem - это блокировка, это также означает, что процесс НЕ работает, и, следовательно, до завершения процесса нет выхода консоли (все вывод printk() поступает только в память).

Звучит логично?