操作系统课设Word文档格式.docx
《操作系统课设Word文档格式.docx》由会员分享,可在线阅读,更多相关《操作系统课设Word文档格式.docx(67页珍藏版)》请在冰点文库上搜索。
文件拷贝。
3.3实验三
掌握增加设备驱动程序的方法。
通过模块方法,增加一个新的设备驱动程序,其功能可以简单。
(实现字符设备的驱动)
3.4实验四(选做)
了解和掌握/proc文件系统的特点和使用方法
(1)了解/proc文件的特点和使用方法;
(2)监控系统状态,显示系统中若干部件使用状态;
(3)用图形界面实现系统监控状态;
3.5实验五(选做)
设计并实现一个模拟的文件系统。
多用户的多级目录的文件系统设计。
多用户、多级目录、login(用户登录)、系统初始化(建文件卷,提供登录模块)、文件的创建、文件的打开、文件的读写、文件关闭、删除文件、创建目录(建立子目录)、改变当前目录、列出文件目录、退出。
4设计与实现
4.1实验一
4.1.1实验要求
4.1.2具体实现
本实验内容是用CodeBlocksIDE实现的,该软件整合了函数库和编译器,因此使用起来非常方便。
在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。
本次实验使用的图形库是跨平台的开发工具Qt。
首先下载Qt的安装包并安装。
Qt安装完之后,先新建一个Qt控制台应用MAIN作为主进程,用于调用三个并发的子进程。
在主进程的main函数中,使用fork创建三个子进程,若进程创建成功(即fork函数返回值等于0),则使用execv函数进入对应的子进程(get、copy、put)。
主进程程序编写完成后,再新建三个QtWidgetsApplication,分别作为三个子进程get、copy、put(所实现的功能并不是拷贝)。
由于三个子进程窗口显示的内容形式一模一样,所以以子进程get为例。
get进程的窗口显示了一下四个内容:
当前时间、子进程名称、子进程的pid和父进程MAIN的pid。
用Qt的对象QDateTime获取系统当前时间,然后将时间转换成一个字符串写在一个QLabel类的实例中,然后将该实例添加至窗口;
直接把当前进程名称写在一个标签上然后添加至窗口;
使用getpid和getppid函数分别获取当前进程号和父进程号,然后调用sprintf把进程号转换成字符串类型之后写在标签上并添加至窗口即可。
主进程和三个子进程的程序全部编写完后,直接在Qt上编译运行。
程序运行结果如下所示:
4.1.3源代码
(1)文件拷贝源代码
#include<
stdio.h>
stdlib.h>
string.h>
unistd.h>
fcntl.h>
sys/syscall.h>
errno.h>
#defineSIZE10///每次读取的字符数目
char*srcFile="
/home/ilbear/桌面/教程.docx"
;
char*goalFile="
/home/ilbear/桌面/教程副本.docx"
intmain(intargc,constchar*argv[])
{
intsrc,goal;
intread_len;
charbuff[SIZE];
src=open(srcFile,O_RDONLY);
if(src<
0)
{
printf("
Failtoopen%s\n."
srcFile);
exit
(1);
}
goal=open(goalFile,O_WRONLY|O_CREAT,0666);
if(goal<
goalFile);
while((read_len=read(src,buff,SIZE))>
write(goal,buff,read_len);
close(src);
close(goal);
return0;
}
(2)三个并发进程
#主进程MAIN
QCoreApplication>
sys/types.h>
sys/wait.h>
sys/stat.h>
intmain(intargc,char*argv[])
QCoreApplicationa(argc,argv);
pid_tp1,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)
/home/ilbear/桌面/build-copy-Desktop_Qt_5_4_1_GCC_64bit-Debug/copy"
if((p3=fork())==0)
/home/ilbear/桌面/build-put-Desktop_Qt_5_4_1_GCC_64bit-Debug/put"
waitpid(p1,NULL,0);
waitpid(p2,NULL,0);
waitpid(p3,NULL,0);
returna.exec();
#子进程get
mainwindow.cpp
#include"
mainwindow.h"
ui_mainwindow.h"
QtCore>
time.h>
MainWindow:
:
MainWindow(QWidget*parent):
QMainWindow(parent),
ui(newUi:
MainWindow),sharememory1("
share1"
)
ui->
setupUi(this);
setWindowTitle("
get"
);
setWindowFlags(Qt:
Dialog);
move(0,0);
resize(500,500);
charstr[128],f_id[128];
sprintf(str,"
%d"
getpid());
sprintf(f_id,"
getppid());
textBrowser->
setText("
textBrowser_2->
setText(str);
textBrowser_3->
setText(f_id);
QTimer*timer=newQTimer(this);
connect(timer,SIGNAL(timeout()),this,SLOT(timerUpDate()));
timer->
start
(1);
~MainWindow()
deleteui;
voidMainWindow:
timerUpDate()
QDateTimetime=QDateTime:
currentDateTime();
QStringstr=time.toString("
yyyy-MM-ddhh:
mm:
ssdddd"
labelCurDate->
#子进程copy
),sharememory2("
share2"
copy"
move(500,500);
#子进程put
MainWindow),sharememory2("
put"
move(1000,0);
4.2实验二
4.2.1实验要求
4.2.2具体实现
(1)系统调用的原理
用户进程不能访问内核所占内存空间,也不能调用内核函数。
进程调用一个特殊的指令,这个指令会跳到一个事先定义的内核中的一个位置。
在IntelCPU中,由中断INT0x80实现。
(与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.kernel.org/下载内核linux-3.14.36.tar.xz。
打开终端,使用sudosu获取root权限,然后使用cplinux-3.14.36.tar.xz/usr/src命令将linux-3.14.36.tar.xz复制到文件夹/usr/src下,复制完毕之后将其解压,用到的命令为:
xz-dlinux-3.14.36.tar.xz和tarxvflinux-3.14.36.tar。
②修改内核
新的内核解压完毕后,使用cd/usr/src/linux-3.14.36命令进入目录/usr/src/linux-3.14.36。
然后使用命令sudogeditkernel/sys.c打开sys.c,将新的系统调用程序复制到该文件的文件末尾,保存退出,系统调用程序详细代码见4.2.3。
使用命令sudogeditarch/x86/syscalls/syscall_64.tbl打开syscall_64.tbl添加系统调用号。
在该文件中添加一行内容317commonmycallsys_mycall,其中系统调用号317不是固定的,只要该文件中没有出现的数字都可以使用。
添加之后保存退出。
使用命令sudogeditinclude/asm-generic/syscalls.h打开syscalls.h,在“#endif/*__ASM_GENERIC_SYSCALLS_H*/这一行的上面一行添加新的系统调用程序的函数定义,即:
#ifndefsys_mycall
asmlinkageintsys_mycall(char*sourceFile,char*destFile);
#endif
然后保存退出。
③编译内核
在编译内核之前先要安装ncurses库,使用命令sudoapt-getinstalllibncurses5-dev安装。
安装完毕后,进入/usr/src/linux-3.14.36目录下,新建一个脚本文件mysyscall.sh,通过命令geditmysyscall.sh打开该脚本文件进行编辑。
将以下内容添加到脚本中:
#!
/bin/bash
makemrproper
makemenuconfig
makedep
makeclean
makebzImage–j4
makemodules–j4
makemodules_install–j4
makeinstall–j4
mkinitramfs-o/boot/initrd.img-3.14.34
update-grub
reboot
保存退出,然后修改脚本文件的权限,使其可以对内核文件进行操作,修改权限的命令为chmod777mysyscall.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源代码
(1)系统调用程序
asmlinkageintsys_mycall(char*SourceFile,char*GoalFile)
intsource=sys_open(SourceFile,O_RDONLY,0);
intgoal=sys_open(GoalFile,O_WRONLY|O_CREAT|O_TRUNC,0600);
charbuff[4096];
mm_segment_tfs;
fs=get_fs();
set_fs(get_ds());
inti;
if(source>
0&
&
goal>
do
i=sys_read(source,buff,4096);
sys_write(goal,buff,i);
while(i);
printk("
Error!
"
sys_close(source);
sys_close(goal);
set_fs(fs);
return10;
(2)测试程序
syscall.h>
intmain()
syscall(317,"
main.c"
"
yyk.text"
4.3实验三
4.3.1实验要求
4.3.2具体实现
(1)Linux核心是一种monolithic类型的内核,即单一的大核心,另外一种形式是MicroKernel,核心的所有功能部件都被拆成独立部分,这些部分之间通过严格的通讯机制进行联系。
Linux内核是一个整体结构,因此向内核添加任何东西.或者删除某些功能,都十分困难。
为了解决这个问题,引入了模块机制,从而可以动态的在内核中添加或者删除模块。
模块一旦被插入内核,就和内核其他部分一样。
Linux内核中的设备驱动程序是一组常驻内存的具有特权的共享库,是低级硬件处理例程。
对用户程序而言,设备驱动程序隐藏了设备的具体细节,对各种不同设备提供了一致的接口,一般来说是把设备映射为一个特殊的设备文件,用户程序可以像对其它文件一样对此设备文件进行操作。
Linux支持3种设备:
字符设备、块设备和网络设备。
设备由一个主设备号和一个次设备号标识。
主设备号唯一标识了设备类型,即设备驱动程序类型,它是块设备表或字符设备表中设备表项的索引。
次设备号仅由设备驱动程序解释,一般用于识别在若干可能的硬件设备中,I/O请求所涉及到的那个设备。
典型的Linux模块实现机制有如下几步:
①注册设备:
在系统初启或者加载模块的时候,必须将设备登记到相应的设备数组,并返回主