华南理工大学《操作系统》大作业报告题目:高级操作系统与分布式系统大作业学院计算机科学与工程专业计算机科学与技术(全英创新班)学生姓名xxxxxx学生学号2012********联系方式_15626243309(Tel)/651476895(QQ)_指导教师吴一民课程编号S0812011课程学分2分起始日期2016年1月11日实验概述【实验目的及要求】内核版本要求:Linux-2.6.18实验任务:1.修改system_call(),使内核能够记录每一个系统调用被使用的次数。同时,为了使应用程序能够查询到这些数据,本实验要求实现两个系统调用,一个供应用程序来查询某个特定系统调用被使用的次数,另一个系统调用将系统调用计数清零。编制一个用户态程序调用你所增加的这两个系统调用,统计在一段时间内各系统调用被调用的次数。2.修改系统的缺页异常处理程序使之能够记录系统缺页次数和当前进程的缺页次数。同样,本实验也要求实现两个系统调用,一个供应用程序查询缺页次数,另一个系统调用将缺页计数清零。编制一个用户态程序你所增加的这两个系统调用,统计在一段时间内你的进程缺页的次数。【实验环境】操作系统:Centos-5.8-i386(内核版本2.6.18-308)编译的内核:2.6.18虚拟机:VMwareWorkstation12.1.0.2487【虚拟机账号密码】★★★★账号:root密码:aaasss实验内容【实验原理】linux系统调用是linux在其内核里都有一些内建的函数,这些函数可以用来完成一些系统级别的功能。这些函数代表了从用户空间到内核空间的一种转换,例如在用户空间调用open函数,则会在内核空间调用sys_open。Linux中每个系统调用都有相应的系统调用号,这样,通过这个独一无二的号就可以关联系统调用。当用户空间的进程执行一个系统调用的时候,这个系统调用号就被用来指明到底是要执行哪个系统调用。进程不会提及系统调用的名称。在这次试验采用的linux-2.6.18内核中共有317个系统调用,他们储存在sys_call_table的数据结构当中,这个数据结构在此内核中的entry.S中定义。sys_call_table是一张由指向实现各种系统调用的内核函数的函数指针组成的表。在用户空间无法直接调用系统调用来执行内核代码,所以需要通过软中断的方式来实现系统调用。通过int$0x80指令产生系统软中断,触发异常使得系统切换到内核态执行128号异常处理程序,调用system_call()函数。但是仅仅陷入内核空间是不够的,还需要通过eax寄存器把相应的系统调用号传递给内核。在陷人内核之前,用户空间就把相应系统调用所对应的号放入eax中了。这样系统调用处理程序一旦运行,就可以从eax中得到数据。system_call()函数通过将给定的系统调用号与NR_syscalls做比较来检查其有效性。如果它大于或者等于NRsyscalls,该函数就返回一ENOSYS。否则,就执行相应的系统调用。由于系统调用表中的表项是以32位(4字节)类型存放的,所以内核需要将给定的系统调用号乘以4,然后用所得的结果在该表中查询其位置。Linux系统本身并未有记录系统调用次数的数据结构,因此我们需要在内核中定义一个全局数组来记录每个系统调用被执行的次数。从调用的原理上看,要记录每个系统调用被执行的次数,我们可以在system_call()参数检查成功后,对相应的系统调用次数做一次增量操作,这样就完成了执行次数记录的工作。为了能得到该数组信息和对该数组进行操作,我们还需要定义自己的系统调用。我们定义的系统调用可以相应地在entry.S中的sys_call_table添加,并重新编译内核,使用该函数。实验过程:1.安装vmwareworkstation,并在其上安装CentOS5.8。2.安装vmware-tools,设置共享文件夹。并把内核文件linux-2.6.18.tar.xz下载至共享文件夹当中(/mnt/hgfs/sharedFiles)。3.登录root,把内核文件压缩包解压至/usr/src目录下:#tar-xvflinux-3.13.2.tar.xz-C/usr/src/实验一内核安装4.安装gcc和ncurses包:yum-yinstallgccyuminstallncursesncurses-deve5.进行内核配置:makemenuconfig6.进入usr/src/linux-2.6.18/目录编译内核:cdusr/src/linux-2.6.18/makemakemodules_install7.安装内核:makeinstall8.内核版本验证:vim/boot/grub/grub.conf查看内核版本列表,可以看到2.6.18内核已安装,修改default=0,默认启动新安装的内核。9.重启,查看内核版本:uname–a内核从2.6.18-308变为2.6.18,内核安装成功。1.创建一个全局数组记录系统调用次数在/usr/src/linux-2.6.18/kernel/sys.c中添加一个callCount数组记录系统调用次数vim/usr/src/linux-2.6.18/kernel/sys.clongcallCount[NR_syscalls];EXPORT_SYMBOL(callCount);其中NR_syacalls是sys.c中的一个宏,记录了系统调用的数量。2.修改system_call(),使其能对每个系统调用进行计数在/usr/src/linux-2.6.18/arch/i386/kernel/entry.S的syscall_call:和call*sys_call_table(,%eax,4)之间插入语句:vim/usr/src/linux-2.6.18/arch/i386/kernel/entry.SinclcallCount(,%eax,4)inclcallCount(,%eax,4)这条会变代码的含义是:incl是对后面的地址的内容进行自增操作,也就是对callCount(,%eax,4)这个元素的内容+1。其中eax是用户空间传递到eax寄存器中的系统调用号。,%eax,4的含义是eax寄存器里面的值乘以4,由于系统调用表中的表项是以32位(4字节)类型存放的,所以内核需要将给定的系统调用号乘以4,所以是对callCount第%eax*4个元素进行增量操作。3.在系统调用表中增加表项在/usr/src/linux-2.6.18/arch/i386/kernel/syscall_table.S尾部添加三个系统调用的表项:.longsys_allSysCall.longsys_spcSysCall.longsys_clrSysCall记录系统调用次数翻到syscall_table.S的头部可以看到下图内容,这个文件存储的是系统调用表,实际上就是sys_call_table这个数组,共有NR_syscalls个表项。语句前面的点号’’.’指的是当前的地址,sys_call_table是数组的首地址,调用表中的第321个表项sys_clrSysCall指代系统调用号为320的clcSysCall()服务例程地址。这个表项的作用就是用来通过系统调用号索引内核服务例程。4.为三个新增的系统调用添加系统调用号的宏在/usr/src/linux-2.6.18/include/asm/unistd.h中添加三个系统调用的调用号:#define__NR_allSysCall318#define__NR_spcSysCall319#define__NR_clrSysCall320把__NR_syscalls这个记录系统调用总数的宏增加3#define__NR_syscalls3215.在头文件中声明三个系统调用在/usr/src/linux-2.6.18/include/linux/syscalls.h的尾部添加:asmlinkageintsys_allSysCall(void);asmlinkageintsys_spcSysCall(intsys_callNum);asmlinkageintsys_clrSysCall(void);syscall.h这个头文件适用于实现系统调用表中指向的服务例程,在本实验中,我定义了三个函数:(1)allSysCall:接受0个参数,用于显示所有系统调用被执行的次数。(2)spcSysCall:接受1个参数,查询的系统调用号作为参数,用于显示某个特定的系统调用被执行的次数,返回值为该系统调用被调用的次数。参数不合法时返回-1。(3)clrSysCall:接受0个参数,用于把系统调用次数数组清零。6.为三个系统调用增加具体实现在/usr/src/linux-2.6.18/kernel/sys.c中实现这三个函数:asmlinkageintsys_allSysCall(void){inti=0;printk(Timesofsystemcalls\n);for(;iNR_syscalls;i++)printk(Systemcall[%d]wascalled%ldtimes.\n,i,callCount[i]);return0;}asmlinkageintsys_spcSysCall(intcallNum){if(callNumNR_syscalls&&callNum=0){printk(Systemcall--%dhasbeencall%ldtime(s).\n,callNum,mycount[callNum]);returncallCount[callNum];}else{printk(Inviliadsystemcallnumber.\n);return-1;}}asmlinkageintsys_clrSysCall(void){inti=0;for(;iNR_syscalls;i++)callCount[i]=0;printk(Timesofsystemcallsettedto0.\n);return0;}7.重新编译内核8.编写测试函数(1)测试allSysCall():test_allSysCall.c#includelinux/unistd.h#includestdlib.h#includestdio.h#includeerrno.h#definetest_allSysCall318intmain(){syscall(test_allSysCall);return0;}测试结果:所有系统调用的次数都已被列出(这里只显示前几个),函数正常工作。(2)测试spcSysCall():test_spcSysCall.c#includelinux/unistd.h#includestdlib.h#includestdio.h#includeerrno.h#definetest_spcSysCall319intmain(){intsysNum;scanf(%d,&sysNum);syscall(test_spcSysCall,sysNum);return0;}测试结果:第200个系统调用被调用3685次,函数正常工作。(3)测试spcSysCall():test_clrSysCall.c#includelinux/unistd.h#includestdlib.h#includestdio.h#includeerrno.h#definetest_allSysCall318#definetest_clrSysCall320intmain(){syscall(test_clrSysCall);syscall(test_allSysCall);return0;}测试结果:所有系统调用执行次数都被清零,除了部分函数立即得到执行,计数为1,函数正常工作。1.进入虚拟机,通过账号root,密码aaasss登录,进入目录/home/testSCcd/ho