我们试图在Linux 4.9.2下添加一个系统调用,这个系统调用会统计进程的缺页数与脏页数
下载并配置Linux内核源码
在Linux官网(https://www.kernel.org/)下载内核源码。我选择的是最新的稳定版4.8.12,下载压缩包linux-4.8.12.tar.xz
由于文件系统格式的问题,不能在共享文件夹中进行一下操作(共享文件夹在windows环境中建立,为FAT32格式),所以需要将压缩包复制到linux虚拟机内部的位置,我是用的是/Documents/OSworks
然后进入目录并解压文件
cd /Documents/OSworks
tar xf linux-4.8.12.tar.xz
cd linux-4.8.12
代码改写
要添加一个系统调用,首先要修改系统调用表,即syscall_64.tbl。在arch/x86/entry/syscalls/syscall_64.tbl
在记录的最后添加我们的系统调用条目,记住调用号为329
321 common bpf sys_bpf
322 64 execveat sys_execveat/ptregs
323 common userfaultfd sys_userfaultfd
324 common membarrier sys_membarrier
325 common mlock2 sys_mlock2
326 common copy_file_range sys_copy_file_range
327 64 preadv2 sys_preadv2
328 64 pwritev2 sys_pwritev2
329 common pagefaultInfo sys_pagefaultInfo
……………………
同时也要修改一下/include/linux/syscalls.h文件,在文件最后添加我们的函数声明:
asmlinkage long sys_pagefaultInfo(void);
为了实现缺页统计的功能,要先对原有的代码进行一些修改。修改的过程参照实验指导,包括:
在include/linux/mm.h中添加
extern ungsigned long pfconfig;
在include/linux/sched.h中添加:
unsigned long pf;
修改kernel/fork.c的dup_task_strcut函数:
static struct task_struct *dup_task__struct(struct task_struct *orig, int node)
{
………………
tsk->pf = 0;
………………
}
修改arch/x86/mm/fault.c。在这里要注意两句指令添加的地方,添加在if(major)的里面,则我们统计的是Major Faults(requiring I/O),并不实际引起硬件I/O的Minor Faults则不会被统计。
unsigned long pfcount;
………………
static noinline void __do_page_fault(struct pt_regs *regs, unsigned long error_code, unsigned long address)
{
………………
if(major){
pfcount++;
current->pf++;
………………
}
然后我们编写实际的系统调用代码。我们用一个额外的文件夹info存放我们的代码文件pagefaultInfo.h以及pagefaultInfo.c
mkdir info
cd info/
在pagefaultInfo.h中声明函数
asmlinkage long sys_pagefaultInfo(void);
在pagefaultInfo.c中定义函数
asmlinkage long sys_pagefaultInfo(void);
之后我们要编写寻找dirtypage数量的函数。这部分比较复杂,基本的思路是去遍历进程的页表,判断每个页表是否为firty,从而进行统计。
首先遍历所有进程(即task_struct),对每个task_struct,得到其mm_struct(表示进程地址空间的结构)。然后对每段(数据段、代码段等等),遍历其4级页表(pgd->pud->pmd->pte),然后对每个pte调用pte__ditry()函数判断是否为脏叶。
最后后要将我们的新代码加到makefile系统里面去,使得在makefile的时候能够编译并链接他们。具体做法是在info文件夹里新建文件Makefile,并添加以上信息:
obj-y:=pagefaultInfo.o
同时修改 kernel里的Makefile文件,在core-y后面添加我们的info/路径
core -y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/ info/
编译并装载内核
首先要配置内核。进入内核源代码根目录。考虑到可以配置的选项比较繁杂,可以直接拷贝当前系统中的配置文件。
cp /boot/config-$(uname -r) .config
make menuconfig
这时会进入一个带GUI的内核配置界面,使用默认配置即可。Save->Exit退出配置
之后可以正式开始编译内核(下一次重新编译可以从这一步开始)
fakeroot make-kpkg kernel_image
漫长的编译之后就可以将内核加载到boot列表里了:
fakeroot make-kpkg kernel_image
update-initramfs –c –k 4.8.12
update-grup2
然后重启系统。如果重启成功,就说明内核重编译基本成功了。
如果空间不够而且或者不准备再次重新编译内核的话,可以删除变异的中间文件
make clean