操作系统实验指导书
操作系统是一门理论性和时间性都很强的课程。要学好操作系统的设计原理,除了听课、看书、做习题外,最好的方法就是在实践中进行。
实验的操作平台是Red Hat Linux 9.0和Windows 2000Professional,其中前者是主要的实验平台,因为源码阅读级等实验都是在Linux平台上进行的,当然,在Linux上进行的实验基本上也都可在UNIX平台上直接进行。读者可根据自己的需要以及实验条件等选择相应的实验内容。
实验一 UNIX/LINUX及其使用环境... 0
实验二 LINUX 下C语言使用、编译与调试实验... 5
实验三进程的创建实验... 8
实验四 进程互斥实验... 10
实验五 进程间管道通信... 13
实验六Linux进程间通信--消息队列... 15
实验七 进程共享存储区通信通信... 18
实验八 线程同步... 22
实验九 银行家算法实现... 24
实验十 模拟分页式虚拟存储管理... 28
实验十一 分区存储管理系统... 32
实验十二 文件系统... 37
参考实验 配置vsftp. 41
实验一UNIX/LINUX及其使用环境
实验目的
1、了解UNIX的命令及使用格式。
2、熟悉UNIX/LINUX的常用基本命令。
实验内容
熟悉UNIX/LINUX的常用基本命令如ls、who、w、pwd、ps、pstree、top等。
实验指导
一、UNIX命令格式
命令[选项] [处理对象]
例:ls -la mydir
注意:(1)命令一般是小写字串。注意大小写有别
(2)选项通常以减号(-)再加上一个或数个字符表示,用来选择一个命令的不同操作
(3)同一行可有数个命令,命令间应以分号隔开
常用命令
1、目录操作
和DOS相似,UNIX采用树型目录管理结构,由根目录(/)开始一层层将子目录建下去,各子目录以 /隔开。用户login后,工作目录的位置称为homedirectory,由系统管理员设定。‘~’符号代表自己的homedirectory,例如 ~/myfile 是指自己home目录下myfile这个文件。
UNIX的通配符有三种:’*’ 和 ’?’ 用法与DOS相同, ‘-‘代表区间内的任一字符,如test[0-5]即代表test0,test1,……,test5的集合。
(1)显示目录文件ls
执行格式: ls [-atFlgR][name](name可为文件或目录名称)
例:ls显示出当前目录下的文件
ls-a显示出包含隐藏文件的所有文件
ls-t按照文件最后修改时间显示文件
ls-F显示出当前目录下的文件及其类型
ls-l显示目录下所有文件的许可权、拥有者、文件大小、修改时间及名称
ls-lg同上
ls-R显示出该目录及其子目录下的文件
注:ls与其它命令搭配使用可以生出很多技巧(最简单的如"ls -l | more"),更多用法请输入ls--help查看,其它命令的更多用法请输入 命令名--help 查看.
(2)建新目录 mkdir
执行格式:mkdir directory-name
例:mkdirdir1 (新建一名为dir1的目录)
(3)删除目录 rmdir
执行格式:rmdirdirectory-name 或rm directory-name
例:rmdirdir1删除目录dir1,但它必须是空目录,否则无法删除
rm -rdir1删除目录dir1及其下所有文件及子目录
rm -rfdir1不管是否空目录,统统删除,而且不给出提示,使用时要小心
(4)改变工作目录位置cd
执行格式:cd[name]
例:cd改变目录位置至用户login时的workingdirectory
cddir1改变目录位置,至dir1目录
cd~user改变目录位置,至用户的working directory
cd..改变目录位置,至当前目录的上层目录
cd../user改变目录位置,至上一级目录下的user目录
cd/dir-name1/dir-name2改变目录位置,至绝对路径(Full path)
cd-回到进入当前目录前的上一个目录
(5)显示当前所在目录pwd
执行格式:pwd
(6)查看目录大小du
执行格式:du [-s] directory
例:dudir1显示目录dir1及其子目录容量(以kb为单位)
du-sdir1显示目录dir1的总容量
(7)显示环境变量
echo $HOME显示家目录
echo $PATH显示可执行文件搜索路径
env显示所有环境变量(可能很多,最好用"env | more","env | grep PATH"等)
(8)修改环境变量,在bash下用export,如:
export PATH=$PATH:/usr/local/bin
想知道export的具体用法,可以用shell的help命令:help export
2、文件操作
(1)查看文件(可以是二进制的)内容 cat
执行格式:cat filename或more filename 或cat filename|more
例:catfile1以连续显示方式,查看文件file1的内容
morefile1
或catfile1|more 以分页方式查看文件的内容
(2)删除文件 rm
执行格式: rmfilename
例:rmfile?
rmf*
(3)复制文件 cp
执行格式:cp [-r]source destination
例:cpfile1file2将file1复制成file2
cp file1dir1将file1复制到目录dir1
cp/tmp/file1将file1复制到当前目录
cp /tmp/file1file2 将file1复制到当前目录名为file2
cp –r dir1dir2(recursivecopy)复制整个目录。
(4)移动或更改文件、目录名称mv
执行格式:mv source destination
例:mv file1file2将文件file1,更名为file2
mvfile1dir1将文件file1,移到目录dir1下
mv dir1dir2
(5)比较文件(可以是二进制的)或目录的内容diff
执行格式: diff [-r] name1name2 (name1、name2同为文件或目录)
例:diff file1file2比较file1与file2的不同处
diff -rdir1dir2比较dir1与dir2的不同处
(6)文件中字符串的查找grep
执行格式:grepstring file
例:grep abcfile1查找并列出串abc所在的整行文字
(7)文件或命令的路径寻找
执行格式一:whereis command显示命令的路径
执行格式二:whichcommand 显示路径及使用者所定义的别名
执行格式三:whatiscommand 显示命令的功能摘要
执行格式四:find search-path -namefilename -print
搜寻指定路径下某文件的路径
执行格式五:locatefilename
根据系统预先生成的文件/目录数据库(/var/lib/slocate/slocate.db)查找匹配的文件/目录,查找速度很快,如果有刚进行的文件改变而系统未到执行定时更新数据库的时间,可以打入updatedb命令手动更新.
(8)建立文件或目录的链接ln
例:lnsource target1建立source文件(已存在)的硬链接,命名为target1
ln-s sourcetarget2建立source文件的符号链接,命名为target2
3、系统询问与权限口令
(1)查看系统中的使用者
执行格式:who
(2)查看username
执行格式:who amI查看自己的username
(3)改变自己的username的帐号与口令su
执行格式:suusername
例:suusername输入帐号
password输入密码
(4)文件属性的设置chmod
改变文件或目录的读、写、执行的允许权
执行格式:chmod[-R] mode name
其中:[-R]为递归处理,将指定目录下所有文件及子目录一并处理
mode为3-8位数字,是文件/目录读、写、执行允许权的缩写(r:read,数字代号为"4"w:write,数字代号为"2"x:execute,数字代号为"1")
mode:rwxrwxrwx
usergroupother
缩写:(u)(g)(o)
例:chmod 755dir1将目录dir1设定成任何人皆有读取及执行的权利,但只有拥有者可作写修改。其中7=4+2+1,5=4+1
chmod700file1将file1设为拥有者可以读、写和执行
chmodo+xfile2将file2,增加拥有者可执行的权利
chmod g+xfile3将file3,增加组使用者可执行的权利
chmodo-rfile4将file4,除去其它使用者可读取的权利
(5)改变文件或目录所有权chown
执行格式:chown [-R]usernamename
例: chownuserfile1将文件file1改为user所有
chown .foxfile1将文件file1改为fox组所有
chownuser.foxfile1 将文件file1改为fox组的user所有
chown-R userdir1将目录dir1及其下所有文件和子目录,改为user 所有
(6)检查用户所在组名称groups
执行格式:groups
(7)改变文件或目录的组拥有权 chgrp
执行格式:chgrp [-R]groupnamename
例: chgrpvlsifile1将文件file1改为vlsi组所有
chgrp-R imagedir1将目录dir1及其下所有文件和子目录,改为image群组
(8)改变文件或目录的最后修改时间touch
执行格式:touch name
4、进程操作
(1)查看系统目前的进程ps
执行格式:ps [-aux]
例: ps或ps -x 查看系统中属于自己的process
ps-au查看系统中所有使用者的process
ps-aux查看系统中包含系统内部及所有使用者的process
ps -aux|grepapache找出系统中运行的所有名称中带有"apache"串的process
(3)结束或终止进程kill
执行格式:kill[-9]PID(PID为利用ps命令所查出的process ID)
例:kill456
或 kill-9 456终止processID 为456的process
(7)以树状图显示执行的程序 pstree
例:pstree -h 列出进程树并高亮标出当前执行的程序
(8)监视虚拟内存vmstat
vmstat对系统的虚拟内存、进程、CPU活动进行监视,同时它也对磁盘和forks和vforks操作的个数进行汇总。
不足是:vmstat不能对某个进程进行深入分析,它仅是一对系统的整体情况进行分析。
例如:[angel@home /angel]#vmstat
procsmemoryswapiosystemcpu
rb wswpdfree buffcache sisobiboincs us syid
00 07180 185256092 4840000652480 0 18
其中:
Procs
r: 等待运行的进程数 b: 处在非中断睡眠状态的进程数 w: 被交换出去的可运行的进程数。
Memory
swpd:虚拟内存使用情况,单位:KB free:空闲的内存,单位KB
buff: 被用来做为缓存的内存数,单位:KB
Swap
si: 从磁盘交换到内存的交换页数量,单位:KB/秒 so:从内存交换到磁盘的交换页数量,单位:KB/秒
IO
bi: 发送到块设备的块数,单位:块/秒 bo: 从块设备接收到的块数,单位:块/秒
System
in: 每秒的中断数,包括时钟中断 cs: 每秒的环境(上下文)切换次数
CPU 按 CPU 的总使用百分比来显示
us: CPU使用时间 sy: CPU系统使用时间 id: 闲置时间
(9)分析共享内存、信号量和消息队列ipcs(相关命令ipcrm:用于给有权限的用户清除这些量,注意不要乱清除,除非该量确实失效了)
例如:[angel@home /angel]# ipcs
------ Shared Memory Segments --------
keyshmidownerpermsbytesnattchstatus
0x002802670root6441048576 1
0x61715f011root6663200033
0x000000002nobody6009216411dest
------ Semaphore Arrays --------
keysemidownerpermsnsemsstatus
0x002802690root66614
0x61715f02257root7771
------ Message Queues --------
keymsqidownerpermsused-bytes messages
(10)监视用户空间程序发出的全部系统调用 strace
strace 还能显示调用的参数,以及用符号方式表示的返回值。
strace 从内核中接收信息,所以一个程序无论是否按调试方式编译(gcc -g)或是否被去掉了调试信息,都可以被跟踪。
执行格式: strace[-tTeo]executable-program-name
-t : 用来显示调用发生的时间
-T : 显示调用花费的时间
-e : 限定被跟踪的调用类型
-o : 将输出重定向到一个文件中
类似命令:ltrace[-fiS] executable-program-name
6、I/O命令
(1)管道(pipe-line)的使用
执行格式:command1|command2
功能:将command1的执行结果送到command2 作为输入
例: ls -R1|more以分页方式列出当前目录文件及子目录名称
catfile1|more以分页方式,列出file1的内容
(2)标准输入控制
执行格式:command-line<file将file作为command-line的输入
例: mail -s“mail test”user@iis.sinica.edu.tw<file1
功能:将文件file1当作信件的内容,subject名称为mail test 送给收信人
(3)标准输出控制
执行格式一:command>filename
功能:将command的执行结果送至指定的filename中
例: ls -l>list将执行”ls -l” 的结果写入文件list中
执行格式二:command>!filename
功能:同上,若filename文件已存在,则强迫重写
执行格式三:command>&filename
功能:将command执行所产生的任何信息写入filename
执行格式四:command>>filename
功能:将command 的执行结果,附加(append)到filename
执行格式五:command>>&filename
????????????功能:将command执行所产生的任何信息附加于filename中
7、其它常用命令
(1)命令在线帮助man
执行格式:mancommand
例:manls查询ls这个指令的用法
(2)设定命令记录表长度history
执行格式一: set history=n
例:set history=40
功能:设定命令记录表长度为40(可记载执行过的前面40个命令)
执行格式二:history查看命令记录表的内容
(3)显示说明info
执行格式:info command-name
例:info gcc
功能:查看gcc的说明,按上下箭头选定菜单,回车进入,"u"键返回上级菜单.
info不加参数则进入最上一级菜单.
四、用cat 命令查看 /proc 动态文件系统目录下的文件,辨识其中的系统信息.
例如: catinterrupts 列出当前中断占用情况
catioports列出设备的硬件IO占用情况
catpci列出pci设备的情况
实验二 LINUX 下C语言使用、编译与调试实验
实验目的
1、复习C语言程序基本知识
2、练习并掌握UNIX提供的vi编辑器来编译C程序
3、学会利用gcc、gdb编译、调试C程序
实验内容
1、用vi编写一个简单的、显示"Hello,World!"的C程序,用gcc编译并观察编译后的结果
2、利用gdb调试该程序
3、运行生成的可执行文件。
实验指导
一、C语言使用简介
LINUX中包含了很多软件开发工具。它们中的很多是用于C和C++应用程序开发的。
C是一种能在UNIX的早期就被广泛使用的通用编程语言。它最早是由Bell实验室的DennisRitchie为了UNIX的辅助开发而写的,从此C就成为世界上使用最广泛的计算机语言。
C能在编程领域里得到如此广泛支持的原因有:
(1)它是一种非常通用的语言,并且它的语法和函数库在不同的平台上都是统一的,对开发者非常有吸引力;
(2)用C写的程序执行速度很快;
(3)C是所有版本UNIX上的系统语言;
二、文件编辑器vi
vi是在UNIX 上被广泛使用的中英文编辑软件。vi是visualeditor的缩写,是UNIX提供给用户的一个窗口化编辑环境。
进入vi,直接执行vi编辑程序即可。
例:$vi test.c
显示器出现vi的编辑窗口,同时vi会将文件复制一份至缓冲区(buffer)。vi先对缓冲区的文件进行编辑,保留在磁盘中的文件则不变。编辑完成后,使用者可决定是否要取代原来旧有的文件。
1、vi的工作模式
vi提供二种工作模式:输入模式(insertmode)和命令模式(commandmode)。使用者进入vi后,即处在命令模式下,此刻键入的任何字符皆被视为命令,可进行删除、修改、存盘等操作。要输入信息,应转换到输入模式。
(1)命令模式
在输入模式下,按ESC可切换到命令模式。命令模式下,可选用下列指令离开vi:
:q! | 离开vi,并放弃刚在缓冲区内编辑的内容 |
:wq | 将缓冲区内的资料写入磁盘中,并离开vi |
:ZZ | 同wq |
:x | 同wq |
:w | 将缓冲区内的资料写入磁盘中,但并不离开vi |
:q | 离开vi,若文件被修改过,则要被要求确认是否放弃修改的内容,此指令可与:w配合使用 |
(2)命令模式下光标的移动
H | 左移一个字符 |
J | 下移一个字符 |
K | 上移一个字符 |
L | 右移一个字符 |
0 | 移至该行的首 |
$ | 移至该行的末 |
^ | 移至该行的第一个字符处 |
H | 移至窗口的第一列 |
M | 移至窗口中间那一列 |
L | 移至窗口的最后一列 |
G | 移至该文件的最后一列 |
W, W | 下一个单词 (W 忽略标点) |
B, B | 上一个单词 (B 忽略标点) |
+ | 移至下一列的第一个字符处 |
- | 移至上一列的第一个字符处 |
( | 移至该句首 |
) | 移至该句末 |
{ | 移至该段首 |
} | 移至该段末 |
NG | 移至该文件的第n列 |
N+ | 移至光标所在位置之后第n列 |
n- | 移至光标所在位置之前第n列 |
(3)输入模式
输入以下命令即可进入vi输入模式:
a(append) | 在光标之后加入资料 |
A | 在该行之末加入资料 |
i(insert) | 在光标之前加入资料 |
I | 在该行之首加入资料 |
o(open) | 新增一行于该行之下,供输入资料用 |
O | 新增一行于该行之上,供输入资料用 |
Dd | 删除当前光标所在行 |
X | 删除当前光标字符 |
X | 删除当前光标之前字符 |
U | 撤消 |
· | 重做 |
F | 查找 |
s | 替换,例如:将文件中的所有"FOX"换成"duck",用":%s/FOX/duck/g" |
ESC | 离开输入模式 |
更多用法见 info vi
三、GNU C编译器
LINUX上可用的C编译器是GNUC编译器,它建立在自由软件基金会编程许可证的基础上,因此可以自由发布。
LINUX 上的GNUC编译器(GCC)是一个全功能的ANCIC兼容编译器,而一般UNIX(如SCOUNIX)用的编译器是CC。下面介绍GCC和一些GCC编译器最常用的选项。
1、使用GCC
通常后跟一些选项和文件名来使用GCC编译器。GCC命令的基本用法如下:
gcc [options] [filenames]
命令行选项指定的编译过程中的具体操作
2、GCC常用选项
GCC有超过100个的编译选项可用,这些选项中的许多可能永远都不会用到,但一些主要的选项将会频繁使用。很多的GCC选项包括一个以上的字符,因此必须为每个选项指定各自的连字符,并且就像大多数LINUX命令一样不能在一个单独的连字符后跟一组选项。例如,下面的命令是不同的:
gcc -p-g test.c
gcc -pg test.c
第一条命令告诉GCC编译test.c时为prof命令建立剖析(profile)信息并且把调试信息加入到可执行文件里。第二条命令告诉GCC只为gprof命令建立剖析信息。
当不用任何选项编译一个程序时,GCC将建立(假定编译成功)一个名为a.out的可执行文件。例如,
gcc test.c
编译成功后,当前目录下就产生了一个a.out文件。
也可用-o选项来为即将产生的可执行文件指定一个文件名来代替a.out。例如:
gcc –ocount count.c
此时得到的可执行文件就不再是a.out,而是count。
GCC也可以指定编译器处理步骤多少。-c选项告诉GCC仅把源代码编译为目标代码而跳过汇编和连接步骤。这个选项使用得非常频繁因为它编译多个C程序时速度更快且更易于管理。默认时GCC建立的目标代码文件有一个.o的扩展名。
3、执行文件
格式:./可执行文件名
例:./a.out
./count
参考程序
main( )
{
printf("Hello,world!n");
}
作业:1编写test程序
流程:
询问用过linux编程吗?
if(回答=‘y’) //scanf( )
then询问会用fork创建子进程吗?
if(回答是y)
产生子进程,子进程运行helloworld可执行程序。
else 显示 自己课下进行学习
父进程 等子进程结束
退出
实验三进程的创建实验
实验目的
1、掌握进程的概念,明确进程的含义
2、认识并了解并发执行的实质
实验内容
1、编写一段程序,使用系统调用fork()创建两个子进程。当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示一个字符:父进程显示'a',子进程分别显示字符'b'和字符'c'。试观察记录屏幕上的显示结果,并分析原因。
2、修改上述程序,每一个进程循环显示一句话。子进程显示'daughter…'及'son ……',父进程显示 'parent……',观察结果,分析原因。
实验准备
(1)阅读LINUX的fork.c源码文件(见附录二),分析进程的创建过程。
(2)阅读LINUX的sched.c源码文件(见附录三),加深对进程管理概念的认识。
实验指导
一、进程
UNIX中,进程既是一个独立拥有资源的基本单位,又是一个独立调度的基本单位。一个进程实体由若干个区(段)组成,包括程序区、数据区、栈区、共享存储区等。每个区又分为若干页,每个进程配置有唯一的进程控制块PCB,用于控制和管理进程。
所涉及的系统调用
1、fork( )
创建一个新进程。
系统调用格式:
pid=fork( )
参数定义:
int fork( )
fork( )返回值意义如下:
0:在子进程中,pid变量保存的fork( )返回值为0,表示当前进程是子进程。
>0:在父进程中,pid变量保存的fork()返回值为子进程的id值(进程唯一标识符)。
-1:创建失败。
如果fork( )调用成功,它向父进程返回子进程的PID,并向子进程返回0,即fork()被调用了一次,但返回了两次。此时OS在内存中建立一个新进程,所建的新进程是调用fork( )父进程(parentprocess)的副本,称为子进程(childprocess)。子进程继承了父进程的许多特性,并具有与父进程完全相同的用户级上下文。父进程与子进程并发执行。
核心为fork( )完成以下操作:
(1)为新进程分配一进程表项和进程标识符
进入fork( )后,核心检查系统是否有足够的资源来建立一个新进程。若资源不足,则fork()系统调用失败;否则,核心为新进程分配一进程表项和唯一的进程标识符。
(2)检查同时运行的进程数目
超过预先规定的最大数目时,fork( )系统调用失败。
(3)拷贝进程表项中的数据
将父进程的当前目录和所有已打开的数据拷贝到子进程表项中,并置进程的状态为“创建”状态。
(4)子进程继承父进程的所有文件
对父进程当前目录和所有已打开的文件表项中的引用计数加1。
(5)为子进程创建进程上、下文
进程创建结束,设子进程状态为“内存中就绪”并返回子进程的标识符。
(6)子进程执行
虽然父进程与子进程程序完全相同,但每个进程都有自己的程序计数器PC(注意子进程的PC开始位置),然后根据pid变量保存的fork()返回值的不同,执行了不同的分支语句。
例:
….. pid=fork( ); if (! pid) printf("I'm the child process!n"); else if (pid>0) printf("I'm the parent process! n"); else printf("Fork fail!n"); …… |
PC |
四、参考程序
1、
#include <stdio.h>
main( )
{
int p1,p2;
while((p1=fork( ))= =-1);
if (p1= =0)putchar('b');
else
{
while((p2=fork( ))= =-1);
if(p2= =0)putchar('c');
elseputchar('a');
}
}
2、
#include <stdio.h>
main( )
{
int p1,p2,i;
while((p1=fork( ))= =-1);
if (p1= =0)
for(i=0;i<10;i++)
printf("daughter %dn",i);
else
{
while((p2=fork( ))= = -1);
if(p2= =0)
for(i=0;i<10;i++)
printf("son %dn",i);
else
for(i=0;i<10;i++)
printf("parent %dn",i);
}
}
五、运行结果
1、bca,bac, abc ,……都有可能。
2、parent…
son…
daughter..
daughter..
或 parent…
son…
parent…
daughter…等
作业:用fork函数创建进程树
实验四 进程互斥实验
实验目的
1、进一步认识并发执行的实质
2、分析进程竞争资源的现象,学习解决进程互斥的方法
实验内容
1、修改实验(一)中的程序2,用lockf( )来给每一个进程加锁,以实现进程之间的互斥
2、观察并分析出现的现象
实验指导
一、所涉及的系统调用
lockf(files,function,size)
用作锁定文件的某些段或者整个文件。
本函数的头文件为
#include "unistd.h"
参数定义:
int lockf(files,function,size)
int files,function;
long size;
其中:files是文件描述符;function是锁定和解锁:1表示锁定,0表示解锁。size是锁定或解锁的字节数,为0,表示从文件的当前位置到文件尾。
二、参考程序
#include <stdio.h>
#include <unistd.h>
main( )
{
int p1,p2,i;
while((p1=fork( ))= = -1);
if (p1= =0)
{
lockf(1,1,0);
for(i=0;i<10;i++)
printf("daughter%dn",i);
lockf(1,0,0);
}
else
{
while((p2=fork( ))= =-1);
if (p2= =0)
{
lockf(1,1,0);
for(i=0;i<10;i++)
printf("son %dn",i);
lockf(1,0,0);
}
else
{
lockf(1,1,0);
for(i=0;i<10;i++)
printf(" parent %dn",i);
lockf(1,0,0);
}
}
}
三、运行结果
parent…
son…
daughter..
daughter..
或parent…
son…
parent…
daughter…
大致与未上锁的输出结果相同,也是随着执行时间不同,输出结果的顺序有所不同。
四、分析原因
上述程序执行时,不同进程之间不存在共享临界资源(其中打印机的互斥性已由操作系统保证)问题,所以加锁与不加锁效果相同。
作业:分析以下程序的输出结果:
#include<stdio.h>
#include<unistd.h>
main()
{
int p1,p2,i;
int *fp;
fp = fopen("to_be_locked.txt"
,"w+");
if(fp==NULL)
{
printf("Failto create file");
exit(-1);
}
while((p1=fork( ))==-1);
if (p1==0)
{
lockf(*fp,1,0);
for(i=0;i<10;i++)
fprintf(fp,"daughter%dn",i);
lockf(*fp,0,0);
}
else
{
while((p2=fork( ))==-1);
if (p2==0)
{
lockf(*fp,1,0);
for(i=0;i<10;i++)
fprintf(fp,"son %dn",i);
lockf(*fp,0,0);
}
else
{
wait(NULL);
lockf(*fp,1,0);
for(i=0;i<10;i++)
fprintf(fp,"parent %dn",i);
lockf(*fp,0,0);
}
}
fclose(fp);
}
cat to_be_locked.txt 查看输出结果
实验五 进程间管道通信
UNIX/LINUX系统的进程间通信机构(IPC)允许在任意进程间大批量地交换数据。本实验的目的是了解和熟悉LINUX支持的信号量机制、管道机制、消息
通信机制及共享存储区机制。
进程的管道通信实验
实验目的
1、了解什么是管道
2、熟悉UNIX/LINUX支持的管道通信方式
实验内容
编写程序实现进程的管道通信。用系统调用pipe( )建立一管道,二个子进程P1和P2分别向管道各写一句话:
Child 1is sending a message!
Child 2is sending a message!
父进程从管道中读出二个来自子进程的信息并显示(要求先接收P1,后P2)。
实验指导
一、什么是管道
UNIX系统在OS的发展上,最重要的贡献之一便是该系统首创了管道(pipe)。这也是UNIX系统的一大特色。
所谓管道,是指能够连接一个写进程和一个读进程的、并允许它们以生产者—消费者方式进行通信的一个共享文件,又称为pipe文件。由写进程从管道的写入端(句柄1)将数据写入管道,而读进程则从管道的读出端(句柄0)读出数据。
句柄fd[0] 句柄fd[1] | 读出端 写入端 |
二、管道的类型:
1、有名管道
一个可以在文件系统中长期存在的、具有路径名的文件。用系统调用mknod()建立。它克服无名管道使用上的局限性,可让更多的进程也能利用管道进行通信。因而其它进程可以知道它的存在,并能利用路径名来访问该文件。对有名管道的访问方式与访问其他文件一样,需先用open()打开。
2、无名管道
一个临时文件。利用pipe()建立起来的无名文件(无路径名)。只用该系统调用所返回的文件描述符来标识该文件,故只有调用pipe()的进程及其子孙进程才能识别此文件描述符,才能利用该文件(管道)进行通信。当这些进程不再使用此管道时,核心收回其索引结点。
二种管道的读写方式是相同的,本文只讲无名管道。
3、pipe文件的建立
分配磁盘和内存索引结点、为读进程分配文件表项、为写进程分配文件表项、分配用户文件描述符
4、读/写进程互斥
内核为地址设置一个读指针和一个写指针,按先进先出顺序读、写。
为使读、写进程互斥地访问pipe文件,需使各进程互斥地访问pipe文件索引结点中的直接地址项。因此,每次进程在访问pipe文件前,都需检查该索引文件是否已被上锁。若是,进程便睡眠等待,否则,将其上锁,进行读/写。操作结束后解锁,并唤醒因该索引结点上锁而睡眠的进程。
三、所涉及的系统调用
1、pipe( )
建立一无名管道。
系统调用格式
pipe(filedes)
参数定义
int pipe(filedes);
int filedes[2];
其中,filedes[1]是写入端,filedes[0]是读出端。
该函数使用头文件如下:
#include <unistd.h>
#inlcude <signal.h>
#include <stdio.h>
2、read()
系统调用格式
read(fd,buf,nbyte)
功能:从fd所指示的文件中读出nbyte个字节的数据,并将它们送至由指针buf所指示的缓冲区中。如该文件被加锁,等待,直到锁打开为止。
参数定义
intread(fd,buf,nbyte);
int fd;
char *buf;
unsigned nbyte;
3、write( )
系统调用格式
read(fd,buf,nbyte)
功能:把nbyte 个字节的数据,从buf所指向的缓冲区写到由fd所指向的文件中。如文件加锁,暂停写入,直至开锁。
参数定义同read( )。
四、参考程序
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
int pid1,pid2;
main( )
{
int fd[2];
char outpipe[100],inpipe[100];
pipe(fd);
while ((pid1=fork( ))= =-1);
if(pid1= =0)
{
lockf(fd[1],1,0);
sprintf(outpipe,"child 1 process is sending message!");
write(fd[1],outpipe,50);
sleep(5);
lockf(fd[1],0,0);
exit(0);
}
else
{
while((pid2=fork( ))= =-1);
if(pid2==0)
{lockf(fd[1],1,0);
sprintf(outpipe,"child 2process is sending message!");
write(fd[1],outpipe,50);
sleep(5);
lockf(fd[1],0,0);
exit(0);
}
else
{wait(0);
read(fd[0],inpipe,50);
printf("%sn",inpipe);
wait(0);
read(fd[0],inpipe,50);
printf("%sn",inpipe);
exit(0);
}
}
}
五、运行结果
延迟5秒后显示
child 1 process is sending message!
再延迟5秒
child2 process is sending message!
六、思考题
1、程序中的sleep(5)起什么作用?
2、子进程1和2为什么也能对管道进行操作?
实验六Linux进程间通信--消息队列
实验目的:掌握利用消息队列实现进程通信
实验内容:创建消息队列,允许一个进程向它写消息,另一个进程从中读消息
实验参考: 消息队列的实现包括创建和打开队列、添加消息、读取消息和控制消息队列这四种操作。 |
实验七进程共享存储区通信通信
实验目的
了解和熟悉共享存储机制
实验内容
编制一长度为1k的共享存储区发送和接收的程序。
实验指导
一、共享存储区
1、共享存储区机制的概念
共享存储区(ShareMemory)是UNIX系统中通信速度最高的一种通信机制。该机制可使若干进程共享主存中的某一个区域,且使该区域出现(映射)在多个进程的虚地址空间中。另一方面,一个进程的虚地址空间中又可连接多个共享存储区,每个共享存储区都有自己的名字。当进程间欲利用共享存储区进行通信时,必须先在主存中建立一共享存储区,然后将它附接到自己的虚地址空间上。此后,进程对该区的访问操作,与对其虚地址空间的其它部分的操作完全相同。进程之间便可通过对共享存储区中数据的读、写来进行直接通信。图示列出二个进程通过共享一个共享存储区来进行通信的例子。其中,进程A将建立的共享存储区附接到自己的AA’区域,进程B将它附接到自己的BB’区域。
进程A的虚空间内存空间进程B的虚空间
正文 数据 B B’ 栈 |
正文 数据 栈 |
共享 |
A
A’
应当指出,共享存储区机制只为进程提供了用于实现通信的共享存储区和对共享存储区进行操作的手段,然而并未提供对该区进行互斥访问及进程同步的措施。因而当用户需要使用该机制时,必须自己设置同步和互斥措施才能保证实现正确的通信。
二、涉及的系统调用
1、shmget( )
创建、获得一个共享存储区。
系统调用格式:
shmid=shmget(key,size,flag)
该函数使用头文件如下:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
参数定义
int shmget(key,size,flag);
key_t key;
int size,flag;
其中,key是共享存储区的名字;size是其大小(以字节计);flag是用户设置的标志,如IPC_CREAT。IPC_CREAT表示若系统中尚无指名的共享存储区,则由核心建立一个共享存储区;若系统中已有共享存储区,便忽略IPC_CREAT。
附:
操作允许权八进制数
用户可读00400
用户可写00200
小组可读00040
小组可写00020
其它可读00004
其它可写00002
控制命令值
IPC_CREAT0001000
IPC_EXCL0002000
例:shmid=shmget(key,size,(IPC_CREAT|0400))
创建一个关键字为key,长度为size的共享存储区
2、shmat( )
共享存储区的附接。从逻辑上将一个共享存储区附接到进程的虚拟地址空间上。
系统调用格式:
virtaddr=shmat(shmid,addr,flag)
该函数使用头文件如下:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
参数定义
char *shmat(shmid,addr,flag);
int shmid,flag;
char * addr;
其中,shmid是共享存储区的标识符;addr是用户给定的,将共享存储区附接到进程的虚地址空间;flag规定共享存储区的读、写权限,以及系统是否应对用户规定的地址做舍入操作。其值为SHM_RDONLY时,表示只能读;其值为0时,表示可读、可写;其值为SHM_RND(取整)时,表示操作系统在必要时舍去这个地址。该系统调用的返回值是共享存储区所附接到的进程虚地址viraddr。
3、shmdt( )
把一个共享存储区从指定进程的虚地址空间断开。
系统调用格式:
shmdt(addr)
该函数使用头文件如下:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
参数定义
int shmdt(addr);
char addr;
其中,addr是要断开连接的虚地址,亦即以前由连接的系统调用shmat()所返回的虚地址。调用成功时,返回0值,调用不成功,返回-1。
4、shmctl( )
共享存储区的控制,对其状态信息进行读取和修改。
系统调用格式:
shmctl(shmid,cmd,buf)
该函数使用头文件如下:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
参数定义
intshmctl(shmid,cmd,buf);
intshmid,cmd;
structshmid_ds *buf;
其中,buf是用户缓冲区地址,cmd是操作命令。命令可分为多种类型:
(1)用于查询有关共享存储区的情况。如其长度、当前连接的进程数、共享区的创建者标识符等;
(2)用于设置或改变共享存储区的属性。如共享存储区的许可权、当前连接的进程计数等;
(3)对共享存储区的加锁和解锁命令;
(4)删除共享存储区标识符等。
上述的查询是将shmid所指示的数据结构中的有关成员,放入所指示的缓冲区中;而设置是用由buf所指示的缓冲区内容来设置由shmid所指示的数据结构中的相应成员。
三、参考程序
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#define SHMKEY 75
intshmid,i; int*addr;
void client( )
{ int i;
shmid=shmget(SHMKEY,1024,0777);
addr=shmat(shmid,0,0);
for (i=9;i>=0;i--)
{ while (*addr!=-1);
printf("(client) sentn");
*addr=i;
}
exit(0);
}
void server( )
{
shmid=shmget(SHMKEY,1024,0777|IPC_CREAT);
addr=shmat(shmid,0,0);
do
{
*addr=-1;
while (*addr==-1);
printf("(server) receivedn");
}while (*addr);
shmctl(shmid,IPC_RMID,0);
exit(0);
}
main( )
{
while ((i=fork( ))==-1);
if (!i) server( );
system(“ipcs -m”);
while ((i=fork( ))==-1);
if (!i) client( );
wait(0);
wait(0);
}
四、程序说明
1、为了便于操作和观察结果,用一个程序作为“引子“,先后fork()两个子进程,server和client,进行通信。
2、server端建立一个key为75的共享区,并将第一个字节置为-1,作为数据空的标志。等待其他进程发来的消息。当该字节的值发生变化时,表示收到了信息,进行处理。然后再次把它的值设为-1,如果遇到的值为0,则视为为结束信号,取消该队列,并退出server。server每接收到一次数据后显示“(server)received”。
3、client端建立一个key为75的共享区,当共享取得第一个字节为-1时,server端空闲,可发送请求。client随即填入9到0。期间等待server 端的再次空闲。进行完这些操作后,client退出。client每发送一次数据后显示“(client)sent”。
4、父进程在server和client均退出后结束。
五、运行结果
和预想的完全一样。但在运行过程中,发现每当client发送一次数据后,server要等待大约0.1秒才有响应。同样,之后client又需要等待大约0.1秒才发送下一个数据。
作业:程序分析
出现上述应答延迟的现象是程序设计的问题。当client端发送了数据后,并没有任何措施通知server端数据已经发出,需要由client的查询才能感知。此时,client端并没有放弃系统的控制权,仍然占用CPU的时间片。只有当系统进行调度时,切换到了server进程,再进行应答。这个问题,也同样存在于server端到client的应答过程中。
实验八 线程同步
实验目的:掌握创建线程的函数,以及pv操作。
实验内容:用线程编写程序实现生产者和消费者问题(一定容量的缓冲区)
实验参考:生产者消费者问题描述:
有一个有限缓冲区和两个线程:生产者和消费者。他们分别把产品放入缓冲区和从缓冲区中拿走产品。当一个生产者在缓冲区满时必须等待,当一个消费者在缓冲区空时也必须等待:
(1)信号量的考虑
这里使用3个信号量,其中两个信号量avail和full分别用于解决生产者和消费者线程之间的同步问题,mutex是用于这两个线程之间的互斥问题。其中avail初始化N(有界缓冲区的空单元数),mutex初始化为1,full初始化为0。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <sys/ipc.h>
#include <semaphore.h>
#include <fcntl.h>
#include <string.h>
#define FIFO "myfifo"
#define N 5
int lock_var;
time_t end_time;
char buf_r[100];
sem_t mutex,full,avail;
int fd;
void pthread1(void *arg);
void pthread2(void *arg);
void productor(void *arg);
void consumer(void *arg);
int main(int argc,char *argv[])
{
pthread_t id1,id2;
pthread_t mon_th_id;
int ret;
end_time = time(NULL) + 30;
if((mkfifo(FIFO,O_CREAT|O_EXCL)<0)&& (errno!=EEXIST))
printf("cannot create fifoservern");
printf("Preparing for reading bytes...n");
memset(buf_r,0,sizeof(buf_r));
fd = open(FIFO,O_RDWR | O_NONBLOCK,0);
if(fd==-1)
{
perror("open error!n");
exit(1);
}
ret = sem_init(&mutex,0,3);
ret = sem_init(&avail,0,N);
ret = sem_init(&full,0,0);
if(ret!=0)
{
perror("sem_init error n");
}
printf("mid pthreadn");
ret = pthread_create(&id1,NULL,(void*)productor,NULL);
if(ret!=0)
perror("pthread create 1n");
ret = pthread_create(&id2,NULL,(void*)consumer,NULL);
if(ret !=0)
perror("pthread create 2n");
pthread_join(id1,NULL);
pthread_join(id2,NULL);
exit(0);
}
void productor(void *arg)
{
int i,nwrite;
while(time(NULL)<end_time)
{
sem_wait(&avail);
sem_wait(&mutex);
if((nwrite=write(fd,"hello",5))==-1)
{
if(errno==EAGAIN)
printf("The FIFO has not been read yet.Please trylatern");
}
else
printf("write hello to the FIFOn");
sem_post(&full);
sem_post(&mutex);
sleep(1);
}
}
void consumer(void *arg)
{
int nolock = 0;
int ret,nread;
while(time(NULL)<end_time)
{
sem_wait(&full);
sem_wait(&mutex);
memset(buf_r,0,sizeof(buf_r));
if((nread=read(fd,buf_r,100))==1)
{
if(errno==EAGAIN)
printf("no data yetn");
}
printf("read %s from FIFOn",buf_r);
sem_post(&avail);
sem_post(&mutex);
sleep(1);
}
}
作业:给上面程序加注释,并分析该程序运行结果。
实验九 银行家算法实现
实验目的:通过编程实现银行家算法,掌握避免死锁的原理和实现
实验内容:用c语言实现银行家算法,验证教程例子的正确性。
实验准备:参考书本银行家算法数据结构和算法分析
参考程序:第一部分:银行家算法(扫描)
1.如果Request<=Need,则转向2;否则,出错
2.如果Request<=Available,则转向3,否则等待
3.系统试探分配请求的资源给进程
4.系统执行安全性算法
第二部分:安全性算法
1.设置两个向量
(1).工作向量:Work=Available(表示系统可提供给进程继续运行所需要的各类资源数目)
(2).Finish:表示系统是否有足够资源分配给进程(True:有;False:没有).初始化为False
2.若Finish[i]=False&&Need<=Work,则执行3;否则执行4(I为资源类别)
3.进程P获得第i类资源,则顺利执行直至完成!并释放资源:
Work=Work+Allocation;
Finish[i]=true;
转2
4. 若所有进程的Finish[i]=true,则表示系统安全;否则,不安全!
程序说明:
一、开发语言:C语言
二、本程序模拟的是课本P97"银行家算法之例"编写的。
即系统中有五个进程{0,1,2,3,4}和三类资源{A,B,C},各进程的初始资源分配情况及各资源的总数量均参照该例子。
三、本程序中共四个函数分别为:zhibiao(),apply(),test(),及主函数main()
其中:
1、zhibiao()的功能为:直观的列出系统某时刻的资源分配情况。
2、apply()的功能为:进程发出资源请求后,系统试探着把资源分配给该进程并修改数据结构中的数值。
3、test()的功能为:系统的安全性检测。
四、需要注意的问题:
1、本程序的前提是假设当前只有一个进程请求资源。
2、一旦操作不符合要求会得到意想不到的结果!再按任意键可能该程序也不能正常运行,则就关闭该程序,然后再打开重新运行便可.
五、课本上的例子在这个程序中运行后成功。
例如:P1:Request1(1,0,2)
p4: Request4(3,3,0)
P0:Request0(0,2,0)
再测试P0:Request0(0,1,0) 同课本上的结果相同。
本程序还有很多不足,功能不很完善,只是适合于当前只有一个进程请求资源,且进程和资源都是事先固定好的.
源代码:
#include "dos.h"
#include "conio.h"
#include "alloc.h"
int available[3]={3,3,2};
int max[6][3]={{7,5,3},{3,2,2},{9,0,2},{2,2,2},{4,3,3}};
intallocation[6][3]={{0,1,0},{2,0,0},{3,0,2},{2,1,1},{0,0,2}};
int need[6][3]={{7,4,3},{1,2,2},{6,0,0},{0,1,1},{4,3,1}};
int live[5]={1,1,1,1,1};
int stay[3][3];
int request[3],process[5];
int i,j,p=5,pro,x,k1;
void zhibiao()
{ x=1;
printf("MaxAllNeeAvan");
printf("PROA BCA BCA BCA B Cn");
for(i=0;i<p;i++)
{ if(live[i]==1)
{printf("P%d",i);
for(j=0;j<=2;j++)
{printf("%-3d",max[i][j]);}printf("");
for(j=0;j<=2;j++)
{printf("%-3d",allocation[i][j]);}printf("");
for(j=0;j<=2;j++)
{printf("%-3d",need[i][j]);}printf("");
}
if(x==1&&live[i]==1){printf("%-3d%-3d%-3d",available[0],available[1],available[2]);x=0;}
if(live[i]==1) printf("n");
}
}
int apply()
{ int v=1;
for(i=0;i<=2;i++)
{if(need[pro][i]<request[i]){v=0;break;}
if(available[i]<request[i]){v=0;break;}}
if(v==1){for(i=0;i<=2;i++)
{stay[0][i]=allocation[pro][i];allocation[pro][i]+=request[i];
stay[1][i]=need[pro][i];need[pro][i]-=request[i];
stay[2][i]=available[i];available[i]-=request[i];}
}
return(v);
}
int test()
{ int work[3],finish[5]={0,0,0,0,0},v,k=0,t=0;
for(i=0;i<p;i++)
if(live[i]==0) {finish[i]=1;k1--;}
for(i=0;i<=2;i++)work[i]=available[i];
while(1)
{ for(i=0;i<=4;i++)
{ if(finish[i]==0) {v=1;
for(j=0;j<=2;j++)
if(need[i][j]>work[j]){v=0;break;}
if(v==1) { finish[i]=1;
for(j=0;j<=2;j++)work[j]+=allocation[i][j];
process[k]=i;k++;
}
}
}
if(t==k) break;
else t=k;
if(k==k1) break;
}
if(k==k1) return(1);
elsereturn(0);
}
main()
{
while(1)
{zhibiao();
k1=5;
printf("该系统中共五个进程{0,1,2,3,4},请输入其中一个进程:P");
scanf("%d",&pro);
if(pro<0||pro>4)break;
printf("P%d请求的三类资源数(资源数用逗号隔开)分别是:",pro);
scanf("%d,%d,%d",&request[0],&request[1],&request[2]);
if(apply()==0){printf("出错或尚无足够资源,P%d必须等待!n",pro);
printf("请按任意键继续。");}
elseif(test()==0) {printf("系统处于不安全状态,P%d必须等待!n",pro);
for(i=0;i<=2;i++)
{ allocation[pro][i]=stay[0][i];
need[pro][i]=stay[1][i];
available[i]=stay[2][i];}
printf("请按任意键继续。");}
else{zhibiao();printf("系统处于安全状态!n");
printf("存在一安全序列为:");
for(i=0;i<k1-1;i++)
printf("P%d>",process[i]);
printf("P%d",process[k1-1]);
printf("n请按任意键继续。");}
if(need[pro][0]==0&&need[pro][1]==0&&need[pro][2]==0)
for(i=0;i<=2;i++)
{available[i]+=max[pro][i];
live[pro]=0;}
getch();
clrscr();
delay(5000);
}
}
实验十 模拟分页式虚拟存储管理
一、实验内容
模拟分页式虚拟存储管理中硬件的地址转换和缺页中断,以及选择页面调度算法处理缺页中断。
二、实验目的
在计算机系统中,为了提高主存利用率,往往把辅助存储器(如磁盘)作为主存储器的扩充,使多道运行的作业的全部逻辑地址空间总和可以超出主存的绝对地址空间。用这种办法扩充的主存储器称为虚拟存储器。通过本实验帮助同学理解在分页式存储管理中怎样实现虚拟存储器。
三、实验题目
模拟分页式存储管理中硬件的地址转换和产生缺页中断。
[提示]
(1)分页式虚拟存储系统是把作业信息的副本存放在磁盘上,当作业被选中时,可把作业的开始几页先装入主存且启动执行。为此,在为作业建立页表时,应说明哪些页已在主存,哪些页尚未装入主存,页表的格式为:
页号标志 主存块号 在磁盘上的位置
其中,标志----用来表示对应页是否已经装入主存,标志位=1,则表示该页已经在主存,标志位=0,则表示该页尚未装入主存。
主存块号----用来表示已经装入主存的页所占的块号。
在磁盘上的位置----用来指出作业副本的每一页被存放在磁盘上的位置。
(2)作业执行时,指令中的逻辑地址指出了参加运算的操作存放的页号和单元号,硬件的地址转换机构按页号查页表,若该页对应标志为“1”,则表示该页已在主存,这时根据关系式:
绝对地址=块号×块长+单元号
计算出欲访问的主存单元地址。如果块长为 2的幂次,则可把块号作为高地址部分,把单元号作为低地址部分,两者拼接而成绝对地址。若访问的页对应标志为“0”,则表示该页不在主存,这时硬件发“缺页中断”信号,有操作系统按该页在磁盘上的位置,把该页信息从磁盘读出装入主存后再重新执行这条指令。
(3)设计一个“地址转换”程序来模拟硬件的地址转换工作。当访问的页在主存时,则形成绝对地址,但不去模拟指令的执行,而用输出转换后的地址来代替一条指令的执行。当访问的页不在主存时,则输出“*该页页号”,表示产生了一次缺页中断。
该模拟程序的算法如图 2-1。
程序代码:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
struct pcb
{
int num;//页号
int flag;//标志
int mainmemblock;//主存块号
int position;//在磁盘上的位置
}pcb[7];
struct job
{
int pages;
int units;
}job[12];
int pc=0;
int page=0;//页号
int blocknum;//块号
int blocklong=128;//块长
int unitsnum;//单元号
void init()
{
int num1[7]={1,1,1,1,0,0,0}; //初始化存储器状态
int num2[7]={5,8,9,1};
int num3[7]={11,12,13,21,22,23,121};
for(int i=0;i<7;i++)
{
pcb[i].num=i;
pcb[i].flag=num1[i];
pcb[i].mainmemblock=num2[i];
pcb[i].position=num3[i];
}
int page[12]={0,1,2,3,0,6,4,5,1,2,4,6};
intunit[12]={70,50,15,21,56,40,53,23,37,78,1,84};
for(i=0;i<12;i++)
{
job[i].pages=page[i];
job[i].units=unit[i];
}
}
int absaddr() //计算实际地址
{
int n=blocknum*blocklong+unitsnum;
return n;
}
void program()
{
pc=0;
int d;
for(pc=0;pc<12;pc++)
{
page=job[pc].pages;
if(pcb[page].flag==1)//如果该块已在内存中,
{
blocknum=pcb[page].mainmemblock;
blocklong=128;
unitsnum=job[pc].units;
d=absaddr();
printf("绝对地址=%dn",d);
}
else//不在内存中则出现缺页错误
{
printf("*%dn",page);
}
}
}
int main()
{
init();
program();
return 0;
}
运行结果如下:
实验十一 分区存储管理系统
存储器是计算机系统的重要组成部分。近年来,随着计算机软件规模的不断扩
大,系统对存储器尤其是内存的要求也越来越高,操作系统如何对它加以有效管理,最大限度地发挥内存这种宝贵而又紧俏的资源至关重要。事实上,操作系统对内存的分配方式有多种,不同的方式特点不一,但由于内存分配是看不见的,不易增加学生的感性认识。为此,采用C编程,模拟内存的固定分区分配,可以使学生能通过图形化的分配界面,加深对内存分配原理的理解。
1。设计目的:在熟练掌握计算机分区存储管理方式的原理的基础上,利用
C程序设计语言在linux操作系统下模拟实现操作系统的分区存储
管理的功能,一方面加深对原理的理解,另一方面提高根据已有原
理通过编程解决实际问题的能力,为进行系统软件开发和针对实际
问题提出高效的软件解决方案打下基础。
2.方法:从整体上看,这个分区管理系统采用的是固定分区法,该方法
实现相对简单。在分区分配与回收中,采用了最先适应法进行。
3.结果: 比较满意的达到了原先设计的要求
4.结论:通过这次课程设计我练习了用C语言写系统软件,对OS中分区
存储管理有了更深刻的了解.
存储器是计算机系统的重要资源之一。因为任何程序和数据以及各种控制的数据结构都必须占用一定的存储空间,因此,存储管理直接影响系统性能。
分区管理是把内存划分成若干个大小不等的区域,除操作系统占用一个区域外,其余由多道环境下的各并发进程共享。分区管理是满足多道程序设计的一中最简单的存储管理方法。
固定分区时的分配与回收较为简单,当用户程序要装入执行时,通过请求表提出内存分配要求的内存空间大小。存储管理程序根据请求表查询分区说明表,从中找出一个满足的空闲分区,并将其分配给申请者。
1课程设计说明
这个系统用的是C语言完成的,可以在windows或者是linux环境下运行。而且该系统采用的是固定分区法,实现比较简单,但它的优点就是易于实现而且不需要硬件的支持。因此,它的简洁,易用。
2.1 设计任务与指标
2.1.1设计任务:在linux操作系统下,采用C编程,模拟内存的固定分区法设计一管理系统,该系统能进行存储管理的设置分区,分配分区,回收分区等功能。
2.1.2设计指标:模拟固定分区存储管理的分配与回收。固定分区就是把内存区固定地划分为若干个大小不等的区域。分区一旦划分结束,在整个执行过程中每个分区的长度和内存的总分区个数将保持不变。当用户进程提出内存分配要求时,找到满足要求的分区分给它,进程执行完后,回收进程和它占有的资源。
2.2 设计原理和方法
2.2.1设计原理:固定分区存储管理技术的基本概念是把主存分成若干个固定大小的存储区(存储块),每个存储区分给某一个作业使用,直到该作业完成后才把该存储区归还系统。在多道固定分区的情况下,所有相关的存储器信息都被保存在存储分配表(MBT)中,该表具体包括:大小,指出存储块的大小;位置,指出存储块在主存中的起始位置;状态,表明该块是否已被使用。
固定分区法的原理图如下:
固定分区法的流程图如下:
2.2.2设计方法:固定分区法,其分配与回收则用最先适应法。
2.3实验环境与问题
2.3.2实验问题:
我在本次实验中发现程序在运行时有两个主要问题:、
(1) 在设置分区时,当输入的分区之和大与存储空间时,仍然能继续进行工作。
解决方法:在判断条件后加一个标志量,当条件满足大与空间时,标志量变为“1”,
跳出后,不进行打印。
(2)在回收分区时,当输入的回收地址后,结果却回收另一个分区。
错误分析:原来是在判断条件“v=n-1”中漏了一个“=”,应为“v=n-1”。
通过本次上机,使我对固定分区分配方法有了具体的认识,形象地理解了它的过程。同时我发现与同学和老师交流是很重要的,能使自己获益良多。希望以后能解决所有发现的问题。
2.4实验过程及结果
① 进入系统界面:
② 选择“1”,进行分区设置(根据提示输入分区块数与各分区的大小):
③ 选择“2”,进行分区分配(根据提示输入分配分区的大小):
④ 选择“3”,进行分区回收(根据提示输入回收分区的地址):
⑤ 选择除“1”,“2”,“3”的其他数字实验将结束。
2源程序代码
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#define MAX 1024
typedef struct node
{ intaddr,use,free;
}LINK;
int n;
setblock(LINK*s)
{
int i,t=MAX,l=0;
printf("the total space is:%dn",MAX);
printf("how many block?");
scanf("%d",&n);
s[0].addr=1;
for(i=0;i<n;i++)
{
if(i<n-1)
{printf("the %d block's spacen",i);
scanf("%d",&s[i].free);
t=t-s[i].free;
if(t<0)
{printf("fail!!the space is not enough!n");l=1;break;}
s[i+1].addr=s[i].free+s[i].addr;
}
else s[i].free=t;
}
if(l!=1)
print(s);
}
int print(LINK*s)
{int index=1,i=0;
printf("indexaddressusefreen");
printf("=======================================n");
while(i<n)
{
printf("%-12d%-12d%-12d%-12dn",index,s[i].addr,s[i].use,s[i].free);
printf("--------------------------------------n");
i++;index++;
}
}
getblock(LINK*s)
{int ap,i,fag=0;
printf("input the application:");
scanf("%d",&ap);
for(i=0;i<n;i++)
{if(ap>s[i].free)
{continue;}
else
{s[i].use=s[i].use+ap;s[i].free=s[i].free-ap;fag=1;break;}
}
if(fag==0)
{printf("failed!!n"); }
print(s);
}
freeblock(LINK*s)
{int ad1,v;
printf("enter the address:");
scanf("%d",&ad1);
v=search(s,ad1);
if(v==n-1)
{ s[v-1].free=s[v-1].free+s[v].free;
s[v].addr=s[v].free+s[v].addr;
s[v].free=0;
}
else
{ s[v+1].free=s[v+1].free+s[v].free;
s[v+1].addr=s[v+1].addr-s[v].free;
s[v].free=0;
}
print(s);
}
int search(LINK *s,intm)
{int i;
for(i=0;i<n;i++)
{ if(s[i].addr==m)
{returni;
}
}
}
main()
{ int k;
static LINK a[10];
do
{ printf("1.......set blockn");
printf("2.......get blockn");
printf("3.......free blocknn");
printf("choose which?");
scanf("%d",&k);
switch(k)
{case 1:setblock(a);break;
case 2:getblock(a);break;
case 3:freeblock(a);break;
default:printf("enter the right number"); break;
}
}while(k==1||k==2||k==3);
}
[4]课程设计网存储管理分区报告http://www.ut365.com/cyuyan/23.html
实验十二 文件系统
实验目的
1、熟悉文件系统编程
实验内容
用c语言编程程序,实现文件创建、读写等功能。
实验参考:文件名filetool.c
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<syslog.h>
#include<string.h>
#include<stdlib.h>
#define MAX 128
int chmd();
int chmd ()
{
int c;
mode_t mode=S_IWUSR;
printf(" 0. 0700n 1. 0400n 2. 0200 n 3. 0100n ");//还可以增加其它权限
printf("Please input your choice(0-3):");
scanf("%d",&c);
switch(c)
{
case 0: chmod("file1",S_IRWXU);break;
case 1: chmod("file1",S_IRUSR);break;
case 2: chmod("file1",S_IWUSR);break;
case 3: chmod("file1",S_IXUSR);break;
default:printf("You have a wrongchoice!n");
}
return(0);
}
main()
{
int fd;
int num;
int choice;
char buffer[MAX];
struct stat buf;
char* path="/bin/ls";
char* argv[4]={"ls","-l","file1",NULL};
while(1)
{
printf("********************************n");
printf("0. 退出n");
printf("1. 创建新文件n");
printf("2. 写文件n");
printf("3. 读文件n");
printf("4. 修改文件权限n");
printf("5.查看当前文件的权限修改文件权限n");
printf("********************************n");
printf("Please input your choice(0-6):");
scanf("%d",&choice);
switch(choice)
{
case0:close(fd);//关闭file1文件
exit(0);
case 1:
fd=open("file1",O_RDWR|O_TRUNC|O_CREAT,0750);
if(fd==-1)
printf("File Create Failed!n");
else
printf("fd =%dn",fd);
break;
case 2:
num=read(0,buffer,MAX); //从键盘里面读取最多128个字符
write(fd,buffer,num);//把读入的信息送到file1里面去
break;
case 3:
read(fd,buffer,MAX);
write(1,buffer,num);
break;
case 4:
chmd ();
printf("Change mode success!n");
break;
case 5:
execv(path,argv);//执行ls –l file1
break;
default:
printf("You have a wrong choice!n");
}
}
}
运行结果:
1. 选择0退出:
2. 选择1创建,选择2写文件:
3. 选择3读文件,选择5查看文件权限:
4. 再选择4修改权限为文件主只读(0200),然后选择5再查看修改后的权限:
参考实验 配置vsftp
配置vsftp 配置文件
[root@lab 4]# cd /etc
[root@lab etc]# vi vsftpd/vsftpd.conf
配置文件的主要内容如下:根据你的需要配置,其中红色的需要重点注意
原始内容 我们配置的内容 说明
anonymous_enable=YES是否允许匿名 ftp,如否选择 NO
local_enable=YES是否允许本地用户登录
write_enable=YES是否开放本地用户的写权限
local_umask=022设置本地用户的文件的掩码是 022,
默认值是 077
#anon_upload_enable=YES anon_upload_enable=YES是否允许匿名用户上传文件
#anon_mkdir_write_enable anon_mkdir_write_enable=YES是否允许匿名用户创建新文件夹
dirmessage_enable=YES是否显示目录说明文件,默认是YES但需要手工创建.message 文件
xferlog_enable=YES激活上传下载日志
connect_from_port_20=YE S 启用FTP 数据端口的连接请求
#chown_uploads=YES是否改变上传文件的属主,如果是
#chown_username= 需要输入一个系统用户名,可以把
whoever 上传的文件都改成 root属主
#xferlog_file=/var/log/vsftp传输日志的路径和名字默认是d.log /var/log/vsftpd.log
xferlog_std_format=YES是否使用标准的 ftp xferlog 模式
#idle_session_timeout=600设置默认的断开不活跃 session 的时间
#data_connection_timeout= data_connection_timeout= 设置数据传输超时时间120 120
#nopriv_user=ftpsecure运行 vsftpd 需要的非特权系统用户默认是 nobody
#ascii_upload_enable=YES是否使用 ascii 码方式上传和下载
#ascii_download_enable= YES 文件
#ftpd_banner=Welcome定制欢迎信息 to blah FTP service.
#deny_email_enable=YES是否允许禁止匿名用户使用某些邮
#banned_email_file= 件地址,如果是,输入禁止的邮件
/etc/vsftpd.banned_emails 地址的路径和文件名
#chroot_list_enable=YESchroot_list_enable=YES 是否将系统用户限止在自己的
#chroot_list_file=chroot_list_file= home 目录下,如果选择了 yes 那么
/etc/vsftpd.chroot_list/etc/vsftpd.chroot_listchroot_list_file=/etc/vsftpd.chroot_li还需要手工创建 st 中列出的是不 chroot的用户的列
/etc/vsftpd.chroot_list,并且加入需要限制的用户
pam_service_name=vsftpd设置 PAM 认证服务的配置文件名称该文件存放在/etc/pam.d/目录下
userlist_enable=YES由于默认情况下
userlist_deny=YES, 所以
/etc/vsftpd.user_list文件中所列出的用户不允许访问 vsftpd服务器
listen=YES使 vsftpd 处于独立启动模式
tcp_wrappers=YES使用 tcp_wrappers 作为主机的访问控制方式
anon_world_readable_only 开放匿名用户的浏览权限 =NO
anon_other_write_enable= 允许匿名用户对服务器上的文件或
YES 文件夹有更名或删除操作的权限
max_clients=200指定服务器总的并发连接数
max_per_ip=2指定每个客户机的最大连接数
accept_timeout=120 空闲 2分钟后自动中断连接
local_max_rate=80000本地用户最大传输速率(KB/s)
anon_max_rate=80000匿名用户最大传输速率(KB/s)
<3>,启动服务器
[root@lab etc]# /etc/init.d/vsftpdstart 或 service vsftpdstart
<4>,修改匿名用户上传目录的权限
匿名用户的默认目录是“/var/ftp/pub”。使用下面的命令修改:
[root@lab etc]# chmod +777 /var/ftp/pub
<5>,测试
在 windows 系统下,使用 FTP工具测试