操作系统课设.docx
- 文档编号:12336779
- 上传时间:2023-04-18
- 格式:DOCX
- 页数:67
- 大小:528.25KB
操作系统课设.docx
《操作系统课设.docx》由会员分享,可在线阅读,更多相关《操作系统课设.docx(67页珍藏版)》请在冰豆网上搜索。
操作系统课设
课程设计报告
课程名称:
计算机操作系统
专业班级:
学号:
姓名:
指导教师:
报告日期:
计算机科学与技术学院
1实验目的
·掌握Linux操作系统的使用方法;
·了解Linux系统内核代码结构;
·掌握实例操作系统的实现方法;
2实验环境
本次课程设计采用的操作系统环境是windows8、Ubuntu双系统,Ubuntu系统版本号为14.04,内核版本号为linux3.13.0;采用的编程环境为CodeBlocksIDE和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具体实现
本实验内容是用CodeBlocksIDE实现的,该软件整合了函数库和编译器,因此使用起来非常方便。
(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)。
主进程程序编写完成后,再新建三个QtWidgetsApplication,分别作为三个子进程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
#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<0)
{
printf("Failtoopen%s\n.",goalFile);
exit
(1);
}
while((read_len=read(src,buff,SIZE))>0)
{
write(goal,buff,read_len);
}
close(src);
close(goal);
return0;
}
(2)三个并发进程
#主进程MAIN
#include
#include
#include
#include
#include
#include
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)
{
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);
returna.exec();
}
#子进程get
mainwindow.cpp
#include"mainwindow.h"
#include"ui_mainwindow.h"
#include
#include
#include
#include
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,"%d",getppid());
ui->textBrowser->setText("get");
ui->textBrowser_2->setText(str);
ui->textBrowser_3->setText(f_id);
QTimer*timer=newQTimer(this);
connect(timer,SIGNAL(timeout()),this,SLOT(timerUpDate()));
timer->start
(1);
}
MainWindow:
:
~MainWindow()
{
deleteui;
}
voidMainWindow:
:
timerUpDate()
{
QDateTimetime=QDateTime:
:
currentDateTime();
QStringstr=time.toString("yyyy-MM-ddhh:
mm:
ssdddd");
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(newUi:
:
MainWindow),sharememory1("share1"),sharememory2("share2")
{
charstr[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=newQTimer(this);
connect(timer,SIGNAL(timeout()),this,SLOT(timerUpDate()));
timer->start
(1);
}
MainWindow:
:
~MainWindow()
{
deleteui;
}
voidMainWindow:
:
timerUpDate()
{
QDateTimetime=QDateTime:
:
currentDateTime();
QStringstr=time.toString("yyyy-MM-ddhh:
mm:
ssdddd");
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(newUi:
:
MainWindow),sharememory2("share2")
{
charstr[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=newQTimer(this);
connect(timer,SIGNAL(timeout()),this,SLOT(timerUpDate()));
timer->start
(1);
}
MainWindow:
:
~MainWindow()
{
deleteui;
}
voidMainWindow:
:
timerUpDate()
{
QDateTimetime=QDateTime:
:
currentDateTime();
QStringstr=time.toString("yyyy-MM-ddhh:
mm:
ssdddd");
ui->labelCurDate->setText(str);
}
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。
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>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);
return10;
}
(2)测试程序
#include
#include
#include
intmain()
{
syscall(317,"main.c","yyk.text");
return0;
}
4.3实验三
4.3.1实验要求
掌握增加设备驱动程序的方法。
通过模块方法,增加一个新的设备驱动程序,其功能可以简单。
(实现字符设备的驱动)
4.3.2具体实现
(1)Linux核心是一种monolithic类型的内核,即单一的大核心,另外一种形式是MicroKernel,核心的所有功能部件都被拆成独立部分,这些部分之间通过严格的通讯机制进行联系。
Linux内核是一个整体结构,因此向内核添加任何东西.或者删除某些功能,都十分困难。
为了解决这个问题,引入了模块机制,从而可以动态的在内核中添加或者删除模块。
模块一旦被插入内核,就和内核其他部分一样。
Linux内核中的设备驱动程序是一组常驻内存的具有特权的共享库,是低级硬件处理例程。
对用户程序而言,设备驱动程序隐藏了设备的具体细节,对各种不同设备提供了一致的接口,一般来说是把设备映射为一个特殊的设备文件,用户程序可以像对其它文件一样对此设备文件进行操作。
Linux支持3种设备:
字符设备、块设备和网络设备。
设备由一个主设备号和一个次设备号标识。
主设备号唯一标识了设备类型,即设备驱动程序类型,它是块设备表或字符设备表中设备表项的索引。
次设备号仅由设备驱动程序解释,一般用于识别在若干可能的硬件设备中,I/O请求所涉及到的那个设备。
典型的Linux模块实现机制有如下几步:
①注册设备:
在系统初启或者加载模块的时候,必须将设备登记到相应的设备数组,并返回主
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 操作系统