资源描述
等中科技火穿课程实验报告题目:_操作系统原理_:_:目录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.实验目的.82.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,实验结果.346.附注:程序清单.406.1.文件拷贝程序.4062 多进程多窗口实验.416.3.编译内核添加系统调用.426.4,编写模块.446.5.任务监视器.506.6.模拟文件系统.651.初探Linux编程环境1.1.实验目的掌握Linux操作系统的使用方法,包括键盘命令、系统调用;掌握在Linux 下的编程环境,了解在Linux如何进行多线程编程以及图形编程。1.2,实验内容1.2.1.实验一编写一个C程序,其内容为实现文件拷贝的功能。要求使用系统调用open、read write o1.2.2.实验二编写一个C程序,其内容为分窗口同时显示三个并发进程的运行结果。要 求用到Linux下的图形库(Gtk/Qt)。1.3.实验设计1.3.1.实验一所谓文件拷贝,就是将源文件的内容全部拷贝到新的文件中,产生一个与 源文件一模一样的副本。因此,文件拷贝的实质就是数据的转移。在Linux系统中,文件拷贝的功能比较简单,其核心流程如图1-1所示。图1-1文件拷贝核心算法11.3.2.实验二本实验的要求是在启动三个并发的进程,并在这三个并发的进程中分别显 示窗口。因此,很容易就想到可以使用利用一个主控程序来启动三个不同的窗口 程序的方式来完成本实验。因此本实验也是比较简单的。本实验中,我首先写了一个“主控程序”,用来创建三个进程来启动另外的 三个窗口程序,这三个窗口程序中,有两个是单一的窗口程序,而第三个窗口程 序则又创建了 4个进程来进行不同的计算操作。“主控程序”非常简单,就是使用了 3个fork系统调用来创建3个新的进 程,再在进程中使用execl()函数来调用我已经编制完成的三个窗口程序。在主程 序的最后,我还使用了三个wait(NULL)函数用来等待所创建的子进程结束,并 起到回收“僵尸进程”的作用。三个窗口程序中,前两个单一的窗口程序都非常简单。第一个程序的作用 是在窗口中显示一个图片文件,它的实现方式就是将图片加载进程序的资源中,然后在窗口中添加一个QLabel,再用QLabel显示这个图像;第二个窗口文件是 在窗口的QLabel控件中动态显示当前时间,它是使用了 QTimer这个定时器,给它设置一个回调函数,用来定时地获取时间并将时间显示在QLabel中。其中,获取时机的函数是定义在time.h头文件中的time函数。相比起前两个窗口程序,第三个稍微复杂一些。它是在程序中创建了 4个 进程,并且申请了 5个缓冲区和9个信号灯来控制着4个进程的行为。在进程创 建的时候,窗口程序通过excel。函数将创建的为各进程创建的共享内存区的ID 与信号灯ID传递给各进程,让其可以正常工作。其中,这4个进程运行的都是 同一个程序,它们唯一不同的地方就是在执行的时候传递给它们的参数。主进程 在子进程启动后,又会启动一个线程,来定时获取各进程的计算结果,并将其显 示在窗口中。子进程的执行流程如图1-2所示。而窗口程序中线程获取各子进程 的计算结果以及向子进程发出退出信号等的方式与子进程中对共享内存区的操 作相同,因此这里就不再列出窗口程序中的代码流程了。2图1-2第三个窗口程序中子进程的执行流程1.4.实验环境硬件平台:VMware虚拟机,2G内存,2个CPU核心,40G虚拟硬盘空间操作系统:64 位 Ubuntu 14.04.3,内核版本 3.19.0-49-generic编译器:64位GCC4.8.4文本编辑器:emacs 24.5.1图形界面开发环境:Qt 5.5.1 for Linux 64-bit1.5.实验步骤1.5.1.实验一首先,我查询了 open、read、write这三个系统调用的相关资料,了解了它 们的大概用法;接着,我就开始按照既定的算法编制程序了。等程序编制完成后,我使用一个文本文件来进行测试,检查复制的文件与先前的文件是否一样。编译时使用命令gcc-o copy copy.Co经过多次不同的测试,结果表明程序运行正常。1.5.2.实验二在此使用中,我首先编写了要在子进程中运行的三个窗口程序,其中,第 三个窗口程序我也是采取的先编写子程序再编写“主控程序”的方法来进行的。等三个窗口程序编写完毕,并且测试运行正常后,我再编写“主控程序”来调用 3这三个已经编写好的程序。执行程序进行测试,结果表明程序运行正常。1.6.调试记录1.6.1.实验一错误L程序可以创建目标文件,但是目标文件中总是没有任何数据。出错原因1:经过仔细检查,发现是在使用open系统调用打开源文件的操作上出的问题。在这 里我错误地使用了 O_WRONLY标识来打开源文件,导致源文件只能写入,不能读出。解决方案1:将open系统调用中的标识改成O_RDONLY就可以正常读取数据了。1.6.2,实验二错误1:将显示突破的窗口程序编译好后,从编译的文件夹内复制到外部以后就不 能正常显示图片了。出错原因1:经过分析,我认为这是由于Qt中程序在显示磁盘中图片的时候,使用了相对路径。因此,当只移动了可执行文件,而没有移动图片文件的时候,程序就不能正常显示图片 To解决方案1:为了一劳永逸地解决这个问题,我将图片文件直接包含进了程序的资源内,这样就可以让QLabel直接加载程序内部的图片资源,而不用去外部寻找文件了,这样也就永远解决了相对路径问题。错误2:在第三个窗口程序运行结束并退出后,它所启动的子进程并不会像预想的 那样退出。出错原因2:4通过仔细检查我的程序的逻辑,以及进行试验,我发现这些程序肯定是运行完毕 了,并不是逻辑上有问题。接着我查找资料,搞明白了其实这些进程是成为了“僵尸进 程”,即当父进程仍在执行并且没有等待它的返回值时结束的子进程。解决方案2:在弄清楚了“僵尸进程”产生的牵引后果之后,我在所有会产生子进程的 程序中都添加了 wait(NULL)函数来使父进程等待子进程退出再退出,这样就解 决了“僵尸进程”的问题。1.7.实验结果1.7.1.实验一在这里,我利用我的程序来复制一个它本身的源代码文件,以直观地看到 程序执行的结果。如图1-3所示,在复制之前目录中只有编译好的程序以及它的源码文件。spiraldoxubuntu:/programe/os/l/copy$Is copy copy.cspiraldoxubuntu:/programe/os/l/copy$图1-3运行程序之前如图1-4所示,在运行程序之后,目录中复制出了一个paste。spiraldoxubuntu:/programe/os/l/copy$Iscopy copy.cspiraldoxubuntu:/programe/os/l/copy$./copy copy.c paste.cspiraldoxubuntu:/programe/os/l/copy$Iscopy copy.c paste.cspiraldoxubuntu:/programe/os/l/copy$图1-4运行程序之后如图1-5所示,使用diff程序检查源文件和复制出来的新文件,结果显示 两个文件的内容是完全一样的。spiraldoxubuntu:/programe/os/l/copy$Iscopy copy.cspiraldoxubuntu:/programe/os/l/copy$./copy copy.c paste.c spiraldoxubuntu:/programe/os/l/copy$Is copy copy.c paste.cspiraldoxubuntu:/programe/os/l/copy$diff-s copy.c paste.cFiles copy.c and paste.c are identicalspiraldoxubuntu:/programe/os/l/copy$图1-5检查两个文件是否相同如图1-6所示,是copy.c文件的部分内容。5spinaldoxubuntu:/programe/os/l/copy$copy copy.cspinaldoxubuntu:/programe/os/l/copy$spinaldoxubuntu:/programe/os/l/copy$copy copy.c paste.cspiraldoxubuntu:/programe/os/l/copy$Files copy.c and paste.c are identical spiraldoxubuntu:/programe/os/l/copy$./copy copy.c paste.c Isdiff-s copy.c paste.c#include#include#include#include#include/open函数mode_t 定义/open。函数符号/close()函数cat copy.cint main(int argc,char*argv)int filein;int fileout;mode_t filein_attrib;mode_t fileout_attrib;char*buffer256;图1-6 copy.c文件的部分内容1.7.2.实验二如图1-7所示,projmain程序是“主控程序”,是用来启动三个进程来执 行另外的三个窗口程序的;prod程序会启动另外四个进程来进行不同的运算,而这四个进程里执行的程序都是proc_l;proc2程序在运行时会显示一张卡通图 片;proc3程序是一个电子时钟,可以动态显示当前世时间。prod proc2 proc3 proc_1 proc_main图1-7编译完成的程序如图1-8所示,双击proc_main程序之后,立马显示出了三个窗口程序各 自创建的窗口。可以看到,现在最右边的procl程序显示的窗口内还没有任何内 容。图1-8双击“主控程序”后显示的三个窗口如图1-9所示,在点击最右边窗口中的“Start”按钮后,窗口的界面上开 始显示内容。可以看到,这些显示的数字每隔1秒钟就自增一个固定值。6O Q DialogProc 1 14 Proc 2 28Proc 3 42 Proc 4 56Start Stop图1-9 procl程序开始进行运算如图1-10所示,再点击“Stop”按钮之后,prod程序停止了运算操作并 保持了停止时的运算结果。事实上,一旦再次开始运算,程序会重新从0开始计 数。图1-10 procl程序停止运算72.通过编译内核的方式为Linux增加系统调用2/I,实验目的掌握通过编译内核的方法实现系统调用的过程,了解Linux内核的编译方 法和编译过程;掌握系统调用命令如syscall函数的使用方法,并能够编写程序 来测试自己新增加的系统调用。22实验内容通过编译内核方法,增加一个新的系统调用。另编写一个应用程序,使用 新增加的系统调用。(1)内核编译、生成,使用新内核启动;(2)新增系统调用实现:文件拷贝或P、V操作。23实验设计由于我使用的Ubuntu内核版本号是3.19.49,因此为了避免系统现有的软件 与我编译的内核出现兼容性问题,我没有使用老师在PPT中推荐的2.6.6版本的 内核,而是使用了 Ubuntu发布的当前内核的源代码来进行使用。在实验过程中,我先查找关于内核的编译以及下载的资料,然后查找了如 何添加以及使用系统调用的资料,最终完成了整个实验。24实验环境硬件平台: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.025实验步骤首先,我使用命令8sudo apt-get install dpkg-dev来安装dpkg-dev,用来自动解压下载的源码包。接着,我就使用命令sudo apt-get source linux-image-Kuname-rv来获取Ubuntu当前版本的源码包。接着,我使用命令sudo apt-get build-dep linux-image-Kuname-rv来自动安装编译当前源码所需要的各种依赖库文件和开发工具。接下来,我就进 入解压好的源码包路径中,首先,在include/asm-generic/syscall.h文件中添加我 的系统调用的原型,接着,我在arch/x86/syscalls/syscall_64.tbl选择了三个没有 被使用的数字来作为我的系统调用号,然后,我在kernel/sys.c中添加了我所要 实现的三个系统调用的C语言代码。这样,编译代码的准备工作就完成了。在进入代码所在的目录后,我首先使用命令sudo su切换到root身份,接着执行命令make mrproper接着我将我的Ubuntu现有的存在于/usr/src/Linux-headers-3.19.0-generic/文件夹 中的.config配置文件拷贝到当前源码目录,然后执行命令make menuconfig这个时候,在弹出的图形菜单里选择载入刚才拷贝的.config文件,然后保存退出o 接下来就可以开始编译内核了。我先执行命令make来编译内核,等代码编译好之后,我再执行命令make modules来编译模块,然后,我执行命令make install来安装内核,接着,我再执行命令make modules install来安装内核模块。这样,内核以及内核模块就全部到了合适的地方了,这个时候,在我执行update-grub之后,重启电脑,内核编译就完成了。内核编译完成之后,我便利用syscall函数来编写程序测试我添加的系统调 用了。这两个程序分别实现了文件复制和临界资源的使用这两个功能。92.6.调试记录错误1:在我第一次将内核编译完成,重启虚拟机后,并没有成功进入新内核,而 是出现了如图2-1所示的界面。0.705706 ACPI PCC probe failed.Gave up waiting for root device.Common problems:-Boot args(cat/proc/cmdline)-Check rootdelay二(did the system wait long enough?)-Check root=(did the system wait for the right device?)-Missing modules(cat/proc/modules;Is/dev)ALERT!/dev/disk/by-uuid/b85ebdl3-86fe-4bd5-9472-31612f7537c4 does not exist.Dropping to a shell IBusyBox vl.21.1(Ubuntu 1:1.21.0-lubuntul)built-in shell(ash)Enter help for a list of built-in commands.(initramfs)_图2-1错误1界面出错原因1:按照图片上面的提示,出错原因应该是找不到磁盘。由于对于这个错误有些迷糊,我就上网查阅了相关资料,最后没有找到具体的原因,但是找到了解决方法。解决方案1:根据我从网络上查阅得到的资料,解决方法就是首先执行命令sudo grub-install/dev/sda接着,执行命令sudo update-initramfs-u就可以了。在我执行完这两个命令之后,问题也真的解决了。错误2:我编写的文件复制系统调用都无法实现预期的功能。出错原因2:为了定位到错误,我在系统调用中添加了 printk函数来打印一些关键算法 的结果,最后我发现程序在执行写入操作的时候出了错误。在反复检查发现程序 的其他部分没有错误之后,我就去查找了相关资料,最终发现是系统调用在由内 核空间向用户空间写入数据的时候出的问题。解决方案2:10经过查阅资料,我发现要使用fs=get_fs()函数保存当前内存段状态,再使 用set_fs(get_ds)函数来设置当前内存空间为用户空间,然后就可以进行文件的读 写操作了。最后,再使用set_fs(fs)来恢复内存段状态即可。同时,我检查实现P、V操作的系统调用之后,发现它们也存在这样的问题,我就一并将其改正过来了。27实验结果如图2-2所示,是我进入自己编译的内核时的内核信息。spiraldoxubuntu:$uname-r3.19.8-cktl0spiraldoxubuntu:$图2-2新内核的内核信息首先测试使用了复制文件的系统调用的程序。如图2-3所示,当前的文件夹里只 有编译出的可执行文件和它的源代码文件copy.cespiraldoxubuntu:/programe/os/2/test_copy$Is copy copy.cspiraldoxubuntu:/programe/os/2/test_copy$图2-3当前文件夹的文件如图2-4所示,我将源代码文件复制了一份,复制为pastespiraldoxubuntu:/programe/os/2/test_copy$Iscopy copy.cspiraldoxubuntu:/programe/os/2/test_copy$./copy copy.c paste.cspiraldoxubuntu:/programe/os/2/test_copy$Iscopy copy.c paste.cspiraldoxubuntu:/programe/os/2/test_copy$图2-4执行程序后的文件如图2-5所示,我diff命令测试结果表明两个文件相同。spiraldoxubuntu:/programe/os/2/test_copy$Iscopy copy.cspiraldoxubuntu:/programe/os/2/test_copy$./copy copy.c paste.cspiraldoxubuntu:/programe/os/2/test_copy$Iscopy copy.c paste.cspiraldoxubuntu:/programe/os/2/test_copy$diff-s copy.c paste.cFiles copy.c and paste.c are identicalspiraldoxubuntu:/programe/os/2/test_copy$图2-5使用diff测试两个文件是否相同如图2-6所示,是copy.c文件的内容11spiraldoxubuntu:/programe/os/2/test_copy$Is copy copy.cspinaldoxubuntu:/programe/os/2/test_copy$spinaldoxubuntu:/programe/os/2/test_copy$copy copy.c paste.cspinaldoxubuntu:/programe/os/2/test_copy$Files copy.c and paste.c are identicalspiraldoxubuntu:/programe/os/2/test_copy$copy copy.cspiraldoxubuntu:/programe/os/2/test_copy$#include./copy copy.c Isdiff-s copy.cat copycat copy.cpaste,cc paste.cint main(int argc,char*argv)(syscall(325,argvl,argv2);return;)图2-6 cat文件的内容测试完了实现复制功能的系统调用之后,我接着编写了一个双线程交替输出的程 序测试实现P、V操作的系统调用。测试结果如图2-7所示。spinaIdoxubuntu:/programe/os/2/test_PV$./PV threadl working!thread2 working!threadl working!thread2 working!threadl working!thread2 working!图2-7 P、V系统调用测试结果123.编写设备驱动程序3/I,实验目的掌握在Linux下编写、使用简单设备驱动程序的方法。3.2,实验内容通过模块方法,增加一个新的设备驱动程序,其功能可以简单。要求实现 字符设备的驱动。33实验设计为了进行本次实验,我首先查阅了各种资料,对Linux的字符驱动程序形 成了一个基本的认识,才开始进行模块程序的编写的。在本次实验中,我设计实现了一个拥有1024 Byte内存缓冲区的字符设备模 块,使之可以存储写入的数据,也允许程序从中读取数据,也可以让程序利用 llseek在其中进行随机读写,还允许程序使用ioctl函数清除缓冲区中的内容。为了让模块实现预定的功能,我实现了一个字符设备驱动所要求的6个函 数,它们分别是:llseek ioctl read、write open、releaseo 其中,llseek 让用 户能够在模块的缓冲区中寻址进行随机读写;ioctl让用户可以清除字符设备模块 缓冲区中的内容,使之以0填充;read让用户可以从模块缓冲区中读取指定字节 的数据;write让用户可以向缓冲区中写入指定字节的数据;open函数在用户打 开字符设备的时候被调用;release函数在用户使用close函数关闭设备的时候被 调用。当然,在字符设备驱动中,实现上面那些功能的函数并不是一定叫那些名 字。这些函数的名字都可以随便起,只要在设备初始化的时候将适当的函数地址 填充到一个file_operations的结构体中,并注册设备就可以了。在设备的初始阶段,我首先直接使用alloc_chdev_region函数来动态申请设 备号,接着使用kmalloc函数为我的设备结构体申请内存,接着我使用cdev_init 函数与之前填充好的file_operations结构体的指针来初始化本设备的cdev结构,再使用cdev_add函数将本设备加入到内核中。接着,我使用了 class_create与 device_create函数在/dev目录下创建字符设备驱动程序的节点。在open函数与release函数中,所做的操作就仅仅是将当前字符设备的引用 13计数,也就是打开此设备的程序的数目进行了改变。就是说,每open一次设备,引用计数就加1,每close一次设备,引用计数就减1。我所实现的read与write函数,功能就是根据当前读写指针所在的位置以 及传入的要读取或写入的字节数,向用户空间的缓冲区复制数据或者从用户空间 的缓冲区中加载数据。这些函数在算法上并没有太多困难的地方,不过为了正确 进行数据交换的操作,在向用户空间写入数据的时候;需要使用copy_to_user函 数,而在从用户空间复制数据的时候,需要使用copy_from_user函数来进行。而对于llseek函数,作用就是设置当前的读写指针,也就是函数传入参数 中filp结构体的f_pos成员。具体的实现就是要求读写指针所在的位置要大于等 于。,小于缓冲区的最大长度1024。对于ioctl函数,我实现的版本只起到清空字符设备缓冲区的功能,它是使 用了 memset函数来实现清空字符设备缓冲区的功能的。3.4.实验环境在进行本实验的时候,我使用了我在第二个实验中编译的内核。因此本次 实验以及后续的实验中,我的实验环境中的内核都是我编译的新内核。我的实验环境如下:硬件平台:VMware虚拟机,2G内存,2个CPU核心,40G虚拟硬盘空间操作系统:64 位 Ubuntu 14.04.3,内核版本 3.19.8-ctklO编译器:64位GCC4.8.4文本编辑器:emacs 24.5.135实验步骤首先,我编写了要实现的设备驱动程序的源代码;接着,我编写了用于编 译本模块的Makefile,在Makefile中,需要指定编译该模块所用的Linux内核头 文件所在路径,我就指定了我的新内核的源码所在的目录。在模块源码所在目录中,我执行make命令就可以编译出模块了;接着我使用sudo insmod linux_mod.ko命令来将我编译的模块安装到系统中。模块安装到系统中后,就可以在/dev 目录下看到我编写的模块了。14接着,我编写了两个程序,一个程序是向设备驱动中写入一个字符串,同 时在llseek后,再从设备驱动中读取一个字符串;另一个程序的功能是从设备驱 动中读取一个字符串,再使用ioctl清空设备驱动的缓冲区中,llsek到缓冲区的 开头,再读取一次。这么设计的目的是看设备驱动的读取、写入、寻址与清空功 能是不是可以正常工作。3.6.调试记录错误L在编译设备驱动时,编译器提示file_operations结构体中不存在ioctl成员。错误原因1:经过查阅资料,我得知在我所编译的内核版本中,file_operations结构体中 的ioctl成员已经被废弃了,转而被unlocked_ioctl取代。解决方案1:我将我所编写的ioctl函数的地址赋值给file_operations的unlocked_ioctl成 员,而不是ioctl成员。这样,编译就通过了。并且在后续测试的时候ioctl的功 能也是可以正常使用的37实验结果如图3-1所示,myMod-0是我使用sudo insmod linux_mod.ko命令安装模块后,模块出现在/dev目录下的设备节点。spiraldoxubuntu:/programe/os/3/module$Is/dev Iagpgartloop0ramlOsndtty27tty50ttyS15uinputautofslooplramllsr0tty28tty51ttyS16urandomblockloop2raml2stderrtty29tty52ttyS17vcsbsgloop3raml3stdintty3tty53ttyS18vcslbtrfs-controlloop4raml4stdouttty30tty54ttyS19vcs2busloop5raml5ttytty31tty55ttyS2vcs3cdromloop6ram2tty0tty32tty56ttyS20vcs4charloop?ram3ttyltty33tty57ttyS21vcs5consoleloop-controlram4ttyl0tty34tty58ttyS22vcs6coremapperram5ttylltty35tty59ttyS23vcs7cpumcelogram6ttyl2tty36tty6ttyS24vcsacpu_dma_latencymemram7ttyl3tty37tty60ttyS25vcsalcusememory_bandwidthram8ttyl4tty38tty61ttyS26vcsa2diskmidiram9ttyl5tty39tty62ttyS27vcsa3dmmidimyMod-0randomttyl6ESIttyS28vcsa4图3-1设备驱动在/dev中的节点如图3-2所示,first程序先向设备驱动中写入在命令行中给定的字符串,再将文 件读写指针置0位置,接着重新读取刚才写入的字符串,再输出到屏幕上。由结 15果可以看到,程序与设备驱动很好地相配合,完成了预定的功能。spinaldoxubuntu:/programe/os/3/test_module$sudo./first abcdefghijklmnopqrstuvwxyzl234567890123 open device success向字符设备中写入数据.将读写指针置0.读取刚才写入的字符串.字符串的结果是:*abcdefghijklmnopqrstuvwxyzl234567890123图3-2 first程序执行结果如图3-3所示,second程序先从设备驱动的缓冲区中取出刚刚被first程序写入 的字符串,将其输出出来,接着将设备驱动的缓冲区置3然后将读写指针置3 再从设备驱动中读取数据,再将结果输出。可以看到,second程序也完美完成了 预定的功能。图3-3 second程序执行结果164.实现系统监控程序4.1.实验目的了解/proc文件的特定和使用方法,了解如何从/proc文件中获取进程与系统 信息、计算机运行状态,同时加深对Linux环境下图形用户界面程序设计的理解。4.2.实验内容通过读取/proc文件系统,获取系统各种信息,并以比较容易理解的方式显 示出来。要求使用GTK+/Qt下的C语言进行开发,具体要求包括:1、获取并显示主机名2、获取并显示系统启动的时I可3、显示系统到目前为止持续运行的时间4、显示系统的版本号5、显示CPU的型号和主频大小6、通过pid或者进程名查询一个进程,并显示该进程的详细信息,提供杀掉该进程的功能7、显示系统所有进程的一些信息,包括pid、ppid、占用内存大小、优先级等等8、CPU使用率的图形化显示(2分钟内的历史记录曲线)9、内存和交换分区(swap)使用率的图形化显示(2分钟内的历史记录曲线)10、在状态栏显示当前时间11、在状态栏显示当前CPU使用率12、在状态栏显示当前内存使用情况13、用新进程运行一个其他程序14、关机功能参照WINDOWS的任务管理器,实现其中的几个功能。1743实验设计出于简单起见,我根据Ubuntu下已有的system monitor程序设计了我的程 序的用户界面。即,将程序界面分为两个部分:第一个部分只显示进程信息以及 只进行与进程有关的操作(比如创建进程、结束进程等);第二个部分显示系统 相关信息。当然,无论在哪个界面,程序底部的状态栏中都会显示CPU利用率、内存使用率和当前时间。首先说与进程有关的界面。在本界面中,会显示所有进程的进程名、所属用户名、进程ID、CPU使用 率、内存占用与优先级。并且,在界面下部还提供了按钮来实现按pid或进程名 来查找某进程详细信息、查看在信息列表中选中进程的详细信息、按pid或进程 名结束进程以及以新进程运行命令行程序的功能。进程信息的列表刷新函数使用 QTimer来计时,保证每隔一段时间来刷新一次。在这里,我参考了系统自带的 monitor,将计时器的超时时间设置为3秒。在proc文件系统中,所有以纯数字命名的文件夹,其文件名都是其所对应 进程的pid,而文件夹中存储的就是该pid所代表进程的详细信息。而其中这么 多信息中,所用到的文件只有两个:stat文件与status文件。在stat文件中,存储有进程的pid、进程名、进程的优先级与进程在用户态 与和心态运行时占用的CPU时间。其中,pid与进程名信息是可以直接用于信息 的显示的,而优先级与进程的CPU占用率则是需要进行一定的计算或者转换以 后才能作为可视化的信息呈现出来。在Linux系统中,进程的优先级是分为静态优先级和动态优先级的。在这 里,为方便起见,我只取进程的静态优先级来显示。进程的静态优先级是一个范 围为-20到20的整数,其中,-20表示进程的优先级最高,20表示进程的优先级 最低。为了显示直观,我将-20到-17之间的优先级定义为“Very High,-16到-10之间的优先级定义为“High”,-9到9之间的优先级定义为“Normal”,10到 16之间的优先级定义为“Low”,17到20之间的优先级定义为“Very Low”。在 取得进程的优先级之后,我就使用些字符串将进程的优先级以可读的方式表示出 来。取得进程CPU使用率就相对比较麻烦了。在Linux系统中,进程的CPU 使用率等于进程CPU时间除以进程运行时间得到的百分比。其中,进程CPU时 间可以从该进程在proc文件系统中的stat文件中读取,就是其utime(进程在用 户态运行时间)字段与stime(进程在核心态运行时间)字段相加得到。而进程 运行时间则是等于系统运行时间减去进程启动时间。其中,系统运行时间需要从18/proc/uptime中获取,而进程启动时间则也可以从该进程的stat文件中获取。进程的所属用户与内存占用这两个信息需要从该进程的status文件中获取。其中,为了获取进程的用户信息、。需要现充status文件中获取进程所属用户的uid,再使用定义在pwd.h头文件中的getpwuid函数来获取该uid对应的用户名即可。而至于进程的内存占用信息,则是直接取status文件中的VmRSS(进程在内存 中的驻留大小),并将数值除以1024 KB得到的。在进程界面底部的按钮中,实现按pid或进程名查找指定进程和查看进程 详细信息的按钮在被按下后,实现的功能是一样的,只是它们的信息来源不一样 而已。为了实现按pid或进程名查找指定进程,我在程序中维护了两个QVector,这两个QVector分别存储了系统中所有进程的pid和进程名,并且这两个QVector 中的元素是一一对应的。这两个QVector的数据是在程序获取并显示所有进程信 息的时候更新的。当用户输入pid后,程序就直接根据pid查找该进程的status 文件,并将其显示出来;而若用户输入的是进程名,程序就根据这两个QVector 中的内容来查找指定进程名的pid,再根据pid显示该进程的status文件内容。当点击“终止进程运行”按钮后,会弹出一个对话框,让用户选择输入要 终止运行的进程的pid或者用户名。其中,查找进程pid的方法与上面的按pid 或进程名查找指定进程的功能是一样的。在找到pid后,程序中使用了 system命 令来执行sudo kill-9$pid,以向指定进程发送exit信号,使其结束运行。为了可 以在不输入密码的情况下执行sudo命令,我向/etc/sudoers文件中的行%admin ALL=(ALL)ALL下面一行中写入了如下语句:spiraldoxALL-NOPASSWD:ALL其中,spiraldox是我的ubuntu中的用户名。这样,我就可以在不输入密码的情 况下使用sudo命令了。也就可以在任务管理器中结束任意进程了。当点击“创建新进程”按钮时,程序也是在fork出的新进程中使用system 命令来执行指定的程序。不过在创建新进程的过程中,我没有使用sudo命令,因此创建的进程是以当前用户身份运行的。接下来要介绍的就是与系统信息相关的界面。在这里我将把状态栏中的项 目于这个界面放在一起进行介绍。在与系统信息相关的界面中,显示了主机名、系统的启动时间、系统持续 运行的时间、系统的内核版本、CPU信息、CPU使用率的图形曲线、内存与交 换分区使用率的图形曲线、内存总量信息、swap总量信息、已经使用的内存大 小信息、已经使用的swap大小信息以及一个关闭计算机的按钮。
展开阅读全文