1、西安交通大学操作系统课内实验报告全解西安交通大学 实验报告 操作系统原理课内实验姓名:班级: 学号:实验一 用户接口实验一、 实验目的 1、理解并掌握面向操作命令的接口Shell,学会简单的shell编码。 2、理解操作系统调用的运行机制,掌握创建系统调用的方法。 二、 实验内容 1、 控制台命令接口实验理解面向操作命令的接口shell和进行简单的shell编程。 该实验是通过“几种操作系统的控制台命令”、“终端处理程序”、“命令解释程序”和“Linux 操作系统的 bash”来让实验者理解面向操作命令的接口 shell 和进行简单的 shell 编程。 查看 bash 版本。 编写 bash
2、 脚本,统计/my 目录下 c 语言文件的个数 2) 系统调用实验 。2、系统调用实验 理解操作系统调用的运行机制。 该实验是通过实验者对“Linux 操作系统的系统调用机制”的进一步了解来理解操作 系统调用的运行机制;同时通过“自己创建一个系统调用 mycall()”和“编程调用自己 创建的系统调用”进一步掌握创建和调用系统调用的方法。 编程调用一个系统调用 fork(),观察结果。 编程调用创建的系统调用 foo(),观察结果。 自己创建一个系统调用 mycall(),实现功能:显示字符串到屏幕上。 编程调用自己创建的系统调用。 三、实验准备 为了使用户通过操作系统完成各项管理任务,操作系
3、统必须为用户提供各种接口来实现 人机交互。经典的操作系统理论将操作系统的接口分为控制台命令和系统调用两种。前者主 要提供给计算机的操作人员对计算机进行各种控制;而后者则提供个程序员,使他们可以方 便地使用计算机的各种资源。四、 实验步骤及结果1、 控制台命令接口实验(1)查看 bash 版本操作:在 shell 提示符下输入:$echo $BASH_VERSION结果:版本是4.2.42(1)-release(2)建立 bash 脚本,输出 Hello word操作:在编辑器中输入以下内容#!/bin/bashecho Hello World!结果:操作:执行脚本 使用指令:$./text结果
4、:(3)编写 bash 脚本:统计/my 目录下 c 语言文件的个数通过 bash 脚本,可以有多种方式实现这个功能,而使用函数是其中个一个选择。在使用函数之前,必须先定义函数。 进入自己的工作目录,编写名为 count 的文件。脚本程序:#! /bin/bashfunction countecho n Number of matches for $1: #接收程序的第一个参数ls $1|wc l #对子程序的第一个参数所在的目录进行操作将 count 文件复制到当前目录下,然后在当前目录下建立文件夹,在 my 目录下建立几个 c 文件,以便用来进行测试。2、 添加系统调用 (1)编程调用一个
5、系统调用fork(),观察结果。源程序:# include int main() int iUid; iUid=fork(); if(iUid=0) for(;) printf(This is child process.n); sleep(1); if(iUid0) for(;) printf(This is parent process.n); sleep(1); if(iUid0) printf(Can not use system call.n); return 0; 实验结果:(2)操作:1. Linux-3.0.tar.bz2拷贝到/usr/src目录下命令:cp linux-3.
6、0.tar.bz2 /usr/src/ 2. 打开终端,获得root权限命令:sudo s 3. 进入/usr/src目录命令:cd /usr/src 4. 解压linux源码命令:tar xvzf linux-3.0.tar.bz2 5. 进入目录linux-3.0.5命令:cd linux-3.0 6. 添加系统调用:gedit kernel/myservice.c 在文本编辑器中添加 #include #include asmlinkage void sys_mycall() printk(KERN_INFO Hello, world!n); return; 7. 修改kernel/Ma
7、kefile添加生成myservice.c添加到Makefile的编译规则中: obj-y += myservice.o 8. .修改arch/x86/include/asm/unistd_32.h,添加以下内容: #define _NR_mycall SYS_ID /SYS_ID表示新添加系统调用的调用号 并修改文件中的NR_syscalls,将其值增加1 9. .修改arxh/x86/include/asm/syscalls.h添加以下内容: asmlinkage void sys_mycall(); 10. 修改arch/x86/kernel/syscall_table_32.S,添加以
8、下内容: .long sys_mycall 11. 配置内核(仅仅修改local versions即可)命令:make menuconfig 12. 编译内核命令:make j4 bzImage(开4个线程编译) 13. 编译内核模块命令:make j4 modules14. 安装内核模块命令:make modules_install15. 安装内核命令:make install 16. 重启系统,在系统选择页面选择进入自己编译的linux-3.0内核 17. 在桌面建立测试的C程序test.c程序内容如下: #include int main(int argc, char *argv) sy
9、scall(SYS_ID); / SYS_ID表示新添加系统调用的调用号 return 0; 18. 编译程序gcc test.c o a.out 19. 运行程序./a.out 20. 查看内核日志(printk的输出信息在内核日志中):dmesg结果:(1)编译内核成功截图:(2)编译模块成功截图:五、实验问题及分析在进行内核编译时,遇到的困难就是将缺少的程序补入,但因为本省 Ubantu 所带 的编辑器很不好用,在输入过程中就花费了非常大的时间。但最后经过学长的帮助顺利完成。 在编译期间有经历了一个多小时的时间,最后编译成功。六、实验心得通过本次实验,我了解并初步掌握了面向操作命令的接口
10、Shell,学会了简单的 shell 编码,理解了操作系统调用的运行机制,掌握了创建系统调用的方法。本次实验通过内核编译,将一组源代码变成操作系统的内核,并由此重新引导系统,这让我们初步了解了操作系统的生成过程。虽然在实验过程中遇到了不少问题,但最终在学长的帮助下,最终还是成功了。最后看见自己的实验结果心里还是挺高兴的。总之,这次实验我们学习了linux系统的使用方法,掌握了一些基本的linux命令,学习了添加系统调用的方法,更深入的了解了操作系统,为我们以后的工作学习打下坚实的基础。实验二:进程管理实验一、 实验目的1、 理解进程的概念,知道进程与程序的区别;2、 理解并发执行、进程互斥及进
11、程通信的基本概念;3、 了解并掌握进程管理的机制和操作步骤。二、 实验内容1、 编制实现软中断通信的程序使用系统调用 fork()创建两个子进程,再用系统调用 signal()让父进程捕捉键盘上发出 的中断信号(即按 delete 键),当父进程接收到这两个软中断的某一个后,父进程用系统调 用 kill()向两个子进程分别发出整数值为 16 和 17 软中断信号,子进程获得对应软中断信号, 然后分别输出下列信息后终止:Child process 1 is killed by parent !Child process 2 is killed by parent !父进程调用 wait()函数等
12、待两个子进程终止后,输入以下信息,结束进程执行: Parent process is killed! 多运行几次编写的程序,简略分析出现不同结果的原因。2、编制实现进程的管道通信的程序使用系统调用 pipe()建立一条管道线,两个子进程分别向管道写一句话:Child process 1 is sending a message!Child process 2 is sending a message!而父进程则从管道中读出来自于两个子进程的信息,显示在屏幕上。要求:父进程先接收子进程 P1 发来的消息,然后再接收子进程 P2 发来的消息。三、 实验步骤及结果分析1、 进程的软中断通信(1) 算
13、法流程图: 软中断通信程序流程图(2) 程序源代码:#include #include #include #include int wait_flag; void stop( ); main( ) int pid1, pid2; / 定义两个进程号变量 signal(3,stop); / 或者 signal(14,stop); while(pid1 = fork( ) = -1); / 若创建子进程1不成功,则空循环if(pid1 0) / 子进程创建成功,pid1为进程号 while(pid2 = fork( ) = -1); / 创建子进程2 if(pid2 0) wait_flag =
14、1; sleep(5); / 父进程等待5秒 kill(pid1,16); / 杀死进程1kill(pid2,17); / 杀死进程2 wait(0); / 等待第1个子进程1结束的信号 wait(0); / 等待第2个子进程2结束的信号 printf(n Parent process is killed !n); exit(0); / 父进程结束 else wait_flag = 1; signal(17,stop); / 等待进程2被杀死的中断号17 printf(n Child process 2 is killed by parent !n); exit(0); else wait_f
15、lag = 1; signal(16,stop); / 等待进程1被杀死的中断号16 printf(n Child process 1 is killed by parent !n); exit(0); void stop( ) wait_flag = 0; (3)实验结果:(4)简要分析1) signal函数 上述程序中,调用函数signal()都放在一段程序的前面部位,而不是在其他接收信号处。这是因为signal()的执行起的作用只是为进程指定信号量16和17,以及分配相应的与stop()过程连接的指针。因而signal()函数必须在程序前面部分执行。 2)wait函数 在父进程中调用第1
16、个wait(0)后,则父进程被阻塞。进入等待第一个子进程运行结束的队列,等待子进程结束。当子进程结束后,会产生一个终止状态字,系统会向父进程发出SIGCHLD信号。当接到信号后,父进程提取子进程的终止状态字,从wait()返回继续执行原程序。同样的方式,父进程继续执行第二个wait(0),并再次阻塞,等待第2个子进程运行结束。当第二个子进程运行结束后父进程继续执行剩余的语句。 3)关于exit函数 该函数中每个进程退出时都用了语句exit(0),这是进程的正常终止。在正常终止时,exit()函数返回进程结束状态。进程终止时,则由系统内核产生一个代表异常终止原因的终止状态,该进程的父进程都能用w
17、ait()得到其终止状态。在子进程调用exit()后,子进程的结束状态会返回给系统内核,由内核根据状态字生成终止状态,供父进程在wait()中读取数据。若子进程结束后,父进程还没有读取子进程的终止状态,则子进程就变成了“孤儿进程”,系统进程init会自动“收养”该子进程,成为该子进程的父进程,即父进程标识号变成1,当子进程结束时,init会自动调用wait()读取子进程的遗留数据,从而避免在系统中留下大量的垃圾。 4)结果显示上述结果中“Child process 1 is killed by parent !” 和“Child process 2 is killed by parent !”
18、相继出现,当运行几次后,谁在前谁在后是随机的。这是因为:从进程调度的角度看,子进程被创建后处于就绪态。此时,父进程和子进程作为两个独立的进程,共享同一个代码段,分别参加调度、执行,直至进程结束。但是谁会先被调度程序选中执行,则与系统的调度策略和系统当前的资源状态有关,是不确定的。因此,谁先从fork()函数中返回继续执行后面的语句也是不确定的。2、 进程的软中断通信(1) 算法流程图: 管道通信程序流程图(2) 程序源代码#include #include #include int pid1,pid2; / 定义两个进程变量 main( ) int fd2; char OutPipe100,I
19、nPipe100; / 定义两个字符数组 pipe(fd); / 创建管道 while(pid1 = fork( ) = -1); / 如果进程1创建不成功,则空循环 if(pid1 = 0) / 如果子进程1创建成功,pid1为进程号 lockf(fd1,1,0); / 锁定管道 sprintf(OutPipe,n Child process 1 is sending message!n); / 给Outpipe赋值 write(fd1,OutPipe,50); / 向管道写入数据 sleep(5); / 等待读进程读出数据 lockf(fd1,0,0); / 解除管道的锁定 exit(0)
20、; / 结束进程1 else while(pid2 = fork() = -1); / 若进程2创建不成功,则空循环 if(pid2 = 0) lockf(fd1,1,0); sprintf(OutPipe,n Child process 2 is sending message!n); write(fd1,OutPipe,50); sleep(5); lockf(fd1,0,0); exit(0); else wait(0); / 等待子进程1 结束 read(fd0,InPipe,50); / 从管道中读出数据 printf(%sn,InPipe); / 显示读出的数据 wait(0);
21、/ 等待子进程2 结束 read(fd0,InPipe,50); printf(%sn,InPipe); exit(0); / 父进程结束 (3) 实验结果 (4) 简要分析 管道,是指用于连接一个读进程和一个写进程,以实现它们之间信息的共享文件又称pipe文件。向管道(共享文件)提供输入的发送进程(即写进程),以字符流形式将大量的数据送入管道;而接收管道输送的接收进程(读进程),可以从管道中接收数据。 为了协调双方的通信,管道通信机制必须提供以下3方面的协调能力:1) 互斥。当一个进程正在对pipe进程读/写操作时,另一进程必须等待,程序中使用lock(fd1,1,0)函数实现对管道的加锁操
22、作,用lock(fd1,0,0)解除管道的锁定。2) 同步。当写进程把一定数量的数据写入pipe后,便去睡眠等待,直到读进程取走数据后,再把它唤醒。当读进程试图从一空管道中读取数据时,也应睡眠等待,直至写进程将数据写入管道后,才将其唤醒。3) 判断对方是否存在。只有确定写进程和读进程都存在的情况下,才能通过管道进行通信。四、 实验心得通过本次实验,我了解了进程的实质和进程管理的机制,并通过实验操作掌握了有关的知识。实验是在 Linux 系统下实现进程从创建到终止的全过程,从中我体会了进程的创建过程、父进程和子进程的关系、进程状态的变化、进程之间的同步机制、 进程调度的原理和以信号和管道为代表的
23、进程间通信方式的实现。当然,在实验过程中还有许多不足,我一定会慢慢改正,提升自我。实验三 存储器管理实验一、实验目的1、 理解内存页面调度的机理2、 掌握几种理论页面置换算法的实现方法3、 了解HASH数据结构的使用4、通过实验比较几种调度算法的性能优劣页面置换算法是虚拟存储管理实现的关键,通过本次实验理解内存页面调度的机制,在模拟实现FIFO、LRU、NRU和OPT几种经典页面置换算法的基础上,比较各种页面置换算法的效率及优缺点,从而了解虚拟存储实现的过程。二、实验内容对比以下几种算法的命中率:1、先进先出算法 FIFO(First In First Out)2、最近最少使用算法 LRU(L
24、east Recently Used)3、最近未使用算法 NUR(Never Used Recently)4、最佳置换算法 OPT(Optimal Replacement)三、实验分析1、置换算法原理FIFO 原理简述:(1) 在分配内存页面数(AP)小天进程页面数(PP)时,当然是最先运行的 AP 个页面放入内存;(2) 这时又需要处理新的页面,则将原来放的内存中的 AP 个页中最先进入的调出(FIFO),再将新页面放入;(3) 以后如果再有新页面需要调入,则都按上述规则进行。(4) 算法特点:所使用的内存页面构成一个队列。LRU 算法原理简述(1) 当内存分配页面数(AP)小于进程页面数(
25、PP)时,把最先执行的 AP 个页面放入内存。(2) 当需调页面进入内存,而当前分配的内存页面全部不空闲时,选择将其中最长时间没有用到的那一页调出,以空出内存来放置新调入的页面(LRU)。算法特点:每个页面都有属性来表示有多长时间未被 CPU 使用的信息。NUR 算法原理简述所谓“最近未使用”,首先是要对“近”做一个界定,比如 CLEAR_PERIOD=50,便是 指在 CPU 最近的 50 次进程页面处理工作中,都没有处理到的页面。那么可能会有以下几种 情况:(1) 如果这样的页面只有一个,就将其换出,放入需要处理的新页面。(2) 如果有这样的页面不止一个,就在这些页面中任取一个换出(可以是
26、下标最小的或者最小的),放入需要处理的页面。如果没有一个这样的页面,就随意换出一个页面(可以是下标最小的或者最大的)。OPT 原理简述所谓的最佳算法是一种理想状况下的算法,它要求先遍历所有的 CPU 待处理的进程页面序 列。在这些页面中,如果有些已经在内存中,而 CPU 不再处理的,就将其换出;而有些页 面已在内存中而 CPU 即将处理,就从当前位置算起,取最后才会处理到的页面,将其换出。OPT 算法实现:为每个进程页面设一个“间隔”属性 cDistance 表示 CPU 将在第几步处理到该页面,如 果页面不再被 CPU 处理,可以被设为某个很大的值(如 32767),这样每次被换出的就是 c
27、Distance 最大的那个页面。(1) 初始化。设置两个数组 pageap和 pagecontrolpp分别表示进程页面数和内存分 配的页面数,并产生一个随机数序列 maintotal_instruction(这个序列由 pageap的下标随机构成)表示待处理的进程页面顺序,diseffect 置 0。然后扫描整个页面访问序列,对 cDistanceTOTAL_VP数组进行赋值,表示该页面将在第几步被处理。(2) 看序列 main中是否有下一个元素,如果有,就由 main中获取一个 CPU 待处 理的页面号,并转(3),如果没有则转(6)。(3) 如果该页面已经在内存中了,就转(2),否则转
28、(4),同时 diseffect 加 1。(4) 看是否有空闲页面,如果有,就返回页面指针,并转到(5),否则,遍历所有 未处理的进程页面序列,如果有位于内存中的页面而以后 CPU 不再处理的,首 将其换出,返回页面指针;如果没有这样的页面,找出 CPU 将最晚处理的页面, 将其换出,并返回该页面指针。(5) 在内存页面和待处理的进程页面之间建立联系,转(2)。 输出计算 1-diseffect / total_instruction*100%的结果,完成。四、 实验步骤及结果(1)实验源程序:#include #include #include #include #include #incl
29、ude using namespace std;#define INVALID -1const int TOTAL_INSTRUCTION(320);const int TOTAL_VP(32);const int CLEAR_PERIOD(50);#include Page.h#include PageControl.h#include Memory.h int main()int i; CMemory a; for(i=4;i=32;i+)a.FIFO(i);a.LRU(i); a.NUR(i); a.OPT(i); coutn;return 0;#ifndef _MEMORY_H#define _MEMORY_Hclass CMemorypubli
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1