华南理工大学《操作系统》大作业报告
题目:高级操作系统与分布式系统大作业
学院计算机科学与工程
专业计算机科学与技术(全英创新班)
学生姓名 xxxxxx
学生学号 2012********
联系方式 _156********(Tel)/651476895(QQ)_
指导教师吴一民
课程编号S0812011
课程学分2 分
起始日期 2016年1月11日
实验过程:
内核安装
1.安装vmware workstation,并在其上安装CentOS5.8。
2.安装vmware-tools,设置共享文件夹。并把内核文件linux-2.6.18.tar.xz下载至共享文
件夹当中(/mnt/hgfs/sharedFiles)。
3.登录root,把内核文件压缩包解压至/usr/src目录下:
# tar -xvf linux-3.13.2.tar.xz -C /usr/src/
4.安装gcc和ncurses包:
yum -y install gcc
yum install ncurses ncurses-deve
5.进行内核配置:
make menuconfig
6.进入usr/src/linux-2.6.18/目录编译内核:
cd usr/src/linux-2.6.18/
make
make modules_install
7.安装内核:
make install
8.内核版本验证:
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.c
long callCount [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.S
incl callCount(,%eax,4)
incl callCount(,%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尾部添加三个系统调用的表项:.long sys_allSysCall
.long sys_spcSysCall
.long sys_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_allSysCall 318
#define __NR_spcSysCall 319
#define __NR_clrSysCall 320
把__NR_syscalls这个记录系统调用总数的宏增加3
#define __NR_syscalls 321
5.在头文件中声明三个系统调用
在/usr/src/linux-2.6.18/include/linux/syscalls.h的尾部添加:
asmlinkage int sys_allSysCall(void);
asmlinkage int sys_spcSysCall(int sys_callNum);
asmlinkage int sys_clrSysCall(void);
syscall.h这个头文件适用于实现系统调用表中指向的服务例程,在本实验中,我定义了三个函数:
(1)allSysCall:接受0个参数,用于显示所有系统调用被执行的次数。
(2)spcSysCall: 接受1个参数,查询的系统调用号作为参数,用于显示某个特定
的系统调用被执行的次数,返回值为该系统调用被调用的次数。参数不合法
时返回-1。
(3)clrSysCall:接受0个参数,用于把系统调用次数数组清零。
6.为三个系统调用增加具体实现
在/usr/src/linux-2.6.18/kernel/sys.c中实现这三个函数:
asmlinkage int sys_allSysCall(void){
int i=0;
printk("Times of system calls\n");
for(;i printk("System call [%d] was called %ld times. \n",i,callCount[i]); return 0;} asmlinkage int sys_spcSysCall(int callNum){ if(callNum< NR_syscalls && callNum >= 0){ printk("System call --%d has been call %ld time(s). \n",callNum,mycount[callNum]); return callCount[callNum];} else{ printk("Inviliad system call number.\n"); return -1;}} asmlinkage int sys_clrSysCall(void){ int i=0; for (;i < NR_syscalls;i++) callCount[i]=0; printk("Times of system call setted to 0.\n"); return 0;} 7.重新编译内核 8.编写测试函数 (1)测试allSysCall(): test_allSysCall.c #include #include #include #include #define test_allSysCall 318 int main() { syscall(test_allSysCall); return 0; } 测试结果: 所有系统调用的次数都已被列出(这里只显示前几个),函数正常工作。 (2)测试spcSysCall(): test_spcSysCall.c #include #include #include #include #define test_spcSysCall 319 int main() { int sysNum; scanf("%d",&sysNum); syscall(test_spcSysCall,sysNum); return 0; } 测试结果: 第200个系统调用被调用3685次,函数正常工作。 (3)测试spcSysCall(): test_clrSysCall.c #include #include #include #include #define test_allSysCall 318 #define test_clrSysCall 320 int main() { syscall(test_clrSysCall); syscall(test_allSysCall); return 0; } 测试结果: 所有系统调用执行次数都被清零,除了部分函数立即得到执行,计数为1,函 数正常工作。 如何验证我的代码 1.进入虚拟机,通过账号root,密码aaasss登录,进入目录/home/testSC cd /home/testSC 2.验证allSysCall()函数(列出所有系统调用执行次数),执行如下命令: gcc -o result_allSysCall test_allSysCall.c (执行test_allSysCall.c代码,生成可执行文件result_allSysCall) ./result_allSysCall (运行代码查看执行结果,在这里能看到所有系统调用执行次数) 3.验证clrSysCall()函数(清零所有系统调用执行次数),执行如下命令: gcc–o result_clrSysCall test_clrSysCall.c (执行test_clrSysCall.c代码,生成可执行文件result_clrSysCal中) ./result_clrSysCall (运行代码查看执行结果,这里能清零所有系统调用执行次数,并查看清零结果) 实验过程: 缺页查询、清零 1.在进程的task_struct中新增一个计数变量,用于记录进程发生缺页的次数 在/usr/src/linux-2.6.18/include/linux/sched.h中添加一个faultTimes成员 vim /usr/src/linux-2.6.18/include/linux/sched.h long faultTimes; 2.进程初始化时,为faultTimes清零 在/usr/src/linux-2.6.18/include/linux/init_task.h的进程初始化#define INIT_TASK(tsk)宏中使faultTimes清零: vim /usr/src/linux-2.6.18/include/linux/init_task.h .faultTimes =0, \ 3.复制进程时,为faultTimes清零 在/usr/src/linux-2.6.18/kernel/fork.c的copy_process()函数为进程复制调用的函数,在该函数返回之前,为faultTimes清零: vim /usr/src/linux-2.6.18/kernel/fork.c p-> faultTimes = 0; 4.定义一个全局变量用来记录总的缺页次数,并为该变量加自旋锁 在/usr/src/linux-2.6.18/arch/i386/mm/fault.c中增加一个totalFault变量记录总的缺页次数。因为内核抢占等原因会造成异步执行,所以要对该变量加上自旋锁pageLock:vim /usr/src/linux-2.6.18/arch/i386/mm/fault.c unsigned long totalFault=0; spinlock_t pageLock=SPIN_LOCK_UNLOCKED; unsigned long lockFlag; EXPORT_SYMBOL(totalFault); EXPORT_SYMBOL(pageLock); EXPORT_SYMBOL(lockFlag); 5.实现缺页计数器自增操作 每次发生缺页时,会调用/usr/src/linux-2.6.18/arch/i386/mm/fault.c中的do_page_fault()函数,在该函数tsk = current之后完成自增操作,同时注意使用自旋锁保护变量:vim /usr/src/linux-2.6.18/arch/i386/mm/fault.c spin_lock_irqsave(&pageLock,lockFlag); totalFault++; spin_unlock_irqrestore(&pageLock,lockFlag); spin_lock_irqsave(&pageLock,lockFlag); tsk->faultTimes++; spin_unlock_irqrestore(&pageLock,lockFlag); 6.在系统调用表中增加表项 在/usr/src/linux-2.6.18/arch/i386/kernel/syscall_table.S尾部添加三个系统调用的表项:.long sys_allFault .long sys_spcFault .long sys_clrFault 翻到syscall_table.S的头部可以看到下图内容,这个文件存储的是系统调用表,实际上就是sys_call_table这个数组,共有NR_syscalls个表项。语句前面的点号’’.’指的是当前的地址,sys_call_table是数组的首地址,调用表中的第323个表项sys_clrFault() 指代系统调用号为322的clcFault ()服务例程地址。这个表项的作用就是用来通过系统调用号索引内核服务例程。 7.为三个新增的系统调用添加系统调用号的宏 在/usr/src/linux-2.6.18/include/asm/unistd.h中添加三个系统调用的调用号: #define __NR_allFault 321 #define __NR_spcFault 322 #define __NR_clrFault 323 把__NR_syscalls这个记录系统调用总数的宏增加3 #define __NR_syscalls 324 8.在头文件中声明三个系统调用 在/usr/src/linux-2.6.18/include/linux/syscalls.h的尾部添加: asmlinkage int sys_allFault(void); asmlinkage int sys_spcFault(int pid); asmlinkage int sys_clrFault(void); syscall.h这个头文件适用于实现系统调用表中指向的服务例程,在本实验中,我定义了三个函数: (1)allFault:接受0个参数,用于显示所有进程的缺页次数和总的缺页数。 (2)spcFault: 接受1个参数,查询的进程PID作为参数,用于显示某个特定的进 程缺页的次数,返回值为该进程缺页的次数。参数不合法时返回-1。 (3)clrFault:接受0个参数,用于把进程缺页计数和总缺页数清零。 9.在实现系统调用的c文件中引用缺页计数器和自旋锁的定义 在/usr/src/linux-2.6.18/kernel/sys.c中引用在fault.c中定义的totalFault,pageLock和lockFlag变量,否则不能访问这几个变量: extern unsigned long totalFault; extern spinlock_t pageLock; extern unsigned long lockFlag; 10.为三个系统调用增加具体实现 在/usr/src/linux-2.6.18/kernel/sys.c中实现这三个函数: v im /usr/src/linux-2.6.18/kernel/sys.c asmlinkage int sys_allFault(void){ struct task_struct *pfTask; printk("Total faults: [%ld] times\n",totalFault); for_each_process(pfTask) printk("Task %d : (%ld) times for page fault.\n",pfTask->pid,pfTask->faultTimes); return 0; } asmlinkage int sys_spcFault(int pid){ struct task_struct *pfTask; for_each_process(pfTask){ if(pfTask->pid==pid){ printk("Task %d : (%ld) times for page fault.\n",pfTask->pid,pfTask->faultTimes); return pfTask->faultTimes;} } return -1; } asmlinkage int sys_clrFault(void){ struct task_struct *pfTask; spin_lock_irqsave(&pageLock,lockFlag); totalFault=0; for_each_process(pfTask) pfTask->faultTimes=0; spin_unlock_irqrestore(&pageLock,lockFlag); printk("Fault times cleared. \n"); return 0;} 11.重新编译内核 12.编写测试函数 (1)测试allFault(): test_allFault.c #include #include #include #include #define sys_allFault 321 int main() { syscall(sys_allFault); return 0; } 测试结果: 所有进程的缺页次数、总的缺页次数都已被列出(这里只显示部分几个),函 数正常工作。 (2)测试spcFault(): test_spcFault.c #include #include #include #include #define sys_spcFault 322 int main() { int tskNum; scanf("%d",&tskNum); syscall(sys_spcFault,tskNum); return 0;} 测试结果: PID为的进程缺页次,函数正常工作。 (3)测试clrFault(): test_clrSysCall.c #include #include #include #include #define sys_allFault 321 #define sys_clrFault 323 int main() { printf("------Fault times before clear------\n"); syscall(sys_allFault); printf("------Clearing fault times------\n"); syscall(sys_clrFault); printf("------Fault times after clear------\n"); syscall(sys_allFault); return 0; } 测试结果: 总的缺页次数清零,进程的缺页清零,函数正常工作。