Таблицы пешеходных страниц процесса в 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 и таблицами страниц трех уровней.
Спасибо вам всем!
2 ответа
pte_unmap(ptep);
отсутствует непосредственно перед лейблом. Попробуйте изменить код следующим образом:
...
page = pte_page(pte);
if (page)
printk(KERN_INFO "page frame struct is @ %p", page);
pte_unmap(ptep);
out:
Смотреть на /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() для page table внутри вашего модуля ядра, модуль ядра запускается в контексте процесса вашего insmod-процесса (просто выведите "comm", и вы увидите "insmod"), означающее, что mmap_sem блокировка, это также означает, что процесс НЕ запущен, и, следовательно, нет вывода на консоль до завершения процесса (все выходные данные printk() отправляются только в память).
Звучит логично?