操作系统课设.docx
- 文档编号:10293210
- 上传时间:2023-02-09
- 格式:DOCX
- 页数:67
- 大小:584.55KB
操作系统课设.docx
《操作系统课设.docx》由会员分享,可在线阅读,更多相关《操作系统课设.docx(67页珍藏版)》请在冰豆网上搜索。
操作系统课设
操作系统课设
课程设计报告
课程名称:
计算机操作系统
专业班级:
学号:
姓名:
指导教师:
报告日期:
计算机科学与技术学院
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。
1.1.1源代码
(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);
}
1.2实验二
1.2.1实验要求
掌握系统调用的实现过程,通过编译内核方法,增加一个新的系统调用,另编写一个应用程序,调用新增加的系统调用。
1.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.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;
}
1.3实验三
1.3.1实验要求
掌握增加设备驱动程序的方法。
通过模块方法,增加一个新的设备驱动程序,其功能可以简单。
(实现字符设备的驱动)
1.3.2具体实现
(1)Linux核心是一种monolithic类型的内核,即单一的大核心,另外一种形式是MicroKernel,核心的所有功能部件都被拆成独立部分,这些部分之间通过严格的通讯机制进行联系。
Linux内核是一个整体结构,因此向内核添加任何东西.或者删除某些功能,都十分困难。
为了解决这个问题,引入了模块机制,从而可以动态的在内核中添加或者删除模块。
模块一旦被插入内核,就和内核其他部分一样。
Linux内核中的设备驱动程序是一组常驻内存的具有特权的共享库,是低级硬件处理例程。
对用户程序而言,设备驱动程序隐藏了设备的具体细节,对各种不同设备提供了一致的接口,一般来说是把设备映射为一个特殊的设备文件,用户程序可以像对其它文件一样对此设备文件进行操作。
Linux支持3种设备:
字符设备、块设备和网络设备。
设备由一个主设备号和一个次设备号标识。
主设备号唯一标识了设备类型,即设备驱动程序类型,它是块设备表或字符设备表中设备表项的索引。
次设备号仅由设备驱动程序解释,一般用于识别在若干可能的硬件设备中,I/O请求所涉及到的那个设备。
典型的Linux模块实现机制有如下几步:
①注册设备:
在系统初启或者加载模块的时候,必须将设备登记到相应的设备数组,并返回主设备号。
②定义功能函数:
对于每一个驱动函数来说,都有一些和此设备密切相关的功能函数。
以最常用的块设备或者字符设备来说,都存在着诸如open()、read()这一类的操作。
当系统调用这些调用时,将自动的使用驱动函数中特定的模块来实现具体的操作。
③卸载设备:
在不用这个设备时,可以将它卸载,主要是从/proc中取消这个设备的特殊文件。
(2)编写Makefile文件
Makefile文件用于编译设备驱动程序,其内容如下:
ifneq($(KERNELRELEASE),)
#kbuildsyntax.
#模块的文件组成
mymodule-objs:
=MyDeviceDriver.o
#生成的模块文件名
obj-m:
=MyDeviceDriver.o
else
PWD:
=$(shellpwd)
KVER:
=$(shelluname-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的具体定义如下:
structfile_operations{
structmodule*owner;//拥有该结构的模块的指针,一般为THIS_MODULES
loff_t(*llseek)(structfile*,loff_t,int);//用来修改文件当前的读写位置
ssize_t(*read)(structfile*,char__user*,size_t,loff_t*);//从设备中同步读取数据
ssize_t(*write)(structfile*,constchar__user*,size_t,loff_t*
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 操作系统