学习重建Linux内核。
学习Linux内核的系统调用,理解、掌握Linux系统调用的实现框架、用户界面、参数传递、进入/返回过程。阅读Linux内核源代码,通过添加一个简单的系统调用实验,进一步理解Linux操作系统处理系统调用的统一流程。了解Linux操作系统缺页处理,进一步掌握task_struct结构的作用。
在现有的系统中添加一个不用传递参数的系统调用。这个系统调用的功能是实现统计操作系统缺页总次数、当前进程的缺页次数及每个进程的"脏"页面数,严格来说这里讲的"缺页次数"实际上是页错误次数,即调用do_page_fault函数的次数。实验主要内容:
实验报告
一、实验环境
二、实验内容和结果及分析
1、实验设计思路
本实验的前半部分按照老师的实验指导来就可以了,只要不发生误操作,就没有什么太大的问题。因此,这里主要对统计程序的设计思路做详细说明:
在实验指导中给出了pf和pfcount,所以实现缺页次数并不难,直接打印即可。那么怎么去统计脏页数呢?经过查阅资料发现task_struct中有一个数据成员叫做nr_dirtied,遍历所有进程,循环打印这个数据成员即可。
2、实验步骤及截图
3、测试程序运行结果截图
首先我们可以查看当前的内核版本:
再对比之前的内核版本:
说明成功吧4.10.0的内核换成了4.8.0。
然后去var/log/kern.log中查看程序输出结果:
可以看见,总的缺页次数是489725,当前进程的缺页次数是72,脏页数的部分截图如上(比较多,这里只截取了一部分)。
4、结果分析
5、源程序
首先是统计总的缺页次数、当前进程缺页次数以及各个进程脏页数的程序,这个程序比较特别的地方是要用task_struct中的nr_dirtied,如下所示:
/***********************************
*Project Name:实验2统计程序
*Author:
*Student ID:
*Last Modified: 2017/12.23
************************************/
externunsignedlong pfcount;//必须先申明其他地方定义的pfcount
asmlinkageint sys_mysyscall(void){
struct task_struct*p=NULL;//用来遍历进程
printk("<1>OSLab2 total page fault: %lu times\n",pfcount);//所有缺页次数
printk("<1>OSLab2 current process's page fault: %lu times\n",current->pf);//当前进程缺页次数
for(p=&init_task;(p=next_task(p))!=&init_task;){//循环打印各个进程脏页数
printk("<1>OSLab2 dirty page: %d\n pages",p->nr_dirtied);
}
return0;
}
然后是用户态程序这个程序比较简单,我没有输出到终端,而是直接/var/log/kern.log查看的,所以只需要调用223号系统调用即可:
/***********************************
*Project Name:实验2用户程序
*Author:
*Student ID:
*Last Modified: 2017/12.23
************************************/
#include
#include
#define __NR_ mysyscall 223
int main(){
syscall(223);
}
三、讨论、心得(20分)
相比之下这个实验比实验1简单了不少,但是比较麻烦,需要很小心地操作,因为如果中间出了什么问题,再编译一遍内核可不是闹着玩的。为了顺利做出这个实验,在实验过程中我每做一步都比较小心,但还是出了很多问题。最后成功完成这个实验是在一个周五的晚上,我熬到了凌晨4点钟,守着机器把内核编译完,然后又解决了"客户机禁用CPU"的错误,才做成功的,重启成功的那一刻我简直都不想睡觉了,很激动。
下面是做这个实验的一些收获:
(1)libncurses5-dev是用来干嘛的?
在make menuconfig的时候会出现终端下的字符菜单,这个软件包就是为此提供支持作用的。
(2)怎么在启动时加载编译好的内核?
因为我客户机的内核是4.10,但是编译的却是4.8,而Ubuntu每次重启默认会启动最新的内核,也就是说每次机器都会加载4.10,而不是我编译好的4.8,这个问题开始很困扰我,我在网上搜到的做法是去改启动项,改grub文件等等。这样导致了很多错误,但后来发现其实只需要在虚拟机启动的时候按住Esc键就可以到启动菜单里去了,然后就能选择需要加载的内核版本了。如下图:
(3)重启后出现"客户机禁用CPU"的情况怎么办?
内核加载正常,本以为大功告成,但是VMWare却告诉我"客户机禁用CPU",经过查资料,发现这主要是配置文件的问题,需要手动进行修改,打开真机上安装虚拟机时留下的vmx文件,在里面加入这句话:
再重启就正常了。
(4)pf初始化的正确位置。这个实验中tsk->pf=0;这句代码的位置十分关键,如果加入的位置不当,做出来的缺页次数会很离谱(比如当前进程缺页数会有好几万)。
正确的位置如下图所示:
这是因为arch_dup_task_struct(tsk,orig)函数的关系,如果看一下这个函数怎么实现的话就会知道:
它直接把orig赋给了tsk,所以如果pf在上面初始化,子进程的pf就还是父进程的,不满足要求。
(5)最后是一个小经验:由于这个实验失败率比较高,所以make的时候如果追求速度的话可以用make -j2或者make -j4(甚至更高的CPU核心数,如果机器性能允许的话),虽然这样做在makefile的依赖关系写得不够好的情况下可能有风险,但是linux内核的makefile肯定是写得比较完美的,所以为了顺利完成实验,尽情地用多核编译吧!
最后回答几个问题:
1.多次运行test程序,每次运行test后记录下系统缺页次数和当前进程缺页次数,给出这些数据。test程序打印的缺页次数是否就是操作系统原理上的缺页次数?有什么区别?
答:我运行了10次test程序,结果记录如下:
次数 |
系统缺页数 |
当前进程缺页数 |
1 |
462445 |
73 |
2 |
464264 |
74 |
3 |
505757 |
73 |
4 |
506182 |
73 |
5 |
506698 |
73 |
6 |
512899 |
73 |
7 |
513841 |
73 |
8 |
514260 |
73 |
9 |
517156 |
73 |
10 |
518152 |
73 |
可以看到,每运行一次,系统缺页次数就会增加,而当前进程缺页次数则(几乎)保持不变。
test打印的缺页数不是操作系统原理上的缺页数,区别在于:...
2.除了通过修改内核来添加一个系统调用外,还有其他的添加或修改一个系统调用的方法吗?如果有,请论述。
答:...
3.对于一个操作系统而言,你认为修改系统调用的方法安全吗?请发表你的观点。
答:...