私はLinuxカーネルについて学びようとしており、プロセスのページテーブルからページを読むために4.15.0+ Linuxカーネルで2つのシステムコールを実装する必要がある練習を見つけました。目的は、最初のシステム呼び出しがページ数を数え、2番目のシステム呼び出しがメモリに存在し、読み取り専用モードのすべてのページを1として表すビットマップを取得することです。
オンラインでいくつかの調査を行った後の結論は次のとおりです。
struct task_struct {
....
struct list_head pg_list;
unsigned long count;
}
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/sched/signal.h>
#include <linux/string.h>
#include <linux/mm.h>
struct page_list{
size_t num_page;
struct list_head list;
};
asmlinkage long sys_get(const char *proc_name const size_t name_len){
char p_name[name_len+1];
struct task_struct *task;
struct vm_area_struct *vma = NULL;
unsigned long vpage;
unsigned long count = 0;
if (copy_from_user(p_name, proc_name, name_len)) return -EFAULT;
p_name[name_len] = '\0';
for_each_process(task){
if(strcmp(task->comm, p_name) == 0) {
INIT_LIST_HEAD(&task->pg_list);
if (task->mm && task->mm->mmap) {
for (vma = task->mm->mmap; vma; vma = vma->vm_next) {
for (vpage = vma->vm_start; vpage < vma->vm_end; vpage += PAGE_SIZE) {
pgd_t *pgd = pgd_offset(task->mm, vpage);
if (pgd_none(*pgd) || pgd_bad(*pgd)) continue;
p4d_t *p4d = p4d_offset(pgd, vpage);
if (p4d_none(*p4d) || p4d_bad(*p4d)) continue;
pud_t *pud = pud_offset(p4d, vpage);
if (pud_none(*pud) || pud_bad(*pud)) continue;
pmd_t *pmd = pmd_offset(pud, vpage);
if (pmd_none(*pmd) || pmd_bad(*pmd)) continue;
pte_t *pte = pte_offset_map(pmd, vpage);
if (pte_present(*pte) && !pte_write(*pte)) {
struct page_list *el = kmalloc(sizeof(*el), GFP_KERNEL);
if (!el) return -ENOMEM;
el->num_page = count;
list_add(&el->list, &our_task->pg_list);
}
count++;
}
}
}
}
}
return count;
}
asmlinkage long sys_get(char *bitmap, const char *proc_name, const size_t name_len){
struct list_head *i;
struct list_head *n;
struct page_list *el;
struct task_struct *task;
char p_name[name_len+1];
if (copy_from_user(p_name, proc_name, name_len)) return -EFAULT;
p_name[name_len] = '\0';
if (bitmap == NULL) {
return -ENOENT;
}
for_each_process(task){
if(strcmp(task->comm, p_name) == 0){
list_for_each_safe(i, n, &task->pg_list){
el = list_entry(i ,struct page_list, list);
bitmap[el->num_page] = 1;
list_del(i);
kfree(el);
}
kfree(&task->pg_list);
}
}
return 0;
}
これは期待どおりに機能せず、時には私がよく理解できない理由でカーネルパニックを引き起こします。カーネルコードで作業するのは今回が初めてです。私が持っているいくつかの具体的な質問は次のとおりです。プロセスに対応するすべてのページを繰り返す効率的な方法ですか?読み取り専用現在のページを見つけるために使用するマクロは正しいですか(x86 - 32アーチのみ)?私の結果をchar * bitmapに書き込む正しい方法は何ですか?結果はユーザー空間から来ており、アクセスが間違っているためです。最後に、カーネルパニックは私がメモリを処理する方法で発生しますが、私が間違っているかどうかはわかりません。どんなフィードバックでも大変感謝します!