课 程 设 计 报 告
课程名称: 计算机操作系统
专业班级:
学 号: 姓 名: 指导教师: 报告日期:
计算机科学与技术学院
华 中 科 技 大 学 课 程 设 计 报 告
目 录
1 2 3 实验目的 ..............................................................................................2 实验环境 ..............................................................................................2 实验内容 ..............................................................................................2 3.1 3.2 3.3 3.4 3.5 4 实验一 ...............................................................................................2 实验二 ...............................................................................................2 实验三 ...............................................................................................2 实验四(选做) ................................................................................3 实验五(选做) ................................................................................3
设计与实现 ...........................................................................................3 4.1 4.2 4.3 4.4 实验一 ...............................................................................................3 实验二 .............................................................................................10 实验三 .............................................................................................14 实验四 .............................................................................................20
5 心得体会 ............................................................................................43
I
华 中 科 技 大 学 课 程 设 计 报 告
1 ·掌握Linux操作系统的使用方法; ·了解Linux系统内核代码结构; ·掌握实例操作系统的实现方法;
实验目的
2 实验环境
本次课程设计采用的操作系统环境是windows
8、Ubuntu双系统,Ubuntu系统版本号为14.04,内核版本号为linux 3.13.0;采用的编程环境为CodeBlocks IDE和QtCreator。
3 3.1 实验一
实验内容
掌握Linux操作系统的使用方法,包括键盘命令、系统调用;掌握在Linux下的编程环境。
(1)编写一个C程序,其内容为实现文件拷贝的功能。
(2)编写一个C程序,其内容为分窗口同时显示三个并发进程的运行结果。要求用到Linux下的图形库(GTK/Qt)。
3.2 实验二
掌握系统调用的实现过程,通过编译内核方法,增加一个新的系统调用,另编写一个应用程序,调用新增加的系统调用。实现的功能是:文件拷贝。
3.3 实验三
掌握增加设备驱动程序的方法。通过模块方法,增加一个新的设备驱动程序,其功能可以简单。(实现字符设备的驱动)
华 中 科 技 大 学 课 程 设 计 报 告
3.4 实验四(选做)
了解和掌握/proc文件系统的特点和使用方法 (1)了解/proc文件的特点和使用方法;
(2)监控系统状态,显示系统中若干部件使用状态; (3)用图形界面实现系统监控状态;
3.5 实验五(选做)
设计并实现一个模拟的文件系统。
多用户的多级目录的文件系统设计。多用户、多级目录、login(用户登录)、系统初始化(建文件卷,提供登录模块)、文件的创建、文件的打开、文件的读写、文件关闭、删除文件、创建目录(建立子目录)、改变当前目录、列出文件目录、退出。
4 4.1 实验一
4.1.1 实验要求
设计与实现
掌握Linux操作系统的使用方法,包括键盘命令、系统调用;掌握在Linux下的编程环境。 4.1.2 具体实现
本实验内容是用CodeBlocks IDE实现的,该软件整合了函数库和编译器,因此使用起来非常方便。
(1)编写一个C程序,其内容为实现文件拷贝的功能。
在windows操作系统上实现的文件拷贝功能一般使用fopen、fread、fwrite三个来自标准C函数库的函数执行对文件的打开、读、写操作,而本次实验要求使用Linux系统的系统调用open、read、write实现上述三个操作。
用到的主要头文件如下:
华 中 科 技 大 学 课 程 设 计 报 告
stdio.h——标准输入输出头文件 string.h——字符串处理相关头文件
unistd.h——Linux系统调用头文件,比如read、write fcntl.h——包含open系统调用
errno.h——包含一些调试错误时用到的变量 具体实现思路:
打开两个文件(分别是源文件和目标文件,可以是任意字符流形式存储的文件,包括文本文件、照片等),调用read函数读取源文件的内容,将read的返回值作为while循环的判断条件,当返回值大于0(即还未读取完毕源文件中的内容)时,调用write执行向目标文件写的操作,否则跳出循环,表示源文件已经被拷贝到目标文件,然后调用close关闭源文件和目标文件。
代码编写完成后,在CodeBlocks上编译运行即可。程序运行之前,桌面上只有“教程.docx”,运行之后,桌面上新建了“教程副本.docx”,并且“教程.docx”中的内容被复制到了“教程副本.docx”,程序运行结果如下所示:
详细代码见4.1.3。
(2)编写一个C程序,其内容为分窗口同时显示三个并发进程的运行结果。要求用到Linux下的图形库(GTK/Qt)。
华 中 科 技 大 学 课 程 设 计 报 告
本次实验使用的图形库是跨平台的开发工具Qt。首先下载Qt的安装包并安装。 Qt安装完之后,先新建一个Qt控制台应用MAIN作为主进程,用于调用三个并发的子进程。在主进程的main函数中,使用fork创建三个子进程,若进程创建成功(即fork函数返回值等于0),则使用execv函数进入对应的子进程(get、copy、put)。 主进程程序编写完成后,再新建三个Qt Widgets Application,分别作为三个子进程get、copy、put(所实现的功能并不是拷贝)。由于三个子进程窗口显示的内容形式一模一样,所以以子进程get为例。get进程的窗口显示了一下四个内容:当前时间、子进程名称、子进程的pid和父进程MAIN的pid。用Qt的对象QDateTime获取系统当前时间,然后将时间转换成一个字符串写在一个QLabel类的实例中,然后将该实例添加至窗口;直接把当前进程名称写在一个标签上然后添加至窗口;使用getpid和getppid函数分别获取当前进程号和父进程号,然后调用sprintf把进程号转换成字符串类型之后写在标签上并添加至窗口即可。
主进程和三个子进程的程序全部编写完后,直接在Qt上编译运行。程序运行结果如下所示:
华 中 科 技 大 学 课 程 设 计 报 告
详细代码见4.1.3。
华 中 科 技 大 学 课 程 设 计 报 告
4.1.3 源代码 (1) 文件拷贝源代码
#include #include #include #include #include #include #include
#define SIZE 10
///每次读取的字符数目
char * srcFile=\"/home/ilbear/桌面/教程.docx\"; char *goalFile=\"/home/ilbear/桌面/教程副本.docx\";
int main(int argc, const char *argv[]) {
int src, goal;
int read_len;
char buff[SIZE];
src=open(srcFile,O_RDONLY);
if(src
{
printf(\"Fail to open %s\\n.\",srcFile);
exit(1);
}
goal=open(goalFile,O_WRONLY|O_CREAT,0666);
if(goal
{
printf(\"Fail to open %s\\n.\",goalFile);
exit(1);
}
while((read_len=read(src,buff,SIZE))>0)
{
write(goal,buff,read_len);
}
close(src);
close(goal);
return 0; } (2) 三个并发进程
#主进程MAIN #include #include #include #include #include #include int main(int argc, char *argv[])
华 中 科 技 大 学 课 程 设 计 报 告
{
QCoreApplication a(argc, argv);
pid_t p1,p2,p3;
if((p1=fork())==0)
{
execv(\"/home/ilbear/桌面/build-get-Desktop_Qt_5_4_1_GCC_64bit-Debug/get\",NULL);
}
else
{
if((p2=fork())==0)
{
execv(\"/home/ilbear/桌面/build-copy-Desktop_Qt_5_4_1_GCC_64bit-Debug/copy\",NULL);
}
else
{
if(( p3=fork())==0)
{
execv(\"/home/ilbear/桌面/build-put-Desktop_Qt_5_4_1_GCC_64bit-Debug/put\",NULL);
}
}
}
waitpid(p1,NULL,0);
waitpid(p2,NULL,0);
waitpid(p3,NULL,0);
return a.exec(); }
#子进程get mainwindow.cpp #include \"mainwindow.h\" #include \"ui_mainwindow.h\" #include #include #include #include MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),sharememory1(\"share1\") {
ui->setupUi(this);
setWindowTitle(\"get\");
setWindowFlags(Qt::Dialog);
move(0,0);
resize(500,500);
char str[128],f_id[128];
sprintf(str,\"%d\",getpid());
sprintf(f_id,\"%d\",getppid());
ui->textBrowser->setText(\"get\");
ui->textBrowser_2->setText(str);
ui->textBrowser_3->setText(f_id);
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(timerUpDate()));
华 中 科 技 大 学 课 程 设 计 报 告
timer->start(1); } MainWindow::~MainWindow() {
delete ui; } void MainWindow::timerUpDate() {
QDateTime time = QDateTime::currentDateTime();
QString str = time.toString(\"yyyy-MM-dd hh:mm: dddd\");
ui->labelCurDate->setText(str); }
#子进程copy mainwindow.cpp #include \"mainwindow.h\" #include \"ui_mainwindow.h\" #include #include #include #include MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),sharememory1(\"share1\"),sharememory2(\"share2\") {
char str[128],f_id[128];
ui->setupUi(this);
setWindowTitle(\"copy\");
setWindowFlags(Qt::Dialog);
move(500,500);
resize(500,500);
sprintf(str,\"%d\",getpid());
sprintf(f_id,\"%d\",getppid());
ui->textBrowser->setText(\"copy\");
ui->textBrowser_2->setText(str);
ui->textBrowser_3->setText(f_id);
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(timerUpDate()));
timer->start(1); } MainWindow::~MainWindow() {
delete ui; } void MainWindow::timerUpDate() {
QDateTime time = QDateTime::currentDateTime();
QString str = time.toString(\"yyyy-MM-dd hh:mm: dddd\");
ui->labelCurDate->setText(str); }
#子进程put mainwindow.cpp #include \"mainwindow.h\" #include \"ui_mainwindow.h\"
华 中 科 技 大 学 课 程 设 计 报 告
#include #include #include #include MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),sharememory2(\"share2\") {
char str[128],f_id[128];
ui->setupUi(this);
setWindowTitle(\"put\");
setWindowFlags(Qt::Dialog);
move(1000,0);
resize(500,500);
sprintf(str,\"%d\",getpid());
sprintf(f_id,\"%d\",getppid());
ui->textBrowser->setText(\"put\");
ui->textBrowser_2->setText(str);
ui->textBrowser_3->setText(f_id);
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(timerUpDate()));
timer->start(1); } MainWindow::~MainWindow() {
delete ui; } void MainWindow::timerUpDate() {
QDateTime time = QDateTime::currentDateTime();
QString str = time.toString(\"yyyy-MM-dd hh:mm: dddd\");
ui->labelCurDate->setText(str); } 4.2 实验二
4.2.1 实验要求
掌握系统调用的实现过程,通过编译内核方法,增加一个新的系统调用,另编写一个应用程序,调用新增加的系统调用。 4.2.2 具体实现 (1) 系统调用的原理
用户进程不能访问内核所占内存空间,也不能调用内核函数。进程调用一个特殊的指令,这个指令会跳到一个事先定义的内核中的一个位置。在Intel CPU中,由中断INT 0x80实现。 (与DOS功能调用int0x21很相似)跳转到的内核位置叫做sysem_call。检查系统调用号,这个号码代表进程请求哪种服务。然后,它查看系统
华 中 科 技 大 学 课 程 设 计 报 告
调用表(sys_call_table)找到所调用的内核函数入口地址。接着,就调用函数,等返回后,做一些系统检查,最后返回到进程(如果这个进程时间用尽,就返回到其他进程)。
(2) 编写新的系统调用程序
新的系统调用程序实现的功能是:将一个文件中的内容拷贝到另一个文件中。这个系统调用的参数是两个char*型的字符指针SourceFile、GoalFile,分别表示源文件和目标文件的路径名。用户进程中的open、read、write、close函数此时对应内核函数 sys_open、sys_read、sys_write、sys_close函数。循环拷贝的判断条件还是
sys_read的返回值,当其大于0的时候执行循环,否则表示源文件已拷贝到了目标文件。
mm_segment_t类型的变量fs的作用是在读写文件前得到当前fs,避免使用的缓冲区超过了用户空间的地址范围而报错。
详细代码见4.2.3。 (3) 编译内核 ①下载并解压内核
先到Linux官方网站http://www.daodoc.com/下载内核linux-3.14.36.tar.xz。打开终端,使用sudo su获取root权限,然后使用cp linux-3.14.36.tar.xz /usr/src命令将linux-3.14.36.tar.xz复制到文件夹/usr/src下,复制完毕之后将其解压,用到的命令为:xz -d linux-3.14.36.tar.xz和tar xvf linux-3.14.36.tar。 ②修改内核
新的内核解压完毕后,使用 cd /usr/src/linux-3.14.36命令进入目录 /usr/src/linux-3.14.36。然后使用命令sudo gedit kernel/sys.c打开sys.c,将新的系统调用程序复制到该文件的文件末尾,保存退出,系统调用程序详细代码见4.2.3。
使用命令sudo gedit arch/x86/syscalls/syscall_64.tbl 打开 syscall_64.tbl添加系统调用号。在该文件中添加一行内容317 common mycall sys_mycall,其中系统调用号317不是固定的,只要该文件中没有出现的数字都可以使用。添加之后保存退出。 使用命令sudo gedit include/asm-generic/syscalls.h打开syscalls.h,在“#endif /* __ASM_GENERIC_SYSCALLS_H */ 这一行的上面一行添加新的系统调用程序的函数定义,即:
#ifndef sys_mycall
华 中 科 技 大 学 课 程 设 计 报 告
asmlinkage int sys_mycall(char* sourceFile,char* destFile); #endif 然后保存退出。 ③编译内核
在编译内核之前先要安装ncurses库,使用命令sudo apt-get install libncurses5-dev安装。安装完毕后,进入 /usr/src/linux-3.14.36目录下,新建一个脚本文件mysyscall.sh,通过命令gedit mysyscall.sh打开该脚本文件进行编辑。将以下内容添加到脚本中: #!/bin/bash
make mrproper
make menuconfig
make dep make clean
make bzImage –j4 make modules –j4 make modules_install –j4 make install –j4
mkinitramfs -o /boot/initrd.img-3.14.34
update-grub
reboot 保存退出,然后修改脚本文件的权限,使其可以对内核文件进行操作,修改权限的命令为chmod 777 mysyscall.sh。权限修改成功后,在终端中运行该脚本文件./mysyscall.sh,内核开始编译,期间会出现配置linux过程,直接先save,然后OK,再exit即可,继续等待编译结束。编译完成后,电脑会自动重启,重启选择进入Ubuntu高级选项,在选项列表中选择新内核linux-3.14.36进入,打开终端输入uname -a查看系统版本号,执行情况如下所示:
说明已经成功进入新的内核linux-3.14.36中。 (4) 编写系统调用测试程序
需要用到的头文件是syscall.h、unistd.h、stdlib.h。在main函数中直接调用头文
华 中 科 技 大 学 课 程 设 计 报 告
件syscall.h中定义的函数syscall,该函数有三个参数,第一个参数是系统调用号(317),第二个参数是源文件(main.c,即测试程序的源代码文件),第三个参数是目标文件(yyk.text)。
程序运行结果为:在main.c所在目录下新建了一个yyk.text文件,并将main.c中的代码拷贝到了yyk.text中。详细代码见4.2.3。 4.2.3 源代码 (1) 系统调用程序
asmlinkage int sys_mycall(char* SourceFile,char* GoalFile) {
int source=sys_open(SourceFile,O_RDONLY,0);
int goal=sys_open(GoalFile,O_WRONLY|O_CREAT|O_TRUNC,0600);
char buff[4096];
mm_segment_t fs;
fs = get_fs();
set_fs(get_ds());
int i;
if(source>0 && goal>0)
{
do
{
i=sys_read(source,buff,4096);
sys_write(goal,buff,i);
}
while(i);
}
else
{
printk(\"Error!\");
}
sys_close(source);
sys_close(goal);
set_fs(fs);
return 10; } (2) 测试程序
#include #include #include
int main() {
syscall(317,\"main.c\",\"yyk.text\");
return 0; }
华 中 科 技 大 学 课 程 设 计 报 告
4.3 实验三
4.3.1 实验要求
掌握增加设备驱动程序的方法。通过模块方法,增加一个新的设备驱动程序,其功能可以简单。(实现字符设备的驱动) 4.3.2 具体实现
(1)Linux核心是一种monolithic类型的内核,即单一的大核心,另外一种形式是MicroKernel,核心的所有功能部件都被拆成独立部分, 这些部分之间通过严格的通讯机制进行联系。Linux内核是一个整体结构,因此向内核添加任何东西.或者删除某些功能,都十分困难 。为了解决这个问题,引入了模块机制,从而可以动态的在内核中添加或者删除模块。模块一旦被插入内核,就和内核其他部分一样。 Linux内核中的设备驱动程序是一组常驻内存的具有特权的共享库,是低级硬件处理例程。对用户程序而言,设备驱动程序隐藏了设备的具体细节, 对各种不同设备提供了一致的接口,一般来说是把设备映射为一个特殊的设备文 件,用户程序可以像对其它文件一样对此设备文件进行操作。
Linux支持3种设备:字符设备、块设备和网络设备。设备由一个主设备号和一个次设备号标识。主设备号唯一标识了设备类型, 即设备驱动程序类型,它是块设备表或字符设备表中设备表项的索引。次设备号仅由设备驱动程序解释 ,一般用于识别在若干可能的硬件设备中,I/O请求所涉及到的那个设备。
典型的Linux模块实现机制有如下几步:
①注册设备:在系统初启或者加载模块的时候,必须将设备登记到相应的设备数组,并返回主设备号。
②定义功能函数:对于每一个驱动函数来说,都有一些和此设备密切相关的功能函数。以最常用的块设备或者字符设备来说,都存在着诸如 open()、read()这一类的操作。当系统调用这些调用时,将自动的使用驱动函数中特定的模块来实现具体的操作。
③卸载设备:在不用这个设备时,可以将它卸载,主要是从/proc 中取消这个设备的特殊文件。
华 中 科 技 大 学 课 程 设 计 报 告
(2) 编写Makefile文件
Makefile文件用于编译设备驱动程序,其内容如下:
ifneq ($(KERNELRELEASE),)
#kbuild syntax.#模块的文件组成
mymodule-objs :=MyDeviceDriver.o
#生成的模块文件名
obj-m := MyDeviceDriver.o
else PWD :=$(shell pwd) KVER :=$(shell uname -r) KDIR :=/lib/modules/$(KVER)/build all: $(MAKE) -C $(KDIR) M=$(PWD) clean: # rm -f *.cmd *.o *.mod *.ko rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions # $(MAKE) -C $(KDIR) M=$(PWD) clean endif (3) 编写设备功能函数
编写设备驱动程序的主要工作就是编写子功能函数,并填充file_operations的各个域 。
结构体file_operations的具体定义如下:
struct file_operations {
struct module *owner;//拥有该结构的模块的指针,一般为THIS_MODULES
loff_t (*llseek) (struct file *, loff_t, int);//用来修改文件当前的读写位置
ize_t (*read) (struct file *, char __user *, size_t, loff_t *);//从设备中同步读取数据
ize_t (*write) (struct file *, const char __user *, size_t, loff_t *);//向设备发送数据
ize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//初始化一个异步的读取操作
ize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);//初始化一个异步的写入操作
int (*readdir) (struct file *, void *, filldir_t);//仅用于读取目录,对于设备文件,该字段为NULL
unsigned int (*poll) (struct file *, struct poll_table_struct *); //轮询函数,判断目前是否可以进行非阻塞的读写或写入
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); //执行设备I/O控制命令
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); //不使用BLK文件系统,将使用此种函数指针代替ioctl
long (*compat_ioctl) (struct file *, unsigned int, unsigned long); //在64位系统上,32位的ioctl调用将使用此函数指针代替
int (*mmap) (struct file *, struct vm_area_struct *); //用于请求将设备内存映射到进程地址空间
int (*open) (struct inode *, struct file *); //打开
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *); //关闭
int (*fsync) (struct file *, struct dentry *, int datasync); //刷新待处理的数据
int (*aio_fsync) (struct kiocb *, int datasync); //异步刷新待处理的数据
华 中 科 技 大 学 课 程 设 计 报 告
int (*fasync) (int, struct file *, int); //通知设备FASYNC标志发生变化
int (*lock) (struct file *, int, struct file_lock *);
ize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **); }; 子功能函数详细代码见4.4.3。 (4) 设备驱动程序安装
① make clean,清除make产生的残留
② make,重新调用Makefile编译设备驱动程序
③ insmod -f MyDeviceDriver.ko,加载生成的 MyDeviceDriver.ko模块 ④ cat /proc/devices,获取设备驱动程序的主设备号
⑤mknod /dev/MyDeviceDriver c 250 0,创建设备文件,250为主设备号,0为从设备号
(5) 测试驱动程序
此设备驱动程序实现的功能是将一个字符串中内容拷贝到另外一个字符串中。测试程序编写完成后,在终端输入gcc test.c -o test进行编译。测试结果如下:
详细代码见4.3.3。 4.3.3 源代码
(1) 设备驱动程序
#include
华 中 科 技 大 学 课 程 设 计 报 告
#include #include #include #include #include #include #include
#define DEV_SIZE 102400 //设备申请的最大内存空间
struct Device {
char *data;//数组指针,用于存放从用户读入的数据
long size;//存储的数据长度 }*devp; struct cdev cdev;//字符设备结构体
static int devNum_major = 0;//主设备号变量
//对应用户态的lseek static loff_t my_llseek(struct file *file,loff_t offset, int whence) {
loff_t cfo=0;//文件当前偏移量,current file offset
switch(whence){
case 0://SEEK_SET
cfo=offset;break; case 1://SEEK_CUR
cfo=file->f_pos+offset;break; case 2://SEEK_END
cfo=DEV_SIZE-1+offset;break;
}
if ((cfoDEV_SIZE))//文件偏移量越界
return -EINVAL;
file->f_pos = cfo;
return cfo; }
//对应用户态的read,写数据到用户空间
static ize_t my_read(struct file *file, char __user *buf, size_t count, loff_t *p_cfo) {
int val = 0;
struct Device *dev = file->private_data; //设备描述结构体指针,获取设备信息
if (copy_to_user(buf, (void*)(dev->data + *p_cfo), count))//如果成功返回0;如果失败,返回未完成copy的长度
华 中 科 技 大 学 课 程 设 计 报 告
val = -EFAULT;
else
{
*p_cfo += count;//copy成功,文件偏移量加上count
val = count;
}
return val; }
//对应用户态的write,从用户空间读入数据
static ize_t my_write(struct file *file, const char __user *buf, size_t count, loff_t *p_cfo) {
int val = 0;
struct Device *dev = file->private_data;
if (copy_from_user(dev->data + *p_cfo, buf, count))//如果成功返回0;如果失败,返回未完成copy的长度
val = -EFAULT;
else
{
*p_cfo += count;//copy成功,文件偏移量加上count
val = count;
}
return val; }
static int my_open(struct inode *inode, struct file *file) {
file->private_data = devp;
return 0; }
struct file_operations fops={
.owner = THIS_MODULE,
.llseek = my_llseek,
.read = my_read,
.write = my_write,
.open = my_open, };
int init_module(void) { int dev_num; dev_num = register_chrdev(0,\"MyDeviceDriver\",&fops); if (dev_num
华 中 科 技 大 学 课 程 设 计 报 告
printk(KERN_INFO \"MyDeviceDriver: FAIL to get major number\\n\"); return dev_num; } if (devNum_major == 0) devNum_major = dev_num;
//初始化cdev结构
cdev_init(&cdev, &fops);
cdev.owner = THIS_MODULE;
cdev.ops = &fops;
cdev_add(&cdev,MKDEV(devNum_major, 0), 1);//注册1个字符设备
//为设备描述结构体分配内存
devp = kmalloc(sizeof(struct Device), GFP_KERNEL);
if (!devp)
{
dev_num = -ENOMEM;
printk(KERN_INFO \"MyDeviceDriver: FAIL to get memory\\n\");
return dev_num;
}
(*devp).size = DEV_SIZE;
(*devp).data = kmalloc(DEV_SIZE, GFP_KERNEL);
memset((*devp).data, 0, (*devp).size);//初始化为0
return 0; }
void cleanup_module(void) { cdev_del(&cdev);//注销设备
kfree(devp);//注销设备结构体
kfree((*devp).data);//注销设备内存空间
unregister_chrdev(devNum_major,\"MyDeviceDriver\");//卸载主设备号 }
MODULE_LICENSE(\"GPL\"); (2) 驱动程序测试程序
#include #include #include #define BUFFER_SIZE 102400 int main(void) { int dev,i=0; char c;
char source[BUFFER_SIZE];//写入MyDeviceDriver设备的内容
char goal[BUFFER_SIZE]; //MyDeviceDriver设备的内容读入到该goal中
printf(\"input the string you want to write in your device:\\n\"); while((c=getchar())!=\'\\n\')
华 中 科 技 大 学 课 程 设 计 报 告
{
source[i++]=c; } printf(\"\\n\");
if((dev=open(\"/dev/MyDeviceDriver\",O_RDWR))==-1) //打开MyDeviceDriver设备失败
printf(\"FAIL to open MyDeviceDriver!\\n\"); else//成功
printf(\"SUCCESS to open MyDeviceDriver!\\n\");
printf(\"source:\\n%s\\n\\n\",source);
write(dev,source,sizeof(source)); //把source中的内容写入MyDeviceDriver设备
lseek(dev,0,SEEK_SET); //把文件指针定位到文件开始的位置
read(dev,goal,sizeof(source)); //把MyDeviceDriver设备中的内容读入到goal中
printf(\"goal:\\n%s\\n\\n\",goal);
return 0; } 4.4 实验四
实验四有两个选做题目,分别是proc文件监控系统和小型文件系统,本次实验我选择的题目是proc文件监控系统。 4.4.1 实验要求
了解/proc文件的特点和使用方法;监控系统状态,显示系统中若干部件使用状态;用图形界面实现系统监控状态。 4.4.2 具体实现
(1)/proc文件系统的特点
Linux的PROC文件系统是进程文件系统和内核文件系统的组成的复合体,是将内核数据对象化为文件形式进行存取的一种内存文件系统,是监控内核的一种用户接口。它拥有一些特殊的文件(纯文本),从中可以获取系统状态信息。 (2)功能清单
①获取并显示主机名,与之相关的proc文件为/proc/sys/kernel/hostname; ②获取并显示系统启动的时间,与之相关的proc文件为/proc/uptime; ③显示系统到目前为止持续运行的时间,与之相关的proc文件为/proc/uptime; ④显示系统的版本号,与之相关的proc文件为/proc/sys/kernel/ostype和/proc/sys/kernel/osrelease;
⑤ 显示CPU的型号和主频大小,与之相关的proc文件为/proc/cpuinfo;
华 中 科 技 大 学 课 程 设 计 报 告
⑥通过pid或者进程名查询一个进程,并显示该进程的详细信息,提供杀掉该进程的功能,与之相关的proc文件为/proc/(pid)/stat;
⑦显示系统所有进程的一些信息,包括pid、ppid、占用内存大小、优先级等,与之相关的proc文件为/proc/(pid)/stat, /proc/(pid)/statm;
⑧CPU使用率的图形化显示(2分钟内的历史记录曲线),与之相关的proc文件为/proc/stat;
⑨内存和交换分区的使用率的图形化显示(2分钟内的历史曲线),与之有关的proc文件为/proc/meminfo;
⑩在状态栏显示当前时间,未使用到/proc中的文件;
⑪在状态栏显示当前CPU使用率,与之相关的proc文件为/proc/stat; ⑫在状态栏显示当前内存使用情况,与之相关的proc文件为/proc/meminfo; ⑬用新线程运行一个其他程序,未使用到/proc中的文件; ⑭关机功能,未使用到/proc中的文件; (3)功能实现
①获取并显示主机名
用fopen函数打开/proc/sys/kernel/hostname文件,然后以文件指针为输入流,用fgets从其中读出一行字符包含主机名,然后用格式化输出函数canf函数输出一个字符串,即主机名。
②获取并显示系统启动的时间
从文件/proc/uptime中获取系统启动到现在的运行时间(单位是s),然后调用time函数获取系统当前时间(单位是s),用当前时间秒数减去运行时间秒数即为系统启动的时间秒数,然后调用localtime函数将系统启动时间秒数转换成tm结构体类型的变量,该变量中的成员包括年份、月份、日期、星期几、时、分、秒等,再调用输出函数输出即可。
③显示系统到目前为止持续运行的时间
用fopen函数打开/proc/uptime文件,然后以文件指针为输入流,用fgets从其中读出一行字符包含系统运行时间,然后用格式化输入函数canf从读取出的字符流中输入一个float类型的数到runtime,即系统运行的时间。
④显示系统的版本号
21
华 中 科 技 大 学 课 程 设 计 报 告
从/proc/sys/kernel/ostype和/proc/sys/kernel/osrelease中读取系统类型(比如linux)和系统内核版本号,处理方法和获取系统运行时间的方法一样。得到系统类型和系统内核版本号之后,调用QString类的方法arg将两个字符串连接起来,再输出显示即可。
⑤显示CPU的型号和主频大小
打开/proc/cpuinfo文件后发现CPU有四个,相同的型号,不同的主频,后来才弄清楚所谓的四个CPU是CPU的四个核心,而“主频”并不是主频,而是当前时刻该核心所使用的CPU核心频率,随时间而变化的。
弄清楚文件中的内容的含义之后,开始处理/proc/cpuinfo,从中读取CPU的型号和频率。处理这个文件没有用到fopen等函数,而是使用了Qt自带的两个类QFile和QTextStream,定义一个QFile类型的变量file,并用路径名“/proc/cpuinfo”初始化该变量,其作用是以只读方式打开文件/proc/cpuinfo,然后以file的地址作为参数初始化QTextStream类型的变量stream,其作用是将/proc/cpuinfo文件中的内容以字符流的形式存入变量stream中,相当于一个文本流。由于CPU的型号是一样的,所以只读取第一个型号名称即可,根据CPU型号名称所在的行,采用while循环读取stream中的内容,每次读取一行,当行数等于CPU型号所在行时,将读取出的一行赋值给一个QString类型的变量,再调用QString的方法mid截取CPU型号名称,file.close()关闭file。CPU四个核心的主频处理方法和CPU型号的处理方法一致,参照上述步骤即可。
⑥通过pid或者进程名查询一个进程,并显示该进程的详细信息,提供杀掉该进程的功能
在获取所有进程信息的时候,获取到的所有信息已经被存储在一个全局结构体数组变量中了,所以查询的时候,先获取输入lineEdit中的文本(QString类型),然后将文本与全局结构体数组变量的每一个数组元素的name成员和pid成员作比较,若相等,说明待查询的进程存在,将该进程的详细信息输出即可。
杀死进程前先要获取将要被杀死进程的名称或者pid,而且该进程必须存在于/proc目录下,即先查询一次待杀死的进程,处理方法如上所述。Linux系统提供了kill、pkill等杀死进程的命令,pkill通过pid杀死进程,kill通过进程名称杀死进程。通过将lineEdit中的内容和kill或pkill组成终端命令,然后调用system函数执行这
22
华 中 科 技 大 学 课 程 设 计 报 告
些命令即可杀死相应的进程。
⑦显示系统所有进程的一些信息,包括pid、ppid、占用内存大小、优先级等 系统的进程众多,且进程号从1到几万的都有,如果从1开始遍历访问,效率就会很低。所以本次实验我采用的是Linux中对目录进行操作的函数opendir(),readdir()。这两个目录操作函数在上学期的操作系统第三次课程实验中已经学习使用过,所以再次使用没遇到多大的障碍。
实现思路为:声明一个DIR类型的指针dir,用opendir函数打开/proc目录,返回值赋值给dir,然后将dir作为函数readdir的参数,readdir函数返回值复制给dirent结构体指针ptr。ptr指向的结构体包含d_name char数组成员,即文件或者目录名,将d_name数组的0号元素分别和字符„1‟、„9‟比较,若处于这两者之间,则表明该文件或者目录是进程目录,以该目录名为参数调用获取进程详细信息的函数read_proc。在read_proc函数中,使用格式化输出函数sprintf将参数目录名c_pid对应目录下的文件stat的路径输出到char数组filename中,再调用C++的类ifstream,声明一个ifstream类性的变量meminfo,用filename初始化meminfo,然后用析取器(>>)从meminfo中输入数据到结构体数组procInfo的每个元素的成员变量中,析取器(>>)会自动忽略空字符。所需要的进程信息并不是连续存放在stat文件中的,中间包含有其他不需要显示的进程信息,解决方法是定义一个string类型的变量temp,根据stat文件中的信息存放顺序,将所有不需要的进程信息全部输入到temp中。此外还要注意一个问题,进程名称带有括号“()”,所以需要将括号去掉,解决方法是使用string类的方法substr,该函数的参数有两个,一个是想截取的字符串首字符在原字符串中的位置,第二个参数是想截取的字符串末尾字符后面一个字符,在这里就是“)”。处理完毕之后,所需要的关于进程c_pid的详细信息都存储在全局结构体数组procInfo的元素中了。
循环采用上述思路,直到所有进程目录都被处理完毕,所有的进程信息就都存储在了全局结构体数组procInfo中,然后用定时器触发定时更新全局结构体数组procInfo中的内容。
⑧CPU使用率的图形化显示
由于需要画出CPU使用率两分钟内的历史记录曲线,所以需要计算并存储120个连续时刻的CPU利用率。定义一个全局QList变量yList,用于存放CPU使用率。
23
华 中 科 技 大 学 课 程 设 计 报 告
在CPURate函数中调用add_point函数,add_point的参数是cpuRate。在函数add_point中,首先判断yList的大小,若大于120,则把yList的队首元素删除,保证yList的元素个数恒等于120;若等于0,则将yList前119个元素初始化为0,这样的话,CPU历史记录曲线就会从右边向左边逐渐显示出来;若大于等于1,则在yList末尾插入新的cpuRate,然后调用UpdateCPULine函数画出记录曲线。
得到CPU使用率之后,在函数UpdateCPULine中画图。先在QPixmap类型的变量pix上画曲线,pix相当于一块画布,然后再把pix添加到label标签上。pix的背景色设置为蓝色,然后定义一个QPainter类painter,painter的画图设备就是pix,定义QPen画笔pen,画笔颜色设置为红色,画笔宽度为2个像素,设置完成后,以yList中存储的cpuRate为纵坐标,以yList的元素下标为横坐标,并根据坐标轴单位长度所占实际像素个数稍微调整一下每个点的横纵坐标,然后调用painter的drawLine方法画出这些点并依次连接起来,就得到了2分钟内的CPU使用率历史记录曲线,而且历史记录曲线可以随着时间从右向左更新。
⑨内存和交换分区的使用率的图形化显示
由于需要画出内存使用率两分钟内的历史记录曲线,所以需要计算并存储120个连续时刻的内存利用率。定义一个全局QList变量yList1,用于存放内存使用率。在MemRate函数中调用add_point函数,add_point的参数是memRate。在函数add_point中,首先判断yList1的大小,若大于120,则把yList1的队首元素删除,保证yList1的元素个数恒等于120;若等于0,则将yList1前119个元素初始化为0,这样的话,内存使用率历史记录曲线就会从右边向左边逐渐显示出来;若大于等于1,则在yList1末尾插入新的memRate,然后调用UpdateMemLine函数画出记录曲线。
得到内存使用率之后,在函数UpdateMemLine中画图。先在QPixmap类型的变量pix上画曲线,pix相当于一块画布,然后再把pix添加到label标签上。pix的背景色设置为蓝色,然后定义一个QPainter类painter,painter的画图设备就是pix,定义QPen画笔pen,画笔颜色设置为红色,画笔宽度为2个像素,设置完成后,以yList1中存储的memRate为纵坐标,以yList1的元素下标为横坐标,并根据坐标轴单位长度所占实际像素个数稍微调整一下每个点的横纵坐标,然后调用painter的drawLine方法画出这些点并依次连接起来,就得到了2分钟内的内存使用率历史记
24
华 中 科 技 大 学 课 程 设 计 报 告
录曲线,而且历史记录曲线可以随着时间从右向左更新。
⑩在状态栏显示当前时间
调用QDateTime的方法currentDateTime获取当前时间time,time是一个QDateTime类型的变量,然后调用QDateTime的方法toString把time转化成格式为yyyy-MM-dd hh:mm: dddd的QString字符串,再在主窗口的状态栏显示输出即可。 ⑪在状态栏显示当前CPU使用率
CPU的使用率是指CPU的使用时间和CPU总时间的比值。因为/proc/stat文件中的所有值都是从系统启动开始累计到当前时刻,所以计算CPU使用率时需要取两个采样点,利用两个点的差值计算CPU使用率,此次实验采样方法是每隔1秒采样一次。CPU使用率的计算公式为cpuRate=100.0-(idle2-idle1)*100.0/(total2-total1)。 定义一个全局结构体CPU_USE,具体定义如下:
typedef struct CPU_USE {
char name[16];//cpu
unsigned int user;
unsigned int nice;
unsigned int system;
unsigned int idle;
unsigned int iowait;
unsigned int irq;
unsigned int softirq; }cpu_use; CPU总时间的值等于结构体CPU_USE中7个unsigned int类型的成员值之和。计算之前,调用get_cpu_use_stat函数获取一个CPU状态,sleep一秒后再调用该函数获取第二个CPU状态,然后用上述CPU使用率计算公式计算即可。
⑫在状态栏显示当前内存使用情况
内存使用率是指已使用的内存和总内存的比值。内存使用率的计算公式为:memRate=(total – (free+buffers+cached))*100.0/total。
定义一个全局结构体MEM_USE,具体定义如下:
25
华 中 科 技 大 学 课 程 设 计 报 告
typedef struct MEM_USE{
unsigned long total;
unsigned long free;
unsigned long buffers;
unsigned long cached; }mem_use; 该结构体中的成员的值都来自文件/proc/meminfo, 声明一个ifstream类性的变量meminfo,用/proc/meminfo初始化meminfo, 然后用析取器(>>)从meminfo中输入数据到结构体mem_use 的成员变量中,析取器(>>)会自动忽略空字符。所需要的内存数据信息并不是连续存放在/proc/meminfo文件中的,中间包含有其他不需要的内存数据信息,解决方法是定义一个string类型的变量str,根据/proc/meminfo文件中的信息存放顺序,将所有不需要的内存数据信息全部输入到str中。得到内存使用的数据信息之后,使用上述内存使用率计算公式计算即可。 ⑬用新线程运行一个其他程序
在主窗口中添加一个lineEdit控件,获取该控件中输入的程序名称,然后调用QProce类的start方法运行该程序。 ⑭关机功能
由于关机功能需要输入密码获取root权限,所以需要想方法在点击关机按钮后输入密码。定义一个QString类型的变量paWord,调用类QInputDialog的方法getText,会弹出一个对话框提示输入密码,该方法的返回值即为密码,将返回值赋值给paWord变量;当对话框的OK按钮被点击时,用QString(\"echo %1 | sudo -S shutdown -h now\").arg(paWord)初始化QString类型的变量sudo,再把sudo转换成char*类型,然后调用system函数执行关机命令。 (4)界面设计
新建一个Qt Widgets Application工程,用setWindowTitle函数将主窗口的标题改为“proc进程管理器”,设置窗口的初始大小为(650,500),初始位置为显示屏左上角,即坐标原点。
在主窗口底部添加一个状态栏控件,用QStatusBar实现。状态栏中添加三个标签和一个按钮,三个标签分别用于显示当前时间、CPU使用率、内存使用率,这三
26
华 中 科 技 大 学 课 程 设 计 报 告
个信息的显示函数都与定时器QTimer绑定,随时间自动更新,按钮用于触发关机函数。
在主窗口的窗体中添加tabWidget控件,设置四个tab选项卡tab_
1、tab_
2、tab_
3、tab_4。tab_1选项卡用于显示主机名、系统启动时间、系统运行时间、系统版本号、CPU型号和主频等信息,这些内容全部用QLabel类的标签显示,其中系统运行时间和CPU主频函数是槽函数,都和定时器QTimer绑定,随着时间自动更新。tab_2选项卡用于显示进程详细信息,进程详细信息包括进程名、pid、ppid、内存占用和优先级,这些信息用tableWidget控件显示,tableWidget控件设置为禁止选中、禁止修改,tableWidget的列数是固定的5列,行数动态确定,由获取所有进程详细信息的函数get_pid_info的返回值提供;这样做的好处是,当创建新进程或者杀死进程时,tableWidget不会出现行数不够或者有空行的情况,并且ProceInfo函数(即控制显示进程信息的函数)与QTimer绑定,随时间定时更新。 tab_3选项卡用于提供查询进程、杀死进程、创建新进程的操作面板,查询进程的输入框用lineEdit控件实现,该控件的好处是可以直接调用控件所包含的方法text获取输入框的内容,便于查询进程信息;在lineEdit下方放置的是tableWidget控件,同样,该控件禁止选中、禁止修改,用于显示查询的进程详细信息;tableWidget控件下方是关闭进程的按钮,当点击该按钮时便会触发killProce函数关闭进程;创建进程的进程名输入框同样也是用lineEdit控件实现,当点击“运行”按钮时便会触发CreateProce函数。tab_4选项卡用于显示CPU使用率历史记录曲线和内存使用率历史记录曲线。 (5)程序运行结果
27
华 中 科 技 大 学 课 程 设 计 报 告
28
华 中 科 技 大 学 课 程 设 计 报 告
4.4.3 源代码
#mainwindow.h,包含各种槽函数
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include #include cla QTabWidget; namespace Ui { cla MainWindow; } cla MainWindow : public QMainWindow {
Q_OBJECT
29
华 中 科 技 大 学 课 程 设 计 报 告
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow(); private:
Ui::MainWindow *ui; private:
QPushButton* shutButton;
QLabel* NowTimeLabel;
QLabel* CPUUseLabel;
QLabel* MemUseLabel;
QTabWidget* tabwidget;
QList yList;
QList yList1;
void init_StatusBar();
void HostName();
void BootTime();
void OSType();
void add_point(float cpuRate);
void UpdateCPULine();
void add_point_mem(float memRate);
void UpdateMemLine(); private slots:
void NowTime();
void CPURate();
void MemRate();
void run_time();
void CPUInfo();
void ProceInfo();
void QueryProce();
void PawordToShutdown();
void KillProce();
void CreateProce(); }; #endif // MAINWINDOW_H
#sys.h,获取主机名、系统运行时间、系统版本号
#ifndef SYS #define SYS #include #include #include #include char* get_hostname() {
FILE* fp;
char host[8];
char *hostname;
hostname=(char*)malloc(7*sizeof(char));
fp=fopen(\"/proc/sys/kernel/hostname\",\"r\");
fgets(host,sizeof(host),fp);
canf(host,\"%s\",hostname);
fclose(fp);
return hostname; } float get_run_time_sec()
30
华 中 科 技 大 学 课 程 设 计 报 告
{
FILE* fp;
float runTime;
char time[32];
fp=fopen(\"/proc/uptime\",\"r\");
fgets(time,sizeof(time),fp);
canf(time,\"%f\",&runTime);
fclose(fp);
return runTime; } QString get_os_type() {
QString os;
char ostype[6],osrelease[8];
char buff1[16],buff2[16];
FILE *fp1,*fp2;
fp1=fopen(\"/proc/sys/kernel/ostype\",\"r\");
fp2=fopen(\"/proc/sys/kernel/osrelease\",\"r\");
fgets(buff1,sizeof(buff1),fp1);
fgets(buff2,sizeof(buff2),fp2);
canf(buff1,\"%s\",ostype);
canf(buff2,\"%s\",osrelease);
os=QString(\"%1 %2\").arg(ostype).arg(osrelease);
fclose(fp1);
fclose(fp2);
return os; } #endif // SYS
#proce.h,获取所有进程详细信息
#ifndef PROCESS #define PROCESS #include #include #include #include #include #include #include using namespace std; struct PROC_INFO{
string name;
string pid;
string ppid;
string r;
string priority; }procInfo[40960]; void read_proc(PROC_INFO *pidinfo,const char *c_pid) {
string temp,pidname;
char filename[18];
sprintf(filename,\"/proc/%s/stat\",c_pid);
std::ifstream meminfo(filename);
meminfo>>(pidinfo->pid)>>pidname>>temp>>(pidinfo->ppid)>>temp>>temp;
meminfo>>temp>>temp>>temp>>temp>>temp>>temp;
31
华 中 科 技 大 学 课 程 设 计 报 告
meminfo>>temp>>temp>>temp>>temp>>temp>>(pidinfo->priority);
meminfo>>temp>>temp>>temp>>temp>>temp>>(pidinfo->r);
pidinfo->name=pidname.substr(1,pidname.find(\')\')-1);//remove\"()\"
meminfo.close(); } int get_pid_info() {
DIR *dir;
struct dirent *ptr;
int i=0;
if(!(dir=opendir(\"/proc\")))
return 0;
while((ptr=readdir(dir))!=false)
{
if(ptr->d_name[0]>=\'1\' && ptr->d_name[0]
{
read_proc(&(procInfo[i]),ptr->d_name);
i++;
}
}
closedir(dir);
return i; } #endif // PROCESS
#mem.h,计算内存使用率
#ifndef MEM #define MEM #include #include #include #include #include using namespace std; typedef struct MEM_USE{
unsigned long total;
unsigned long free;
unsigned long buffers;
unsigned long cached; }mem_use; void get_mem_use_stat(mem_use *memStat) {
string str;
unsigned long memtotal,memfree,memavailable,membuffers,memcached;
std::ifstream meminfo(\"/proc/meminfo\");
meminfo>>str>>memtotal>>str;
meminfo>>str>>memfree>>str;
meminfo>>str>>memavailable>>str;
meminfo>>str>>membuffers>>str;
meminfo>>str>>memcached>>str;
(*memStat).total=memtotal;
(*memStat).free=memfree;
(*memStat).buffers=membuffers;
(*memStat).cached=memcached;
meminfo.close();
32
华 中 科 技 大 学 课 程 设 计 报 告
} float clacu_memRate(mem_use *memStat) {
float memRate=0.0;
memRate=(float)(((*memStat).totalruntime;
ptm = localtime(&boot_time);
boot=QString(\"%1-%2-%3 %4:%5:%6 %7\").arg(ptm->tm_year+1900).arg(ptm->tm_mon+1).arg(ptm->tm_mday).arg(ptm->tm_hour).arg(ptm->tm_min).arg(ptm->tm_sec).arg(week[ptm->tm_wday]);
ui->bootlabel->setText(boot); } void MainWindow::OSType() {
QString os;
os=get_os_type();
ui->ostypelabel->setText(os); } void MainWindow::PawordToShutdown() {
QString paWord;
QString sudo;
char* command;
bool OK;
QByteArray ba;
paWord=QInputDialog::getText(this,\"输入密码\",\"输入密码\",QLineEdit::Normal,\"\",&OK);
if(OK)
{
sudo=QString(\"echo %1 | sudo -S shutdown -h now\").arg(paWord);
ba=sudo.toLatin1();
command=ba.data();
38
华 中 科 技 大 学 课 程 设 计 报 告
system(command);
} } void MainWindow::CPUInfo() {
QString proceor;
QString Hz0,Hz1,Hz2,Hz3;
proceor=get_proceor();
ui->proceorlabel0->setText(QString(\"0: %1\").arg(proceor));
ui->proceorlabel1->setText(QString(\"1: %1\").arg(proceor));
ui->proceorlabel2->setText(QString(\"2: %1\").arg(proceor));
ui->proceorlabel3->setText(QString(\"3: %1\").arg(proceor));
Hz0=get_Hz0();
Hz0=QString(\"%1%2\").arg(Hz0).arg(\"MHz\");
ui->Hzlabel0->setText(Hz0);
Hz1=get_Hz1();
Hz1=QString(\"%1%2\").arg(Hz1).arg(\"MHz\");
ui->Hzlabel1->setText(Hz1);
Hz2=get_Hz2();
Hz2=QString(\"%1%2\").arg(Hz2).arg(\"MHz\");
ui->Hzlabel2->setText(Hz2);
Hz3=get_Hz3();
Hz3=QString(\"%1%2\").arg(Hz3).arg(\"MHz\");
ui->Hzlabel3->setText(Hz3); } void MainWindow::ProceInfo() {
int pidNum;
int i;
QStringList headers;
QTableWidgetItem *nameItem;
QTableWidgetItem *pidItem;
QTableWidgetItem *ppidItem;
QTableWidgetItem *rItem;
QTableWidgetItem *priorityItem;
pidNum=get_pid_info();
ui->tableWidget->setColumnCount(5);
ui->tableWidget->setRowCount(pidNum);
headers
ui->tableWidget->setHorizontalHeaderLabels(headers);
for(i=0;i { nameItem=new QTableWidgetItem(QString::fromStdString(procInfo[i].name)); ui->tableWidget->setItem(i,0,nameItem); nameItem->setTextAlignment(Qt::AlignCenter); pidItem=new QTableWidgetItem(QString::fromStdString(procInfo[i].pid)); ui->tableWidget->setItem(i,1,pidItem); pidItem->setTextAlignment(Qt::AlignCenter); ppidItem=new QTableWidgetItem(QString::fromStdString(procInfo[i].ppid)); ui->tableWidget->setItem(i,2,ppidItem); ppidItem->setTextAlignment(Qt::AlignCenter); rItem=new QTableWidgetItem(QString::fromStdString(procInfo[i].r)); ui->tableWidget->setItem(i,3,rItem); rItem->setTextAlignment(Qt::AlignCenter); priorityItem=new QTableWidgetItem(QString::fromStdString(procInfo[i].priority)); ui->tableWidget->setItem(i,4,priorityItem); 39 华 中 科 技 大 学 课 程 设 计 报 告 priorityItem->setTextAlignment(Qt::AlignCenter); } ui->tableWidget->setColumnWidth(0,121); ui->tableWidget->setColumnWidth(1,121); ui->tableWidget->setColumnWidth(2,121); ui->tableWidget->setColumnWidth(3,121); ui->tableWidget->setColumnWidth(4,121); ui->tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); ui->tableWidget->setSelectionMode(QAbstractItemView::NoSelection); } void MainWindow::QueryProce() { QString queryitem; int pidNum,i; QStringList headers; QTableWidgetItem *nameItem; QTableWidgetItem *pidItem; QTableWidgetItem *ppidItem; QTableWidgetItem *rItem; QTableWidgetItem *priorityItem; QHeaderView* headerView = ui->tableWidget_2->verticalHeader(); headerView->setHidden(true);//no row number queryitem=ui->lineEdit->text(); pidNum=get_pid_info(); for(i=0;i { if(queryitem==QString::fromStdString(procInfo[i].name) || queryitem==QString::fromStdString(procInfo[i].pid)) break; } ui->tableWidget_2->setColumnCount(5); ui->tableWidget_2->setRowCount(1); headers ui->tableWidget_2->setHorizontalHeaderLabels(headers); nameItem=new QTableWidgetItem(QString::fromStdString(procInfo[i].name)); ui->tableWidget_2->setItem(0,0,nameItem); nameItem->setTextAlignment(Qt::AlignCenter); pidItem=new QTableWidgetItem(QString::fromStdString(procInfo[i].pid)); ui->tableWidget_2->setItem(0,1,pidItem); pidItem->setTextAlignment(Qt::AlignCenter); ppidItem=new QTableWidgetItem(QString::fromStdString(procInfo[i].ppid)); ui->tableWidget_2->setItem(0,2,ppidItem); ppidItem->setTextAlignment(Qt::AlignCenter); rItem=new QTableWidgetItem(QString::fromStdString(procInfo[i].r)); ui->tableWidget_2->setItem(0,3,rItem); rItem->setTextAlignment(Qt::AlignCenter); priorityItem=new QTableWidgetItem(QString::fromStdString(procInfo[i].priority)); ui->tableWidget_2->setItem(0,4,priorityItem); priorityItem->setTextAlignment(Qt::AlignCenter); ui->tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); ui->tableWidget->setSelectionMode(QAbstractItemView::NoSelection); } void MainWindow::KillProce() { QString queryitem; int pidNum,i; 40 华 中 科 技 大 学 课 程 设 计 报 告 int killtype=-1; QString sudo; char* command; QByteArray ba; queryitem=ui->lineEdit->text(); pidNum=get_pid_info(); for(i=0;i { if(queryitem==QString::fromStdString(procInfo[i].name) || queryitem==QString::fromStdString(procInfo[i].pid)) break; } if(queryitem==QString::fromStdString(procInfo[i].name)) killtype=0; if(queryitem==QString::fromStdString(procInfo[i].pid)) killtype=1; switch (killtype) { case 0: sudo=QString(\"pkill %1\").arg(queryitem); ba=sudo.toLatin1(); command=ba.data(); system(command); break; case 1: sudo=QString(\"kill %1\").arg(queryitem); ba=sudo.toLatin1(); command=ba.data(); system(command); break; default: break; } } void MainWindow::CreateProce() { QProce *pro=new QProce; QString newProc; newProc=ui->lineEdit_2->text(); pro->start(newProc); } void MainWindow::add_point(float cpuRate) { int i; int size=yList.size(); int y=cpuRate; if(size>120) { yList.pop_front(); } if(size==0) { for(i=0;i { yList.push_back(0); } } 41 华 中 科 技 大 学 课 程 设 计 报 告 if(size>=1) { yList.push_back(y); emit UpdateCPULine(); } else{ yList.push_back(y); } } void MainWindow::UpdateCPULine() { int count=0; QPixmap pix(600,160); QPainter painter(&pix); pix.fill(Qt::blue); QPen pen0; pen0.setColor(Qt::lightGray); painter.setPen(pen0); for(int i=1;i { painter.drawLine(0,i*40,600,i*40); } QPen pen; pen.setColor(Qt::red); pen.setWidth(2); painter.setPen(pen); while(count { painter.drawLine(5*count,160-1.6*(yList.value(count)),5*(count+1),160-1.6*(yList.value(count+1))); count++; } ui->cpuline_label->setPixmap(pix); } void MainWindow::add_point_mem(float memRate) { int i; int size=yList1.size(); int y=memRate; if(size>120) { yList1.pop_front(); } if(size==0) { for(i=0;i { yList1.push_back(0); } } if(size>=1) { yList1.push_back(y); emit UpdateMemLine(); } 42 华 中 科 技 大 学 课 程 设 计 报 告 else{ yList1.push_back(y); } } void MainWindow::UpdateMemLine() { int count=0; QPixmap pix(600,160); QPainter painter(&pix); pix.fill(Qt::blue); QPen pen0; pen0.setColor(Qt::lightGray); painter.setPen(pen0); for(int i=1;i { painter.drawLine(0,i*40,600,i*40); } QPen pen; pen.setColor(Qt::red); pen.setWidth(2); painter.setPen(pen); while(count { painter.drawLine(5*count,160-1.6*(yList1.value(count)),5*(count+1),160-1.6*(yList1.value(count+1))); count++; } ui->memlinelabel->setPixmap(pix); } #main.cpp,新建主窗口并显示 #include \"mainwindow.h\" #include int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); } 5 心得体会 本次课程设计,第一个题目很简单,以前写过文件拷贝的C语言程序,第一个题目在以前的基础上用open、read、write等替换fopen、fread、fwrite即可;第二个题目和第三个题目是第一次遇见,在此之前对系统调用和设备驱动程序的知识知之甚少,所以刚开始障碍比较大,但是慢慢地就有了些了解,也就没那么复杂了,但是需要细心;第四个题目看起来很复杂,其实没那么复杂,第一题已经解决了Qt 43 华 中 科 技 大 学 课 程 设 计 报 告 的使用问题,剩下的就是对/proc文件系统的处理问题,包括文件的读取、读取之后对字符串的处理等,这些处理相对来说比较繁琐,所以花费了不少时间。 这次课设让我对Linux的系统调用、设备驱动程序的工作原理、/proc文件系统等有了更深的理解,对Qt的使用比以前更加熟练。总而言之,这次课设让我收获很多,受益匪浅。 44