计算机操作系统课设操作系统课程设计报告_第1页
计算机操作系统课设操作系统课程设计报告_第2页
计算机操作系统课设操作系统课程设计报告_第3页
计算机操作系统课设操作系统课程设计报告_第4页
计算机操作系统课设操作系统课程设计报告_第5页
已阅读5页,还剩83页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、课 程 实 验 报 告题目: 操作系统原理专业班级: 信息安全 1302 班学号: 姓名: 指导教师: 报告日期: 2016 年 2 月 29 日计算机科学与技术学院目 录1.初探 Linux 编程环境11.1.实验目的11.2.实验内容11.2.1.实验一11.2.2.实验二11.3.实验设计11.3.1.实验一11.3.2.实验二21.4.实验环境31.5.实验步骤31.5.1.实验一31.5.2.实验二31.6.调试记录41.6.1.实验一41.6.2.实验二41.7.实验结果51.7.1.实验一51.7.2.实验二62.通过编译内核的方式为 Linux 增加系统调用82.1.实验目的8

2、2.2.实验内容82.3.实验设计82.4.实验环境82.5.实验步骤82.6.调试记录102.7.实验结果113.编写设备驱动程序133.1.实验目的133.2.实验内容133.3.实验设计133.4.实验环境143.5.实验步骤143.6.调试记录153.7.实验结果154.实现系统监控程序174.1.实验目的174.2.实验内容174.3.实验设计184.4.实验环境224.5.实验步骤224.6.调试记录224.7.实验结果235.设计模拟文件系统285.1.实验目的285.2.实验内容285.3.实验设计285.4.实验环境335.5.实验步骤345.6.调试记录345.7.实验结果

3、346.附注:程序清单396.1.文件拷贝程序396.2.多进程多窗口实验406.3.编译内核添加系统调用416.4.编写模块436.5.任务监视器496.6.模拟文件系统641. 初探 Linux 编程环境1.1. 实验目的掌握 Linux 操作系统的使用方法,包括键盘命令、系统调用;掌握在Linux 下的编程环境,了解在 Linux 如何进行多线程编程以及图形编程。1.2. 实验内容1.2.1. 实验一编写一个 C 程序,其内容为实现文件拷贝的功能。要求使用系统调用open、read、write。1.2.2. 实验二编写一个 C 程序,其内容为分窗口同时显示三个并发进程的运行结果。要求用到

4、 Linux 下的图形库(Gtk/Qt)。1.3. 实验设计1.3.1. 实验一所谓文件拷贝,就是将源文件的内容全部拷贝到新的文件中,产生一个与源文件一模一样的副本。因此,文件拷贝的实质就是数据的转移。在 Linux 系统中,文件拷贝的功能比较简单,其核心流程如图 1-1 所示。图 1-1 文件拷贝核心算法1.3.2. 实验二本实验的要求是在启动三个并发的进程,并在这三个并发的进程中分别显示窗口。因此,很容易就想到可以使用利用一个主控程序来启动三个不同的窗口程序的方式来完成本实验。因此本实验也是比较简单的。本实验中,我首先写了一个“主控程序”,用来创建三个进程来启动另外的三个窗口程序,这三个窗

5、口程序中,有两个是单一的窗口程序,而第三个窗口程序则又创建了 4 个进程来进行不同的计算操作。“主控程序”非常简单,就是使用了 3 个 fork 系统调用来创建 3 个新的进程,再在进程中使用 execl()函数来调用我已经编制完成的三个窗口程序。在主程序的最后,我还使用了三个 wait(NULL)函数用来等待所创建的子进程结束, 并起到回收“僵尸进程”的作用。三个窗口程序中,前两个单一的窗口程序都非常简单。第一个程序的作用是在窗口中显示一个图片文件,它的实现方式就是将图片加载进程序的资源中, 然后在窗口中添加一个 QLabel,再用 QLabel 显示这个图像;第二个窗口文件 是在窗口的 Q

6、Label 控件中动态显示当前时间,它是使用了 QTimer 这个定时器,给它设置一个回调函数,用来定时地获取时间并将时间显示在 QLabel 中。其中, 获取时机的函数是定义在 time.h 头文件中的 time 函数。相比起前两个窗口程序,第三个稍微复杂一些。它是在程序中创建了 4 个进程,并且申请了 5 个缓冲区和 9 个信号灯来控制着 4 个进程的行为。在进程创建的时候,窗口程序通过 excel()函数将创建的为各进程创建的共享内存区的ID 与信号灯 ID 传递给各进程,让其可以正常工作。其中,这 4 个进程运行的都是同一个程序,它们唯一不同的地方就是在执行的时候传递给它们的参数。主进

7、程在子进程启动后,又会启动一个线程,来定时获取各进程的计算结果, 并将其显示在窗口中。子进程的执行流程如图1-2 所示。而窗口程序中线程获取各子进程的计算结果以及向子进程发出退出信号等的方式与子进程中对共享内存区的操作相同,因此这里就不再列出窗口程序中的代码流程了。图 1-2 第三个窗口程序中子进程的执行流程1.4. 实验环境硬件平台:VMware 虚拟机,2G 内存,2 个 CPU 核心,40G 虚拟硬盘空间操作系统:64 位 Ubuntu 14.04.3,内核版本 3.19.0-49-generic编译器:64 位 GCC 4.8.4文本编辑器:emacs 24.5.1图形界面开发环境:Q

8、t 5.5.1 for Linux 64-bit1.5. 实验步骤1.5.1. 实验一首先,我查询了 open、read、write 这三个系统调用的相关资料,了解了它们的大概用法;接着,我就开始按照既定的算法编制程序了。等程序编制完成 后,我使用一个文本文件来进行测试,检查复制的文件与先前的文件是否一样。编译时使用命令 gcc o copy copy.c。经过多次不同的测试,结果表明程序运行正常。1.5.2. 实验二在此使用中,我首先编写了要在子进程中运行的三个窗口程序,其中,第三个窗口程序我也是采取的先编写子程序再编写“主控程序”的方法来进行的。等三个窗口程序编写完毕,并且测试运行正常后,

9、我再编写“主控程序”来调用这三个已经编写好的程序。执行程序进行测试,结果表明程序运行正常。1.6. 调试记录1.6.1. 实验一错误 1:程序可以创建目标文件,但是目标文件中总是没有任何数据。出错原因 1:经过仔细检查,发现是在使用 open 系统调用打开源文件的操作上出的问题。在这里我错误地使用了 O_WRONLY 标识来打开源文件,导致源文件只能写入,不能读出。解决方案 1:将 open 系统调用中的标识改成 O_RDONLY 就可以正常读取数据了。1.6.2. 实验二错误 1:将显示突破的窗口程序编译好后,从编译的文件夹内复制到外部以后就不能正常显示图片了。出错原因 1:经过分析,我认为

10、这是由于 Qt 中程序在显示磁盘中图片的时候,使用了相对路径。因此,当只移动了可执行文件,而没有移动图片文件的时候,程序就不能正常显示图片了。解决方案 1:为了一劳永逸地解决这个问题,我将图片文件直接包含进了程序的资源内, 这样就可以让 QLabel 直接加载程序内部的图片资源,而不用去外部寻找文件了, 这样也就永远解决了相对路径问题。错误 2:在第三个窗口程序运行结束并退出后,它所启动的子进程并不会像预想的那样退出。出错原因 2:通过仔细检查我的程序的逻辑,以及进行试验,我发现这些程序肯定是运行完毕了,并不是逻辑上有问题。接着我查找资料,搞明白了其实这些进程是成为了“僵尸进程”,即当父进程仍

11、在执行并且没有等待它的返回值时结束的子进程。解决方案 2:在弄清楚了“僵尸进程”产生的牵引后果之后,我在所有会产生子进程的程序中都添加了 wait(NULL)函数来使父进程等待子进程退出再退出,这样就解决了“僵尸进程”的问题。1.7. 实验结果1.7.1. 实验一在这里,我利用我的程序来复制一个它本身的源代码文件,以直观地看到程序执行的结果。如图 1-3 所示,在复制之前目录中只有编译好的程序以及它的源码文件。图 1-3 运行程序之前如图 1-4 所示,在运行程序之后,目录中复制出了一个 paste.c。图 1-4 运行程序之后如图 1-5 所示,使用 diff 程序检查源文件和复制出来的新文

12、件,结果显示两个文件的内容是完全一样的。图 1-5 检查两个文件是否相同如图 1-6 所示,是 copy.c 文件的部分内容。图 1-6 copy.c 文件的部分内容1.7.2. 实验二如图 1-7 所示,proc_main 程序是“主控程序”,是用来启动三个进程来执行另外的三个窗口程序的;proc1 程序会启动另外四个进程来进行不同的运算, 而这四个进程里执行的程序都是 proc_1;proc2 程序在运行时会显示一张卡通图片;proc3 程序是一个电子时钟,可以动态显示当前世时间。图 1-7 编译完成的程序如图1-8 所示,双击 proc_main 程序之后,立马显示出了三个窗口程序各自创

13、建的窗口。可以看到,现在最右边的 proc1 程序显示的窗口内还没有任何内容。图 1-8 双击“主控程序”后显示的三个窗口如图 1-9 所示,在点击最右边窗口中的“Start”按钮后,窗口的界面上开始显示内容。可以看到,这些显示的数字每隔 1 秒钟就自增一个固定值。图 1-9 proc1 程序开始进行运算如图 1-10 所示,再点击“Stop”按钮之后,proc1 程序停止了运算操作并保持了停止时的运算结果。事实上,一旦再次开始运算,程序会重新从 0 开始计数。图 1-10 proc1 程序停止运算2. 通过编译内核的方式为 Linux 增加系统调用2.1. 实验目的掌握通过编译内核的方法实现

14、系统调用的过程,了解 Linux 内核的编译方法和编译过程;掌握系统调用命令如 syscall 函数的使用方法,并能够编写程序来测试自己新增加的系统调用。2.2. 实验内容通过编译内核方法,增加一个新的系统调用。另编写一个应用程序,使用新增加的系统调用。(1) 内核编译、生成,使用新内核启动;(2) 新增系统调用实现:文件拷贝或 P、V 操作。2.3. 实验设计由于我使用的 Ubuntu 内核版本号是 3.19.49,因此为了避免系统现有的软件与我编译的内核出现兼容性问题,我没有使用老师在 PPT 中推荐的 2.6.6 版本的内核,而是使用了 Ubuntu 发布的当前内核的源代码来进行使用。在

15、实验过程中,我先查找关于内核的编译以及下载的资料,然后查找了如何添加以及使用系统调用的资料,最终完成了整个实验。2.4. 实验环境硬件平台:VMware 虚拟机,2G 内存,2 个 CPU 核心,40G 虚拟硬盘空间操作系统:64 位 Ubuntu 14.04.3,内核版本 3.19.0-49-generic编译器:64 位 GCC 4.8.4文本编辑器:emacs 24.5.1待编译内核:linux-lts-vivid-3.19.02.5. 实验步骤首先,我使用命令sudo apt-get install dpkg-dev来安装 dpkg-dev,用来自动解压下载的源码包。接着,我就使用命令

16、sudo apt-get source linux-image-uname r来获取 Ubuntu 当前版本的源码包。接着,我使用命令sudo apt-get build-dep linux-image-uname r来自动安装编译当前源码所需要的各种依赖库文件和开发工具。接下来,我就进入解压好的源码包路径中,首先,在 include/asm-generic/syscall.h 文件中添加我的系统调用的原型,接着,我在 arch/x86/syscalls/syscall_64.tbl 选择了三个没有被使用的数字来作为我的系统调用号,然后,我在 kernel/sys.c 中添加了我所要实现的三个

17、系统调用的 C 语言代码。这样,编译代码的准备工作就完成了。在进入代码所在的目录后,我首先使用命令sudo su切换到 root 身份,接着执行命令make mrproper接着我将我的 Ubuntu 现有的存在于/usr/src/Linux-headers-3.19.0-generic/文件夹中的.config 配置文件拷贝到当前源码目录,然后执行命令make menuconfig这个时候,在弹出的图形菜单里选择载入刚才拷贝的.config 文件,然后保存退出。接下来就可以开始编译内核了。我先执行命令make来编译内核,等代码编译好之后,我再执行命令make modules来编译模块,然后,

18、我执行命令make install来安装内核,接着,我再执行命令make modules_install来安装内核模块。这样,内核以及内核模块就全部到了合适的地方了,这个时候,在我执行update-grub之后,重启电脑,内核编译就完成了。内核编译完成之后,我便利用 syscall 函数来编写程序测试我添加的系统调用了。这两个程序分别实现了文件复制和临界资源的使用这两个功能。2.6. 调试记录错误 1:在我第一次将内核编译完成,重启虚拟机后,并没有成功进入新内核,而是出现了如图 2-1 所示的界面。图 2-1 错误 1 界面出错原因 1:按照图片上面的提示,出错原因应该是找不到磁盘。由于对于这

19、个错误有些迷糊, 我就上网查阅了相关资料,最后没有找到具体的原因,但是找到了解决方法。解决方案 1:根据我从网络上查阅得到的资料,解决方法就是首先执行命令sudo grub-install /dev/sda接着,执行命令sudo update-initramfs u就可以了。在我执行完这两个命令之后,问题也真的解决了。错误 2:我编写的文件复制系统调用都无法实现预期的功能。出错原因 2:为了定位到错误,我在系统调用中添加了 printk 函数来打印一些关键算法的结果,最后我发现程序在执行写入操作的时候出了错误。在反复检查发现程序的其他部分没有错误之后,我就去查找了相关资料,最终发现是系统调用在

20、由内核空间向用户空间写入数据的时候出的问题。解决方案 2:经过查阅资料,我发现要使用 fs = get_fs()函数保存当前内存段状态,再使用 set_fs(get_ds)函数来设置当前内存空间为用户空间,然后就可以进行文件的读写操作了。最后,再使用 set_fs(fs)来恢复内存段状态即可。同时,我检查实现 P、V 操作的系统调用之后,发现它们也存在这样的问题,我就一并将其改正过来了。2.7. 实验结果如图 2-2 所示,是我进入自己编译的内核时的内核信息。图 2-2 新内核的内核信息首先测试使用了复制文件的系统调用的程序。如图2-3 所示,当前的文件夹里只有编译出的可执行文件和它的源代码文

21、件 copy.c。图 2-3 当前文件夹的文件如图 2-4 所示,我将源代码文件复制了一份,复制为 paste.c图 2-4 执行程序后的文件如图 2-5 所示,我 diff 命令测试结果表明两个文件相同。图 2-5 使用 diff 测试两个文件是否相同如图 2-6 所示,是 copy.c 文件的内容图 2-6 cat 文件的内容测试完了实现复制功能的系统调用之后,我接着编写了一个双线程交替输出的程序测试实现 P、V 操作的系统调用。测试结果如图 2-7 所示。图 2-7 P、V 系统调用测试结果3. 编写设备驱动程序3.1. 实验目的掌握在 Linux 下编写、使用简单设备驱动程序的方法。3

22、.2. 实验内容通过模块方法,增加一个新的设备驱动程序,其功能可以简单。要求实现字符设备的驱动。3.3. 实验设计为了进行本次实验,我首先查阅了各种资料,对 Linux 的字符驱动程序形成了一个基本的认识,才开始进行模块程序的编写的。在本次实验中,我设计实现了一个拥有 1024Byte 内存缓冲区的字符设备模块,使之可以存储写入的数据,也允许程序从中读取数据,也可以让程序利 用 llseek 在其中进行随机读写,还允许程序使用 ioctl 函数清除缓冲区中的内容。为了让模块实现预定的功能,我实现了一个字符设备驱动所要求的 6 个函数,它们分别是:llseek、ioctl、read、write、

23、open、release。其中,llseek 让用户能够在模块的缓冲区中寻址进行随机读写;ioctl 让用户可以清除字符设备模块缓冲区中的内容,使之以 0 填充;read 让用户可以从模块缓冲区中读取指定字节的数据;write 让用户可以向缓冲区中写入指定字节的数据;open 函数在用户打开字符设备的时候被调用;release 函数在用户使用 close 函数关闭设备的时候被调用。当然,在字符设备驱动中,实现上面那些功能的函数并不是一定叫那些名字。这些函数的名字都可以随便起,只要在设备初始化的时候将适当的函数地址填充到一个 file_operations 的结构体中,并注册设备就可以了。在设备

24、的初始阶段,我首先直接使用 alloc_chdev_region 函数来动态申请设备号,接着使用 kmalloc 函数为我的设备结构体申请内存,接着我使用cdev_init 函数与之前填充好的 file_operations 结构体的指针来初始化本设备的cdev 结构,再使用 cdev_add 函数将本设备加入到内核中。接着,我使用了class_create 与 device_create 函数在/dev 目录下创建字符设备驱动程序的节点。在 open 函数与 release 函数中,所做的操作就仅仅是将当前字符设备的引用计数,也就是打开此设备的程序的数目进行了改变。就是说,每 open 一次

25、设备,引用计数就加 1,每 close 一次设备,引用计数就减 1。我所实现的 read 与 write 函数,功能就是根据当前读写指针所在的位置以及传入的要读取或写入的字节数,向用户空间的缓冲区复制数据或者从用户空间的缓冲区中加载数据。这些函数在算法上并没有太多困难的地方,不过为了正确进行数据交换的操作,在向用户空间写入数据的时候;需要使用copy_to_user 函数,而在从用户空间复制数据的时候,需要使用 copy_from_user 函数来进行。而对于 llseek 函数,作用就是设置当前的读写指针,也就是函数传入参数中 filp 结构体的 f_pos 成员。具体的实现就是要求读写指针

26、所在的位置要大于等于 0,小于缓冲区的最大长度 1024。对于 ioctl 函数,我实现的版本只起到清空字符设备缓冲区的功能,它是使用了 memset 函数来实现清空字符设备缓冲区的功能的。3.4. 实验环境在进行本实验的时候,我使用了我在第二个实验中编译的内核。因此本次实验以及后续的实验中,我的实验环境中的内核都是我编译的新内核。我的实验环境如下:硬件平台:VMware 虚拟机,2G 内存,2 个 CPU 核心,40G 虚拟硬盘空间操作系统:64 位 Ubuntu 14.04.3,内核版本 3.19.8-ctk10编译器:64 位 GCC 4.8.4文本编辑器:emacs 24.5.13.5

27、. 实验步骤首先,我编写了要实现的设备驱动程序的源代码;接着,我编写了用于编译本模块的 Makefile,在 Makefile 中,需要指定编译该模块所用的 Linux 内核头文件所在路径,我就指定了我的新内核的源码所在的目录。在模块源码所在目录中,我执行make命令就可以编译出模块了;接着我使用sudo insmod linux_mod.ko命令来将我编译的模块安装到系统中。模块安装到系统中后,就可以在/dev 目录下看到我编写的模块了。接着,我编写了两个程序,一个程序是向设备驱动中写入一个字符串,同时在 llseek 后,再从设备驱动中读取一个字符串;另一个程序的功能是从设备驱动中读取一个

28、字符串,再使用 ioctl 清空设备驱动的缓冲区中,llsek 到缓冲区的开头,再读取一次。这么设计的目的是看设备驱动的读取、写入、寻址与清空功能是不是可以正常工作。3.6. 调试记录错误 1:在编译设备驱动时,编译器提示 file_operations 结构体中不存在 ioctl 成员。错误原因 1:经过查阅资料,我得知在我所编译的内核版本中,file_operations 结构体中的 ioctl 成员已经被废弃了,转而被 unlocked_ioctl 取代。解决方案 1:我将我所编写的 ioctl 函数的地址赋值给 file_operations 的 unlocked_ioctl 成员,而

29、不是 ioctl 成员。这样,编译就通过了。并且在后续测试的时候 ioctl 的功能也是可以正常使用的3.7. 实验结果如图 3-1 所示,myMod-0 是我使用sudo insmod linux_mod.ko命令安装模块后,模块出现在/dev 目录下的设备节点。图 3-1 设备驱动在/dev 中的节点如图3-2 所示,first 程序先向设备驱动中写入在命令行中给定的字符串,再将文件读写指针置 0 位置,接着重新读取刚才写入的字符串,再输出到屏幕上。由结果可以看到,程序与设备驱动很好地相配合,完成了预定的功能。图 3-2 first 程序执行结果如图3-3 所示,second 程序先从设备

30、驱动的缓冲区中取出刚刚被 first 程序写入的字符串,将其输出出来,接着将设备驱动的缓冲区置 0,然后将读写指针置0,再从设备驱动中读取数据,再将结果输出。可以看到,second 程序也完美完成了预定的功能。图 3-3 second 程序执行结果4. 实现系统监控程序4.1. 实验目的了解/proc 文件的特定和使用方法,了解如何从/proc 文件中获取进程与系统信息、计算机运行状态,同时加深对 Linux 环境下图形用户界面程序设计的理解。4.2. 实验内容通过读取/proc 文件系统,获取系统各种信息,并以比较容易理解的方式显示出来。要求使用 GTK+/Qt 下的 C 语言进行开发,具体

31、要求包括:1、获取并显示主机名2、获取并显示系统启动的时间3、显示系统到目前为止持续运行的时间4、显示系统的版本号5、显示 CPU 的型号和主频大小6、通过 pid 或者进程名查询一个进程,并显示该进程的详细信息,提供杀掉该进程的功能7、显示系统所有进程的一些信息,包括 pid、ppid、占用内存大小、优先级等等8、CPU 使用率的图形化显示(2 分钟内的历史记录曲线)9、内存和交换分区(swap)使用率的图形化显示(2 分钟内的历史记录曲线)10、在状态栏显示当前时间11、在状态栏显示当前 CPU 使用率12、在状态栏显示当前内存使用情况13、用新进程运行一个其他程序14、关机功能参照 WI

32、NDOWS 的任务管理器,实现其中的几个功能。4.3. 实验设计出于简单起见,我根据 Ubuntu 下已有的 system monitor 程序设计了我的程序的用户界面。即,将程序界面分为两个部分:第一个部分只显示进程信息以及只进行与进程有关的操作(比如创建进程、结束进程等);第二个部分显示系统相关信息。当然,无论在哪个界面,程序底部的状态栏中都会显示 CPU 利用率、内存使用率和当前时间。首先说与进程有关的界面。在本界面中,会显示所有进程的进程名、所属用户名、进程 ID、CPU 使用率、内存占用与优先级。并且,在界面下部还提供了按钮来实现按 pid 或进程名来查找某进程详细信息、查看在信息列

33、表中选中进程的详细信息、按 pid 或进程名结束进程以及以新进程运行命令行程序的功能。进程信息的列表刷新函数使用 QTimer 来计时,保证每隔一段时间来刷新一次。在这里,我参考了系统自带的 monitor,将计时器的超时时间设置为 3 秒。在 proc 文件系统中,所有以纯数字命名的文件夹,其文件名都是其所对应进程的 pid,而文件夹中存储的就是该 pid 所代表进程的详细信息。而其中这么多信息中,所用到的文件只有两个:stat 文件与 status 文件。在 stat 文件中,存储有进程的 pid、进程名、进程的优先级与进程在用户态与和心态运行时占用的 CPU 时间。其中,pid 与进程名

34、信息是可以直接用于信息的显示的,而优先级与进程的 CPU 占用率则是需要进行一定的计算或者转换以后才能作为可视化的信息呈现出来。在 Linux 系统中,进程的优先级是分为静态优先级和动态优先级的。在这里,为方便起见,我只取进程的静态优先级来显示。进程的静态优先级是一个范围为-20 到 20 的整数,其中,-20 表示进程的优先级最高,20 表示进程的优先级最低。为了显示直观,我将-20 到-17 之间的优先级定义为“Very High”,- 16 到-10 之间的优先级定义为“High”,-9 到 9 之间的优先级定义为“Normal”,10 到 16 之间的优先级定义为“Low”,17 到

35、20 之间的优先级定义为“VeryLow”。在取得进程的优先级之后,我就使用些字符串将进程的优先级以可读的方式表示出来。取得进程 CPU 使用率就相对比较麻烦了。在 Linux 系统中,进程的 CPU 使用率等于进程 CPU 时间除以进程运行时间得到的百分比。其中,进程 CPU 时间可以从该进程在 proc 文件系统中的 stat 文件中读取,就是其 utime(进程在用户态运行时间)字段与 stime(进程在核心态运行时间)字段相加得到。而进程运行时间则是等于系统运行时间减去进程启动时间。其中,系统运行时间需要从/proc/uptime 中获取,而进程启动时间则也可以从该进程的 stat 文

36、件中获取。进程的所属用户与内存占用这两个信息需要从该进程的 status 文件中获取。其中,为了获取进程的用户信息。需要现充 status 文件中获取进程所属用户的uid,再使用定义在 pwd.h 头文件中的 getpwuid 函数来获取该 uid 对应的用户名即可。而至于进程的内存占用信息,则是直接取 status 文件中的 VmRSS(进程在内存中的驻留大小),并将数值除以 1024 KB 得到的。在进程界面底部的按钮中,实现按 pid 或进程名查找指定进程和查看进程详细信息的按钮在被按下后,实现的功能是一样的,只是它们的信息来源不一样而已。为了实现按 pid 或进程名查找指定进程,我在程

37、序中维护了两个QVector,这两个 QVector 分别存储了系统中所有进程的 pid 和进程名,并且这两个 QVector 中的元素是一一对应的。这两个 QVector 的数据是在程序获取并显示所有进程信息的时候更新的。当用户输入 pid 后,程序就直接根据 pid 查找该进程的 status 文件,并将其显示出来;而若用户输入的是进程名,程序就根据这两个 QVector 中的内容来查找指定进程名的 pid,再根据 pid 显示该进程的status 文件内容。当点击“终止进程运行”按钮后,会弹出一个对话框,让用户选择输入要终止运行的进程的 pid 或者用户名。其中,查找进程 pid 的方法

38、与上面的按 pid 或进程名查找指定进程的功能是一样的。在找到 pid 后,程序中使用了 system 命令来执行 sudo kill -9 $pid,以向指定进程发送 exit 信号,使其结束运行。为了可以在不输入密码的情况下执行 sudo 命令,我向/etc/sudoers 文件中的行%admin ALL=(ALL) ALL下面一行中写入了如下语句:spiraldox ALL = NOPASSWD : ALL其中,spiraldox 是我的 ubuntu 中的用户名。这样,我就可以在不输入密码的情况下使用 sudo 命令了。也就可以在任务管理器中结束任意进程了。当点击“创建新进程”按钮时,

39、程序也是在 fork 出的新进程中使用 system 命令来执行指定的程序。不过在创建新进程的过程中,我没有使用 sudo 命令, 因此创建的进程是以当前用户身份运行的。接下来要介绍的就是与系统信息相关的界面。在这里我将把状态栏中的项目于这个界面放在一起进行介绍。在与系统信息相关的界面中,显示了主机名、系统的启动时间、系统持续 运行的时间、系统的内核版本、CPU 信息、CPU 使用率的图形曲线、内存与交换分区使用率的图形曲线、内存总量信息、swap 总量信息、已经使用的内存大小信息、已经使用的 swap 大小信息以及一个关闭计算机的按钮。而在状态栏中, 提供了当前 CPU 使用率、当前内存使用

40、率与当前时间这三个信息。这个界面中, 需要刷新的部分也是使用 QTimer 来定时触发函数刷新的。这里设置的触发时间是 980 毫秒。之所以选择这个数字,是因为当我设置为 1 秒的时候,程序由 于执行时的时间误差,导致时间显示不正确。当设置超时时间为 980 毫秒之后, 问题就解决了。由于这个界面中内容比较多,因此我将分条目介绍。1、主机名主机名以字符串形式存储在/proc/sys/kernel/hostname 中,可以在读取之后直接显示在界面上。2、系统启动时间在 proc 文件系统中,是不能直接获取系统的显示时间的,不过可以从/proc/uptime 文件的第一项中获取系统持续运行的时间

41、(时间单位为毫秒)。我获取系统启动时间的方法就是,先使用 Qt 的 QDateTime 类库中的 currentDateTime().toMSecsSinceEpoch()函数来获取当前时间的毫秒数,然后减去从 uptime 文件中取得的系统运行时间,再使用QDateTime 中的 fromMSecsSinceEpoch 函数将毫秒数转换为形式时间, 就得到了系统的启动时间。3、系统持续运行时间系统的持续运行时间就是从上面提到的/proc/uptime 中取得的。4、系统内核版本系统的内核版本由两部分组成,一个是内核类型,一个是内核的发布版本。其中,内核类型在文件/proc/sys/kerne

42、l/ostype 中,而发布版本则存在于文件/proc/sys/kernel/osrelease 中。两个文件中的字符串都可以直接读取。在读取之后,将两个字符串连接起来,就获得了系统的内核版本。5、CPU 信息CPU 信息可以从/proc/cpuinfo 文件中取得。不过对于多核 CPU,该文件会对应每一个核心显示一个 CPU 信息。为方便起见,我的程序只选取了文件内容中的第一组 CPU 信息的 model name 来作为 CPU 信息显示。6、内存总量信息内存与 swap 分区的信息都存在于/proc/meminfo 中。其中,内存总量就是 MemTotal 的数值。7、swap 总量信息

43、上面已经说了,swap 总量的数值也是在/proc/meminfo 中,它是其中的SwapTotal 的数值。8、 已使用内存大小信息在/proc/meminfo 文件中,并没有直接给出已使用内存大小的信息,而是给出了当前可用内存的大小。因此要获取已用内存大小,就直接使用内存总大小减去可用内存大小(MemAvaliable 的数值)即可。9、 已使用 swap 大小信息同内存一样,已使用 swap 的大小也需要 swap 的总大小减去可用 swap空间的大小(SwapFree 的数值)来获得。10、 关闭计算机我使用了 system 函数执行sudo poweroff命令来实现这个功能。由于已

44、经在前面的步骤中给/etc/sudoers 文件添加了我的用户名,因此在这里也可以直接点击按钮进行关机操作,而不必输入密码。11、 当前 CPU 使用率系统的 CPU 使用率等于两个时间段的 CPU 非空闲时间差除以 CPU 时间总量差得多的百分比。也就是说,要得到这个数值,需要多次对/proc/stat 中的数据进行采样,并且将相邻两次所得的数据想减,然后作除法得到的。而 CPU 时间总量(time)的计算是从/proc/stat 数据中第一行所有数字相加得到的,CPU 非空闲时间(busy)则是从 CPU 时间总量中,减去/proc/stat 文件中第一行第 4 排的数据,也就是空闲(id

45、le) 时间。CPU 的使用率就是(busy2 - busy1)/(time2 - time1)。12、 当前内存使用率与 CPU 使用率不同,内存使用率不需要多次采样才能计算。内存的使用率就是利用/proc/meminfo 文件中的内存总量和可用内存大小来计算的。也就是(MemTotal MemAvaliable)/MemTotal。13、 CPU 使用率的图形化显示在我的程序中,CPU 使用率的曲线图是使用 QCusteomPlot 绘制的。为了绘制图形,我使用了两个 QVector 来保存数据,一个保存过往的CPU 使用率,另外一个保存过往 CPU 使用率对应的横坐标。每次刷新的时候,保

46、存横坐标的 QVector 中的数据可以不移动,而保存过往CPU 使用率的 QVector 中的数据要向前移动,QVector 中装满了数据, 就将最后的数据丢弃。为了保持图形相对连贯,我设置图形为每 200 毫秒刷新一次。在 CPU 使用率的图形中,曲线颜色为蓝色。14、内存与 swap 使用率的图形化显示内存与 swap 的图形化使用率的设置与 CPU 的图形化使用率的设置方法是一样的,而获取 swap 使用率的方法与获取内存使用率的方法也是相同的,因此这里不再对这两天曲线的绘制进行赘述。在本图中,内存的使用率曲线是红色的,而 swap 使用率的曲线则是蓝色的。4.4. 实验环境硬件平台:

47、VMware 虚拟机,2G 内存,2 个 CPU 核心,40G 虚拟硬盘空间操作系统:64 位 Ubuntu 14.04.3,内核版本 3.19.0-49-generic编译器:64 位 GCC 4.8.4文本编辑器:emacs 24.5.1图形界面开发环境:Qt 5.5.1 for Linux 64-bit图标绘图库:QCustomPlot 1.3.24.5. 实验步骤首先,我根据 ubuntu 自带的 monitor 程序设计了我的系统监控器的界面, 然后我就根据系统监控器要显示的信息在网络上查阅文件系统的相关资料以及Qt 中实现相关界面效果的控件与方式,最后写完了程序就开始调试差错。4.

48、6. 调试记录错误 1:程序运行的时候,显示的时间在改变的时候会一次增加二秒,而不是一次增加一秒。错误原因 1:经过分析与实验,我认为问题出在触发时间刷新函数的计时器上。因为我一开始给计时器设计的超时时间是一秒钟,而程序的执行也是需要时间的。这样一来,等到程序显示的时间刷新的时候,时间已经过去了一秒多了,而以秒计数的时间是没有四舍五入这种计算方式的,超过了这一秒就到了下一秒。因此出现了时间每二秒变化一次的情况。解决方案 1:我将该计时器的超时时间由一秒钟修改为 980 毫秒,让程序有一定时间来执行相关操作,最终解决了问题。错误 2:在使用 Qt 的 QTableWidget 控件显示进程信息的

49、时候,若点击了某列的表头,则该列后面的所有列都会显示空白。错误原因 2:我在网上找了很久,也看了 Qt 的官方文档,也没有找到答案。而换用其他控件之后,数据显示正常,因此我怀疑是此控件应该不支持大量数据的排序的吧。解决方案 2:我 QTableWidget 控件替换为了 QTableView 控件,最终解决了显示异常的问题。4.7. 实验结果如图 4-1 所示,这是我编写的程序的进程信息界面,可以在界面中看到进程的进程名、进程所属用户、进程 pid、进程 CPU 使用率以及进程优先级等信息。还可以在底部状态栏看到当前 CPU 使用率、当前内存使用率以及当前时间等信息。图 4-1 监控器的进程信

50、息界面如图 4-2 所示,我使用 pid 查找 init 进程,查找结果如图 4-3 所示。图 4-2 按 pid 查找 init 进程图 4-3 按 pid 查找结果如图 4-4 所示,我按进程名查找 sshd 进程,查找图 4-5 结果如所示。图 4-4 按进程名查找 sshd 进程图 4-5 按进程名查找结果如图 4-6 所示,选中 cron 进程来查看其详细信息。图 4-6 选中 cron 进程查看其详细信息如所示,创建新进程运行 gtk3-demo 程序,如所示是运行结果图 4-7 创建新进程运行 gtk3-demo图 4-8 进程运行结果如图 4-9 所示是根据 pid 结束进程运

51、行,如图 4-10 所示是根据进程名结束进程运行。执行后都可以成功结束 gtk3-demo 的运行。图 4-9 根据 pid 结束进程执行图 4-10 根据进程名结束进程执行如图 4-11 所示是系统信息显示界面,在本界面上可以看到系统当前的各种信息, 并且可以注意到底部状态栏的显示数据。值得一提的时候,由于 swap 分区使用量太小,因此标识 swap 使用率的蓝色曲线并没有在下面的图中显示出来。图 4-11 系统信息显示界面关闭计算机的按钮也是可以正常工作的,这里我就不记录执行结果了。5. 设计模拟文件系统5.1. 实验目的了解并掌握简单文件系统的设计,体会并理解文件系统中数据的组织方式与

52、各模块间协同工作的过程。5.2. 实验内容设计并实现一个模拟的文件系统1、基于一大文件(10M 或 100M),模拟磁盘;2、格式化,建立文件系统管理数据结构;3、基本操作,实现文件、目录相关操作。5.3. 实验设计由于文件系统的设计相对来说比较复杂因此在进行程序编制之前一定要先规划好文件系统要实现的功能以及大致的实现步骤,避免在程序编制后期产生混乱。首先要做的,就是确定文件系统程序的特性,这个功能分为系统自身的特性和用户操作方式。在系统自身的特性上,我的设计如下:1、支持树形目录结构2、以索引方式存储文件和目录,支持一级索引;其中,索引值可以直接用于在文件系统内部查找文件和目录,不过目录中数

53、据的索引值存储的是磁盘块号3、使用 100M 的文件来模拟文件系统,每个磁盘块大小 1024 Byte4、使用位示图实现文件系统空白块的分配5、能够分辨给定的文件系统程序是否已被格式化6、除根目录外的目录支持“.”目录,即可以直接返回上一级目录而在用户操作方式上,我为系统设计了 14 个基本操作,它们分别是: 1、格式化文件系统2、切换目录3、新建目录4、列出目录下的所有项目5、删除目录6、新建文件7、打开文件8、删除文件9、关闭文件10、从控制台向打开的文件中写入数据11、在控制台打印打开的文件中的数据12、项目(目录或者文件)更名13、将文件系统内部的文件拷贝到文件系统的外部14、从文件系

54、统的外部拷贝文件到系统内部文件系统的特性决定以后,我就开始设计文件系统中所需要的数据结构了, 它们是:其中,文件系统控制块位于整个文件系统所在磁盘文件中的最前方,也就是其在文件中的开始偏移是 0,用于整个磁盘文件的管理。其中的 root_dir 结构成员存储的是根目录所在磁盘块的文件偏移,用于使程序在载入磁盘文件后第 一时间找到根目录的位置并将其载入内存。其中的 header 整形变量是一个常数, 程序在载入磁盘文件后读取文件中的数据,并根据读取到的第一个整形变量的 数值来确定磁盘文件是否已被格式化。目录控制块的作用是存储目录的相关信息,它包括文件名(name)、存储的目录项的个数(dir_i

55、tem_num)、其目录项链表的第一个目录项在磁盘中的位置(item_addr)以及其父目录的目录控制块在磁盘中的位置(parent_addr)。目录项的作用是标记属于该目录的项目的位置和类型。其中的 next_disk 结构成员用于标记目录项链表中链接到此目录项的下一个目录项在磁盘上的位置;type 用于标记此目录项索引的是目录还是文件;subitem 是该目录项所指项目在磁盘中的偏移;而 flag 则是标识此目录项是否已被删除,如果它的值为0,说明这个目录项已不再使用,它所在的空间可以被重新分配给其他目录项;在最下面用于形成链表的两个数据成员 next 与 prev 的数据不在磁盘文件上存储。文件控制块的作用是记录文件的文件名(name)、文件大小(size)、文件的数据所在的磁盘块(addr_disk9)。其中,addr_disk 数组的前 8 个成员都是直接索引表,即可以直接根据其中的内容找到数据所在磁盘块;而 addr_disk 的第 9 个成员是一级索引表,即它索引了一个有 256

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论