本人成功的环境是在Ubuntu12下将内核换成linux-3.2.58。
一、实验目的
学习虚拟内存的基本原理和Linux虚拟内存管理技术;
深入理解、掌握Linux的按需调页过程;
掌握内核模块的概念和操作方法,和向/proc文件系统中增加文件的方法;
综合运用内存管理、系统调用、proc文件系统、内核编译的知识。
二、实验内容
1.原理
Linux的虚拟内存技术采用按需调页,当CPU请求一个不在内存中的页面时,会发生缺页,缺页被定义为一种异常(缺页异常),会触发缺页中断处理流程。每种CPU结构都提供一个do_page_fault处理缺页中断。由于每发生一次缺页都要进入缺页中断服务函数do_page_fault一次,所以可以认为执行该函数的次数就是系统发生缺页的次数。因此可以定义一个全局变量pfcount 作为计数变量,在执行do_page_fault时,该变量值加1。本实验通过动态加载模块的方法,利用/proc文件系统作为中介来获取该值。
2.实验环境
操作系统:Ubuntu 12.04(内核版本为3.2.0-23-generic-pae)
内核源码:linux-3.2.58
实验指导
- 下载一份内核源代码并解压
Linux受GNU通用公共许可证(GPL)保护,其内核源代码是完全开放的。现在很多Linux的网站都提供内核代码的下载。推荐使用Linux的官方网站:http://www.kernel.org。
在terminal下可以通过wget命令下载源代码:
$ cd /tmp
$ wget http://www.kernel.org/pub/linux/kernel/v3.x/linux-3.2.58.tar.xz
切换到root身份,解压源代码到/usr/src目录下:
# xz –d linux-3.2.58.tar.xz
# tar –xvf linux-3.2.58.tar –C /usr/src
2. 修改内核源代码,添加统计变量
1、切换到预编译内核目录
#cd /usr/src/linux-3.2.58
2、修改处理内存访问异常的代码
//用gedit 编辑器打开fault.c,一般使用Intel x86体系结构,则修改arch/x86/目录下的文件
#gedit arch/x86/mm/fault.c
//在do_page_fault函数的上一行定义统计缺页次数的全局变量pfcount
unsigned long volatile pfcount;
//将pfcount加入到do_page_fault中,用以统计缺页次数
pfcount++;
3、修改内存管理代码
//用gedit 编辑器打开头文件mm.h
#gedit include/linux/mm.h
//在mm.h中加入全局变量pfcount的声明,代码加在extern int page_cluster;语句之后
extern unsigned long volatile pfcount;
4、导出pfcount全局变量,让整个内核(包括模块)都可以访问。方法是:
#gedit kernel/kallsyms.c
//在文件最后加入一行代码
EXPORT_SYMBOL(pfcount);
- 配置编译新内核
用编译Linux内核预备实验中的方法完成新内核的配置、编译、替换,重启后验证是否完成替换。
#uname –r //如果为3.2.58(与你采用的新内核版本一至)说明替换完成
步骤:
1.安装需要的环境
apt-get install kernel-package build-essential libncurses5-dev fakeroot
2.使用make menuconfig 生成的内核配置文件,决定将内核的各个功能系统编译进内核还是编译为模块还是不编译。
(可以什么都不加,直接exit退出,yes保存。)
3.配置内核:
建议使用当前系统配置文件,使用当前系统配置文件方法:
#cp /boot/config-3.2.0-23-generic ./.config,然后进入menuconfig选择load选项。
4. 编译内核:
make,这可能需要2~3个小时的时间。
make完成如图:
5.加入模块
#make modules_install
6.安装内核:
#make install
即复制.config,vmlinuz,initrd.img,System.map文件到/boot目录、更新grub。对于RedHat系统以下三个grub文件自动会更新,默认启动新内核。
/etc/grub.conf
/boot/grub/menu.lst
/boot/grub/grub.conf生成可执行的内核引导文件:
7.重启系统:
#reboot
可以看到内核已经换成功了。
4. 编写读取pfcount值的模块代码
系统重启后,执行如下操作:
#mkdir source //在当前用户目录下创建source文件夹,用于存放编写的用户程序
#cd source //切换到source目录
#vi pf.c //新建用于构建模块的代码
--------------------------------------------
/*pf.c*/
/*modules program*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <asm/uaccess.h>
struct proc_dir_entry *proc_pf;
struct proc_dir_entry *proc_pfcount;
extern unsigned long volatile pfcount;
static inline struct proc_dir_entry *proc_pf_create(const char* name, mode_t mode, read_proc_t * get_info)
{
return create_proc_read_entry(name,mode,proc_pf,get_info,NULL);
}
int get_pfcount(char *buffer, char **start, off_t offset, int length, int *peof, void *data)
{
int len = 0;
len = sprintf(buffer, "%ld \n", pfcount);
return len;
}
static int pf_init(void)
{
proc_pf = proc_mkdir("pf", 0);
proc_pf_create("pfcount", 0, get_pfcount);
return 0;
}
static void pf_exit(void)
{
remove_proc_entry("pfcount",proc_pf);
remove_proc_entry("pf",0);
}
module_init(pf_init);
module_exit(pf_exit);
MODULE_LICENSE("GPL");
- 编译、构建内核模块
#vi Makefile //在source目录下建立Makefile文件
在Makefile中添加如下内容:
ifneq ($(KERNELRELEASE),)
obj-m:=pf.o
else
KDIR:= /lib/modules/$(shell uname -r)/build
PWD:= $(shell pwd)
default:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
endif
make
- 加载模块到内核中
执行加载模块命令:
#insmod pf.ko
查看统计缺页次数:
#cat /proc/pf/pfcount
问题:出现错误
解决:换成了ubuntu12解决。
问题:类型冲突
解决:返现实验指导书上的pfcount类型和之前的内核里面的不一致,改过来解决。
- 实验心得
。。。。。。。。。。
参考链接:https://blog.csdn.net/lqglqglqg/article/details/43229669