实验1 Linux基本命令
实验目的
1 2 3 4 5 1.
掌握进入和退出Linux系统过程。 掌握Linux系统文件操作命令的使用。 掌握Linux下进程管理命令的使用。 了解Linux下的用户间通信命令。 了解Linux下的联机帮助。
系统管理员在服务器上安装Linux操作系统并配置网络环境。
实验准备
2. 每一个终端用户建立帐号并设置好口令。 实验内容
1.Linux的注册和注销 (1)注册
在Windows环境下,点击“开始”→“运行”,如图1-1所示。在相应的“打开”输入框内输入Telnet命令以便于与服务器建立连接,然后按“确定”按钮,如图1-2所示。
例如:如果服务器的IP为192.168.0.1,则输入: Telnet 192.168.0.1
图1-1 启动telne图 图1-2 运行telnet 界面
一般此时需要等待登录提示,然后,屏幕上出现登陆提示符: login:
输入用户名,一般学生上机的用户名和密码均为: user+(学号的后两位+(班级号-1)*30)
例如:1班的学号后两位为26的学生的用户名和密码为
1
“user26”; 3班的学号后两位为26的学生的用户名和密码为“user86”。
输入用户名后,如果用户名正确,会出现密码提示符Passward: 注意:在输入密码的过程中,屏幕上不显示任何字符。 如果用户名和密码全部正确,出现Linux的shell提示符“$”,此时可以输入Linux的各种命令。表明系统已经准备好接受您的命令,这时系统在SHELL控制之下;若输入的口令与用户名不符,则系统提示注册失败,并允许重新注册,根据系统的默认的设置,若三次注册失败,则系统自动将帐户和终端封锁。 (2) 注销:
当用户完成所要做的工作以后,要退出LINUX操作系统时,应该通过注销通知系统,这样LINUX系统将释放用户占用的终端供他人使用。
LINUX有两种不同的注销过程
①命令方式:在$提示符下打入:exit命令 ②利用组合键:CTRL/D
此时屏幕上重新出现提示符login:,表明用户已从LINUX系统中退出。
2.Linux的文件操作命令
LINUX操作系统是一个简单易懂的系统,其命令是以众多可执行程序的形式驻留在系统目录下,每个用户可以在自己的目录下使用LINUX系统命令。
在LINUX系统中,标准的命令解释程序称为SHELL.一个简单的命令就是以空格隔开的一串字符。命令的第一个字是命令动词,后接一些参数,命令行以回车键结束,一行中可以打入多个命令,命令之间用分号分开。在使用LINUX系统命令时,一定要注意区分大小写字母,大小写表示不同含义,通常命令是以小写方式给出。
LINUX系统允许在命令中使用下列特殊字符: “*” 可匹配任意字符串 “?” 可匹配任意一个字符 “[] ”可匹配方括号中字符集的任何一个单个字符。如:[1-9]与[0123......9]一样,[A-Z]表示大写字母A-Z。“\\”是转义字符,放在特殊字符“* ? [] &”前,可使特殊字符成为普通字符,放在行尾可做续行符。
(1)列文件目录命令:ls
2
格式:ls [参数列表] 功能:列文件目录。
说明:参数可以有以下内容 -a 以ASCII码顺序排列显示全部内容,包括隐含文件“·”和“··”
-x 横向显示文件及目录名 用空格分隔 -m 横向显示文件及目录名 用逗号分隔 -l 每行显示一个文件目录的长列表信息
-t 按时间顺序显示,根据每个文件修改的日期。 例:列出当前目录下所有以\打头的文件。
$ ls a*
例:用长列表方式以ASCII码顺序排列显示全部内容,包括隐含文件。
$ls –al
(2)显示当前工作目录:pwd
格式:pwd
功能:显示当前工作目录。 例:$ pwd
执行结果: /usr/user1 (3)创建子目录:mkdir
格式: mkdir 目录名
例:创建子目录d1 $ mkdir d1
说明:每个目录建立后,自动建立两个包括隐含文件“·”和“··”,前者表示当前子目录,后者表示当前子目录的上一级目录。 (4)改变当前工作目录:cd
格式:cd [目录名]
例:假如已经创建目录d1, 进入子目录d1: $ cd d1
不带任何参数的cd命令表示返回到用户主目录。 (5)删除子目录:rmdir
格式:rmdir <目录名> 例:删除子目录d1 $ rmdir d1
(6)文本文件显示、建立与连接cat
格式:
cat [[文件名1 文件名2 [ 文件名3…… ]]> ]文件名 功能:① 显示已经存在的文件内容
3
② 从键盘建立新文件
③ 将两个或两个以上文件连接成一个文件
例:显示文本文件a1的内容
$ cat a1
例:将文本文件a1和 a2连接在一起结果放入a3中 $ cat a1 a2 >a3
例:从键盘创建文本文件 $ cat >a4 ......... ......... ......... ctrl/d $
(7)复制文件:cp
格式:cp 文件1[,文件2...... ] 目标文件 例:将文件a1复制到另一个文件abf1中 $ cp a1 abf1
例:将文件a1 a2 a3 a6拷贝到目录d21下 $ cp a1,a2,a3,a6 d21 (8)移动命令:mv
格式:mv 文件1 [文件2 ......] 目标文件 例:将文件a1更名为aa1 $ mv a1 aa1
例:将当前目录下以a打头的所有文件移到/usr/user15/d1下 $ mv a* /usr/user15/d1
例: 将子目录d2更名为d3 $ mv d2 d3 (9)删除文件:rm
格式: rm [参数列表] 文件1 [文件2 ......] 参数列表:
-r 对目录和子目录进行递归删除 -i 当删除无写权文件时交互式删除 -f 无条件删除指定的所有文件 例:删除文件a3 $ rm a3
例:交互式删除当前目录下的所有文件 $ rm -i * 例:递归删除目录d1及其子目录 $ rm -r d1 (10)改变属性:chmod
格式:chmod 方式 文件名或目录名
4
例:构造除本人外任何人都不能读写的文件a1
$ chmod go-rw a1
例:构造文件所有者、同组用户及其它用户都可以读写的文件a2
$ chmod a=rw a2
例:增加同组用户对文件abf1的执行许可
$ chmod g+x abf1
例:用绝对修改法使所有用户对文件a6有读写权
$ chmod 666 a6
例:用绝对修改法使文件主对文件a7有读写执行权,同组
用户有读写权,其它用户只有读权。
$ chmod 764 a7
3. Linux下进程管理命令 (1)后台进程:&
格式:<命令>&
例:以长列表方式列出/dev/bin下的文件目录,并将结果送入文件devo,以后台方式执行 $ ls -l /dev/bin>devo& (2)列出进程状态:ps 格式:ps [选择项] 其中选择项为:
-e 所有用户的每个进程的信息 -f 全部列表 -l 长的列表显示
-p<进程标识> 列出指定进程的状态 -u<用户名> 列出指定用户的进程状态
例:列出所有用户的每个进程的全部列表信息 $ps -ef
(3)中断后台进程:kill 格式: kill [-9] pid
例:中断548号后台进程 $ kill 548 (4)nohup 进程
格式:nohup <命令>&
例:作业job1是一个大作业,在用户退出系统后不希望被
5
杀死。
$ nohup job1&
4. Linux下的用户通信命令 (1)发送/接受信件 mail
格式:mail 用户名 [<文件名] 例:给用户user4发送信件 $mail user4
subject:.......... ......... ......... ......... ctrl/d
例:将文件test的内容发送给用户user8 $ mail user8 格式:write 用户名 [设备名] 例:将终端设备上的内容直接复制到另一用户的终端上 $ write user6 输入信息,例如: How are you doing about your project? (3)禁止或允许来自其它终端的信息:mesg 格式:mesg n|y 例:禁止来自其它终端的信息 $ mesg n 5.Linux下的联机帮助 LINUX系统中的联机帮助是通过在$提示符下打入:man命 令来实现的,命令格式:man <命令名> 6. 其他命令 (1) 输入输出转向 输入转向:SHELL 从普通文件中获得输入信息 格式:命令 <文件名 输出转向: 格式1 :命令<文件名 将一个命令的执行结果存放于一个文件中。 6 格式2 :命令<文件名 将一个命令的执行结果追加到文件的尾部。 例:将文件a1的内容发送给用户user3 $ mail user3 例:以长列表方式列当前目录下的文件目录, 将显示结果送入文件dir下保存 $ ls -l >dir 例:显示当前登录的用户情况,并将显示结果 追加到文件dir的尾部 $ who>dir (2) 管道命令 格式:命令1 |命令2 例:统计一下当前目录下有多少文件 $ ls |wc -l 实验习题 1 反复练习以自己的注册号进入LINUX系统,再从系统中退出。 2 通过联机帮助命令了解date、 who、 cal、 tty、 man 命令的语法。 3 反复练习ls、pwd、mkdir、cd、rmdir、cat、cp、rm、ps、chmod、date、who、cal、tty、man命令的使用,并注意每个命令中参数的使用。 4 创建三个子目录d1、d2、d3,并在d1下创建子目录d11和d12,在d2下创建子目录d21,然后在d12下创建子目录d121。 5 在子目录d2下创建文本文件a1、a2、a3、a4、 a5, 内容自定。然后将a1、a2和a3连接起来结果放入a6中;将a3、a4和a5连接起来放入a7中。完成上述工作之后,用cat命令显示一下a6和a7的内容。 实验报告 1 普通终端用户能否用date命令修改系统日期? 2 在您的终端上执行who 、who am I和 who -H命令的结果分别是什么? 3 写出显示1997年7月的日历的命令的执行结果。 4 写出cal的主要参数及含义。 5 写出ls、pwd、cp、ps、chmod命令不同参数的执行结果。 7 8 实验2 VI编辑软件 实验目的 1.熟悉vi操作的基本流程和基本修改命令。 2.熟悉vi中光标移动方法及文本添加方法。 3.熟悉vi中文本删除命令及查找与替换命令。 4.熟悉vi中文本的拷贝与移动命令以及涉及多个文本时的操作方法。 5.掌握linux下C语言程序的编辑、编译、连接、执行命令。 实验准备 1.学生使用自己的注册号LINUX进入操作系统。 2.每个学生创建一个文件夹存放用vi建立的文件。 实验内容 一、vi软件的使用 1.进入 格式:vi [参数] [文件名] 说明:不给定文件名,应该在退出编辑状态时给定文件名,一般用于建立新文件的情况。例: $ vi 例:修改一个已经存在的文件或编辑一个新文件 letter.c $ vi letter.c 例:从已经存在的文件a1的第5行开始编辑 $ vi +5 a1 例:从已经存在的文件a2的最后一行开始编辑 $ vi + a2 例:从已经存在的文件a7中指定串you开始编辑,光标停留在第一个满足条件的字符串处,若想向下搜索,则进入最后行方式,打入“/”继续向下搜索。 $ vi +/you a7 2.退出vi (1) 保存编辑内容并退出vi:按ESC键,打入冒号,进入最后行方式,打入:wq! (2) 将编辑内容存入新文件:按ESC键,打入冒号,进入最后行方式,打入:w 新文件名,写入完成后,打入q!退出vi。打入vi返 9 回到编辑状态。 (3)不存盘退出:按ESC键,打入冒号,进入最后行,打入:q! 3.光标定位命令 l 右移一列 h 左移一列 j 下移一行 k 上移一行 $ 移到行首 ^ 移到行尾 G 移到文件尾 1G 移到文件首 nG 移到第n行 w 移到下一字首 e 移到字尾 4.删除命令 x 删除光标所在处字符 nx 删除当前光标起n个字符 dw 删除字符,包括前面空格及其后部分 ndd 删除光标所在行开始向下 n行 D 删除光标至行尾 5.修改命令 r 用单个字符替换当前字符。 R 输入要替换的字符,按ESC键终止。 s 进入插入方式,用输入的新串替换光标所在处一个字符,按ESC键终止。 S 删除当前行且进入输入方式,按ESC终止 C 替换当前光标到行尾的内容,按ESC终止 cc 删当前行且进入输入方式,按ESC终止 6.文本输入命令 i 在光标前插入 I 在行首插入 a 在光标后附加 A 在行尾附加 o 在当前行之下加一新行,进入插入方式 O 在当前行之上加一新行,进入插入方式 ※ 在文本输入方式下,所有命令都是按ESC键终止输入方式而进入命令方式。 7.屏幕显示处理命令 ctrl/l 压缩屏幕 ctrl/f 下翻一屏 ctrl/b 上翻一屏 ctrl/d 下翻半屏 ctrl/u 上翻半屏 8.最后行方式命令:按ESC键后按冒号进入最后行方式。 (1)写行1~行2之间的缓冲区内容到文件名指定的磁盘文件中。 :[行1,行2] w文件名 10 (2)读文件名指定的文件内容到n指定的行之下 :nr 文件名 (3)光标移动到第n行 :n (4)确定当前行数 := (5)执行shell命令按ctrl/d返回vi :sh (6) 从前向后搜索指定字符串,光标定位于第一次匹配 :/串 (7)从后向前搜索指定字符串,光标定位于第一次匹配 :?串 9.块的移动和复制 (1)移动 先将光标移到要移动的起始行,打入命令 ndd,再移动光标到目标行,打入命令 p,则完成一次移动,若当前状态不变,将光标移到另一行,打入“p”又完成一次移动。 (2)复制 先移动光标到要复制的行,打入命令 nyy ,再移动光标到某行的开始,打入命令 p,则要复制的n行内容插入到该行之下。 10.vi参数设置(在最后行方式下进行) (1)设置文本编辑行号 :set number 不想要行号时,键入命令:set nonumber即可 (2)搜索字符串时不分大小写 :set ignorecase (3)显示文本中的制表符(TAB键) :set list 以^I表示TAB键,用“$”标记每行行尾 :set nolist 以空格表示TAB键,每行行尾不带“$”标记 (4)设置编辑时的状态信息 :set showmode 除命令方式外,屏幕下方将根据编辑方式的不同显示提示信息。 :set noshowmode 则取消显示 11 二、C程序开发工具 1.编辑程序 linux/UNIX的编辑程序有Emacs、vi、ed等。我们选用vi,用前边所述方法编辑简单的C程序。注意:文件名的扩展名必须为“.c” 2.编译程序 编译、调试C程序时,一般在命令行下编译,命令格式: #cc 源文件名 [参数 目标文件名] 说明: (1) 源文件名必须以.c作为扩展名。 (2) 参数和目标文件名缺省时输出的可执行文件名为a.out。 例:$ cc f.c 该命令编译C源程序f.c并输出目标文件a.out。 (3) 参数一般为-o表示输出目标文件,并指定目标文件名(建议使用该格式) 例:$ cc f.c –o f 上述命令行编译C源程序f.c并输出的可执行目标程序f。 3.调试程序 linux下的常用调试程序是GDB。此外,ptrace系统调用(跟踪进程执行)、strace命令(跟踪系统调用与信号)ltrace命令(跟踪库函数调用,a library call tracer)等,也可以用来辅助调试。 4.make程序 如果所编制的程序由几十个模块组成,修改其中任一模块时都要将这几十个模块重新连接,则此时make可以大大提高工作效率,减少很多重复性劳动。 5.运行 运行编译后的目标程序,不能直接在操作系统提示符输入目标文件名下,需要指定目标文件的位置,如果在当前目录下就用“.”表示。 例:执行前边编译的目标文件f(假设f在当前目录下)输入命令: $ ./f 实验习题 1.利用vi 命令写一封信,然后对信的内容进行各种修改,试图增加一些内容,删除一些内容,移动一些内容,复制一些内容。 12 2.利用vi 命令编辑两个文本文件,然后试图将两个文件连接起来,再试图将文件的某一部分存入另一个文件中,之后用最后行方式的其他命令。 3.编辑一个长于一屏的文件,然后试图使用屏幕显示处理命令。 4.编辑一个文本文件,然后用vi 的各种退出方式退出vi操作。 5.编辑一个文本文件,使用vi的参数设置命令。 6.用vi编辑一个简单的C语言程序并完成编译、连接、运行过程。 注意事项 使用vi时,一定不能用上下左右箭头键移动光标,也不能使用PageUp和PageDown键来翻屏。 实验报告 1.写出编辑一个已经存在的文本文件有哪几种方式?写出编辑方式与命令方式之间进行转换一般用什么方法? 2.ndd是删除命令,想想为什么块移动命令也用ndd,而且在状态不变时可以通过打入“p”再次移动,尤其要强调状态不变,状态不变主要指什么? 3.用vi命令编辑的文件,除了重新进入编辑状态显示文件内容以外,还可以用什么方法显示文件内容? 4.写出在编辑状态要执行shell命令的过程。 13 实验3 银行家算法的实现 实验目的 1. 2. 3. 4. 了解银行家算法是避免死锁的一种重要方法。 加深理解有关资源申请、避免死锁等概念。 体会、理解死锁和避免死锁的具体实施方法。 模拟实现银行家算法,用银行家算法实现资源分配。 实验准备 1 以自己的用户名进入LINUX 操作系统。 2 会用vi编辑文本文件。 3 熟悉C语言编程实验内容。 4 在PC机上安装C语言集成开发环境。 实验内容: 设计五个进程{P0,P1,P2,P3,P4}共享三类资源{A,B,C}的系统,{A,B,C}的资源数量分别为10,5,7。进程可动态地申请资源和释放资源,系统按各进程的申请动态地分配资源。要求程序具有显示和打印各进程的某一时刻的资源分配表和安全序列;显示和打印各进程依次要求申请的资源号以及为某进程分配资源后的有关资源数据。 实验提示: 1.数据结构 假设有M个进程N类资源,则有如下数据结构: MAX[M*N] M个进程对N类资源的最大需求量 AVAILABLE[N] 系统可用资源数 ALLOCATION[M*N] M个进程已经得到N类资源的资源量 NEED[M*N] M个进程还需要N类资源的资源量 2.银行家算法 设进程I提出请求Request[I],则银行家算法按如下规则进行判断。 (1)如果Request[I]<=NEED[I],则转(2);否则,出错。 (2) 如果Request[I]<=AVAILABLE,则转(3);否则,出错。 14 (3)系统试探分配资源,修改相关数据: AVAILABLE=AVAILABLE-REQUEST[I] ALLOCATION=ALLOCATION+REQUEST[I] NEED[I]=NEED[I]-REQUEST[I] (4)系统执行安全性检查,如安全,则分配成立;否则试探险性分配作废,系统恢复原状,进程等待。 3.安全性检查 (1) 设置两个工作向量 WORK=AVAILABLE; 对所有进程I:设FINISH[I]=FALSE; (2) 从进程集合中找到一个满足下述条件的进程, FINISH[I]=FALSE NEED[I]<=WORK 如找到,执行(3);否则,执行(4) (3)设进程获得资源,可顺利执行,直至完成,从而释放资源。 WORK=WORK+ALLOCATION[I] FINISH[I]=TRUE GO TO (2) (4)如所有的进程Finish[I]=true,则表示安全;否则系统不安全。 实验报告 1 2 3 画出银行家算法的程序流程图。 给出实现银行家算法和安全性算法的关键代码。 给出实验输入数据及运行结果。 15 实验4 作业调度算法 实验目的 1 加深对作业概念的理解。 2 理解操作系统中调度的概念和调度算法。 3 深入理解操作系统中如何组织、管理和调度作业,如何协调和控制各作业对CPU的使用。 实验准备 1 以自己的用户名进入LINUX 操作系统。 2 会用vi编辑文本文件。 3 在PC机上安装C语言集成开发环境。 4 熟悉C语言编程。 5 理解作业调度算法的基本思想。 实验内容 编写程序完成批处理系统中的作业调度,要求分别采用先来先服务算法(FCFS)、短作业优先算法(SJF)和高响应比优先算法(HRN)。实验具体包括:首先确定作业控制块的内容,作业控制块的组织方式;然后完成作业调度;最后编写主函数对所做工作进行测试,并对程序的运行结果进行分析。 实验提示: 操作系统根据允许并行工作的道数和一定的算法从系统中选取若干作业把它们装入住存储器,使它们有机会获得处理器运行,这项工作被称为“作业调度”。实现这部分功能的程序就是“作业调度算法”。 作业调度的实现主要有两个问题,一个是如何将系统中的作业组织起来;另一个是如何进行作业调度。 为了将系统中的作业组织起来,需要为每个进入系统的作业建立档案以记录和作业相关的信息,例如作业名、作业所需资源、作业所需运行时间、作业提交时间、指向下一个作业控制块的指针等信息。这个记录作业相关信息的数据块称为作业控制块(JCB),并将系统中等待作业调度的作业控制块组织成一个队列,这个队列称为后备队列。一个作业全部信息进入系统后,就为其建立作业控 16 制块,并挂入后备队列。当进行作业调度时,从后备队列中查找选择作业。 由于实验中没有实际作业,作业控制块中的信息内容只使用了实验中需要的数据,其数据结构可以做如下定义: struct jcb{ char name[10]; /* 作业名 */ char state; /* 作业状态 */ int ts; /* 提交时间 */ float super; /* 优先权 */ int tb; /* 开始运行时间 */ int tc; /* 完成时间 */ float ti; /* 周转时间 */ float wi; /* 带权周转时间 */ int ntime; /*作业所需运行时间*/ char resource[10]; /* 所需资源 */ struct jcb *link; /* 结构体指针 */ } *p,*q,*head=NULL; typedef struct jcb JCB; 确定作业组织方式之后,就要开始考虑如何进行作业调度。尽管不同的计算机系统可以采用不同的调度原则和调度算法,但是都必须遵循一个必要条件,即系统现有的尚未分配的资源可以满足被选作业的资源要求。就是说,所有的作业调度都是按照一定的算法,从满足必要条件的作业种选择一部分作业装入主存储器。 常用的作业调度算法有先来先服务算法(FCFS)、短作业优先算法(SJF)和高响应比优先算法(HRN)。在高响应比优先算法中,响应比的定义为: 响应比=作业的等待时间/作业估计运行时间 采用高响应比优先算法,进行调度时必须计算出系统中所有满足必要条件作业的响应比;从中选择响应比最高的一个作业装入主存储器分配资源。由于是实验,所以就用将作业的作业控制块出队,并输出作业的作业名代替装入主存储器,同时修改系统的资源数量;用同样方法选择第二个、第三个??直到不再有满足必要条件的作业。 模拟实验中,首先,手工输入或从文件读入某个时刻系统中的各个作业情况;然后,进行作业调度,并将结果输出。 注意事项 本实验只是模拟作业调度算法,用输出的作业序列表示调度 17 结果,作业的到达时间需要输入。 实验报告 1. 画出采用先来先服务算法(FCFS)、短作业优先算法(SJF)和高响应比优先算法(HRN)的作业调度程序流程图。 2. 给出实现三种算法的关键代码。 3. 给出实验结果。 4.对实验运行结果进行简单的分析。 18 实验5 动态分区 实验目的 1 加深对动态分区基本思想的理解,深入理解操作系统中如何用分区的方法管理内存。 2 理解操作系统中动态分区的分配、回收算法。 3 根据动态分区分配、回收算法的描述编程实现该算法。 实验准备 1 理解存储管理中动态分区管理方法。 2 在PC机上安装C语言集成开发环境。 3 熟悉C语言编程。 实验内容 编写程序完成动态分区存储管理方式的主存分配回收的实现。实验具体包括:确定主存空间分配表;采用最佳适应算法完成主存空间的分配和回收,编写主函数对所做的工作进行测试。 实验提示: 动态分区管理方式预先不将主存划分成几个区域,而把主存除操作系统占用区域外的空间看作一个大的空闲区。当作业要求装入主存时,根据作业所需主存空间的大小查询主存内各个空闲区,当从主存空间中找到一个大于或等于该作业大小的主存空闲区时,选择其中一个空闲区,按作业需求量划出一个分区装入该作业。作业执行完后,它所占的主存分区被收回,成为一个空闲区。如果该空闲区的相邻分区也是空闲区,则需要将相邻空闲区合并成一个空闲区。 实现动态分区的分配和回收,主要考虑的问题有三个:第一,设计记录主存使用情况的数据表格,用来记录空闲区和作业占用的区域;第二,在设计的数据表格基础上设计主存分配算法;第三,在设计的数据表格基础上设计主存回收算法。 首先,考虑第一个问题:设计记录主存使用情况的数据表格,用来记录空闲区和作业占用的区域。 由于动态分区的大小是由作业需求量决定的,故分区的长度是 19 预先不固定的,且分区的个数也随主存分配和回收变动。总之,所有分区情况随时可能发生变化,数据表格的设计必须和这个特点相适应。由于分区长度不同,因此设计的表格应该包括分区在主存中的起始地址和长度。由于分配时空闲区有时会变成两个分区:空闲区和已分分区,回收主存分区时,可能会合并空闲分区,这样如果整个主存采用一张表格记录己分分区和空闲区,就会使表格操作繁琐。主存分配时查找空闲区进行分配,然后填写已分配区表,主要操作在空闲区;某个作业执行完后,将该分区变成空闲区,并将其与相邻的空闲区合并,主要操作也在空闲区。由此可见,主存的分配和回收主要是对空闲区的操作。这样为了便于对主存空间的分配和回收,就建立两张分区表记录主存使用情况,一张表格记录作业占用分区的“已分配区表”;一张是记录空闲区的“空闲区表”。这两张表的实现方法一般有两种,一种是链表形式,一种是顺序表形式。在实验中,采用顺序表形式,用数组模拟。由于顺序表的长度必须提前固定,所以无论是“已分配区表”还是“空闲区表”都必须事先确定长度。它们的长度必须是系统可能的最大项数,系统运行过程中才不会出错,因而在多数情况下,无论是“已分配区表” 还是“空闲区表”都有空闲栏目。己分配区表中除了分区起始地址、长度外,也至少还要有一项“标志”,如果是空闲栏目,内容为“空”,如果为某个作业占用分区的登记项,内容为该作业的作业名;空闲区表中除了分区起始地址、长度外,也要有一项“标志”,如果是空闲 栏目,内容为“空”,如果为某个空闲区的登记项,内容为“末分配”。在实际系统中,这两表格的内容可能还要多,实验中仅仅使用上述必须的数据。为此,“已分配区表”和“空闲区表”在实验中有如下的结构定义。 已分配区表的定义: #define N 10 //假定系统允许的最大作业数量为N Typedof struct {long address;//已分分区起始地址 long length; //已分分区长度,单位为字节 int flag;//已分配区表登记栏标志,“0”表示空栏目, 实验中只支持一个字符的作业名 }used_table[N];//已分配区表 空闲区表的定义: #define M 10 //假定系统允许的空闲区表最大为M Typedof Struct {long address; //空闲区起始地址 20 long length; //空闲区长度,单位为字节 int f1ag;//空闲区表登记栏标志,用“0” 表示空栏目, 用“1”表示未分配。 }free_table[M];//空闲区表 其中分区起始地址和长度数值大大超出了整型表达范围,所以采用了long类型。然后,就要考虑如何在设计的数据表格上进行主存的分配。 当要装入一个作业时,从空闲区表中查找标志为“未分配”的空闲区,从中找出一个能容纳该作业的空闲区。如果找到的空闲区正好等于该作业的长度,则把该分区全部分配给作业。这时应该把该空闲区登记栏中的标志改为“空”,同时在己分配区表中找到一个标志为“空”的栏目登记新装入作业所占用分区的起始地址、长度和作业名。如果找到的空闲区大于作业长度,则把空闲区分成两部分,一部分用来装入作业,另外一部分仍为空闲区。这时只要修改原空闲区的长度,且把新装入的作登记到己分配区表中。 实验中主存分配算法采用“最佳适应”算法。最佳适应算法是按作业要求挑选一个能满足作业要求的最小空闲区,这样保证可以不去分割一个大的区域,使装入大作业时比较容易得到满足。但是最佳适应算法容易出现找到的一个分区可能只比作业所要求的长度略大一点的情况,这时,空闲区分割后剩下的空闲区就很小,这种很小的空闲区往往无法使用,影响了主存的使用。为了一定程度上解决这个问题,如果空闲区的大小比作业要求的长度略大一点,不再将空闲区分成己分分区和空闲区两部分,而是将整个空闲区分配给作业。在实现最佳适应算法时,可把空闲区按长度以递增方式登记在空闲区表中。分配时顺序查找空闲表,查找到的第一个空闲区就是满足作业要求的最小分区。这样查找速度快,但是为使空闲区按长度以递增顺序登记在空闲表中,就必须在分配回收时进行空闲区表的调整。空闲区表调整时移动表目的代价要高于查询整张表的代价,所以实验中不采用空闲区有序登记在空闲表中的方法。 动态分区方式的主存分配流程如图5-1所示。最后是动态分区方式下的主存回收问题。 动态分区方式下回收主存空间时,应该检查是否有与归还区相邻的空闲区。若有,则应该合并成一个空闲区。一个归还区可能有上邻空闲区,也可能有下邻空闲区,或者既有上邻空闲区又有下邻空闲区,或者既无上邻空闲区也无下邻空闲区。在实现回收时,首先将作业归还的区域在已分配表中找到,将该栏目的状态变为“空”,然后检查空闲区表中标志为“未分配”的栏目,查找是否 21 有相邻空闲区;最后,合并空闲区,修改空闲区表。假定作业归还的分区起始地址为S,长度为L,则: (1)归还区有下邻空闲区:如果S+L正好等于空闲区表中某个登记栏目(假定为第j栏)的起始地址,则表明归还区有一个下邻空 图5-1 动态分区最佳适应法流程图 闲区。这时只要修改第j栏登记项的内容: 起始地址-S; 第 j栏长度=第 j栏长度+L; 则第j栏指示的空闲区是归还区和下邻空闲区合并后的大空闲区。 22 (2)归还区有上邻空闲区 如果空闲区表中某个登记栏目(假定为第k栏)的“起始地址+长度”正好等于S,则表明归还区有一个上邻空闲区。这时要修改第k栏登记项的内容(起始地址不变): 第k栏长度=第k栏长度+L; 则第k栏指示的空闲区是归还区和上邻空闲区合并后的大空闲区。 (3)归还区既有上邻空闲区又有下邻空闲区 如果S+L正好等于空闲区表中某个登记栏目(假定为第j栏)的起始地址,同时还有某个登记栏目(假定为第k栏)的“起始地址+长度”正好等于S,这表明归还区既有一个上邻空闲区又有一个下邻空闲区。此时对空闲区表的修改如下: 第k栏长度=第k栏长度+第j栏长度+L;(第k栏起始地址不变) 第j栏状态=“空”;(将第j栏登记项删除) 这样,第k栏指示的空闲区是归还区和上、下邻空闲区合并后的大空闲区;原来的下邻空闲区登记项(第3栏)被删除,置为“空”。 (4)归还区既无上邻空闲区又无下邻空闲区 如果在检查空闲区表时,无上述三种情况出现,则表明归还区既无上邻空闲区又无下邻空闲区。这时,应该在空闲区表中查找一个状态为“空”的栏目(假定查到的是第t栏),则第t栏的内容修改如下: 第t栏起始地址=S; 第t栏长度=L; 第t栏状态=“未分配” 这样,第t栏指示的空闲区是归还区。 按上述方法归还主存区域的流程如图5-2所示。 由于是实验,没有真正的主存要分配,所以在实验中,首先应建立一张空闲区表,初始状态只有一个空闲登记项(假定的主存空闲区)和一张所有状态都为“空”的已分配区表,假定主存空间11 OKB,操作系统占用10KB,其余为空闲区;然后,可以选择进行主存分配或主存回收,如果是分配,要求输入作业名和所需主存空间大小,如果是回收,输入回收作业的作业名,循环进行主存分配和回收后,如果需要,则显示两张表的内容,以检查主存的分配和回收是否正确。 23 图5-2 动态分区回收流程图 实验习题: 24 (1) 编程实现页式存储管理的主存分配和回收。 (2) 用链表方式表示主存空间分配情况,完成动态分区管理方式下的主存空间分配和回收。 实验报告: 写出实验习题的主要代码并对实验运行结果进行分析。 25 实验6 页式地址变换与页面置换 实验目的 1 深入了解页式存储管理如何实现地址转换。 2 进一步认识页式虚拟存储管理中如何处理缺页中断。 3 根据页面置换算法的描述编程实现该算法。 实验准备 1 页式存储管理中地址转换的方法。 2 页式虚拟存储的缺页中断处理方法。 3 在PC机上安装C语言集成开发环境。 4 熟悉C语言编程。 实验内容 编写程序完成页式虚拟存储管理中地址转换过程和模拟缺页中断的处理。实验具体包括:首先对给定的地址进行地址转换工作,若发生缺页则先进行缺页中断处理,然后再进行地址转换;最后编写主函数对所做工作进行测试。 假定主存64KB,每个主存块1024字节,作业最大支持到 64KB,系统中每个作业分得主存块4块。 实验提示 页式存储管理中地址转换过程很简单,假定主存块的大小为2n字节,主存大小为2m字节和逻辑地址m位,则进行地址转换时,首先从逻辑地址中的高m-n位中取得页号,然后根据页号查页表,得到块号,并将块号放入物理地址的高m-n位,最后从逻辑地址中取得低n位放入物理地址的低n位就得到了物理地址,过程如图6-1所示。 图6-1 页式存储管理系统地址转换示意图 26 图6-2 模拟地址转换流程图 地址转换是由硬件完成的,实验中使用软件程序模拟地址转换过程,模拟地址转换的流程如图6-2所示(实验中假定主存64 KB,每个主存块1024宇节,即n=10,m=16,物理地址中块号6位、块内地址10位;作业最大64KB,即m=16,逻辑地址中页号6位、页内地址10位)。 在页式虚拟存储管理方式中,作业信息作为副本放在磁盘上,作业执行时仅把作业信息的部分页面装入主存储器,作业执行时若访问的页面在主存中,则按上述方式进行地址转换,若访问的页面不在主存中,则产生一个“缺页中断”,由操作系统把当前所需的页面装入主存储器后,再次执行时才可以按上述方法进行地址转换。页式虚拟存储管理方式中页表除页号和该页对应的主存块号外,至少还要包括存在标志(该页是否在主存),磁盘位置(该页的副本在磁盘上的位置)和修改标志(该页是否修改过)。 页表用数组模拟,在实验中页表数据结构定义为: #define N 32 Typedof struct {int lnumber; //页号 int flag; //表示该页是否在主存,“1’表示在主存, “0”表示不在主存 int pnumber; //该页所在主存块的块号 int write; //该页是否被修改过,“1’表示修改过,“0” 表示没有修改过 int dnumber; //该页存放在磁盘上的位置,即磁盘块号 }Page [N]; //页表定义 27 缺页处理过程简单阐述如下: (1) 根据当前执行指令中逻辑地址的页号查页表,判断该页是否在主存储器中,若该页标志为 “0”,形成缺页中断。中断装置通过交换PSW让操作系统的中断处理程序占用处理器; (2)操作系统处理缺页中断的方法就是查主存分配表,找一个空闲主存块;若无空闲块,查页表,选择一个已在主存的页面,把它暂时调出主存。若在执行过程中该页被修改过,则需将该页信息写回磁盘,否则不必写回; (3)找出该页的磁盘位置,启动磁盘读出该页信息,把磁盘上读出的信息装入第2步找到的主存块,修改页表中该页的标志为“1’; (4)由于产生缺页中断的那条指令没有执行完,所以页面装入后应重新执行被中断的指令。当重新执行该指令时,由于要访问的页面己在主存中,所以可正常执行。 关于第2步的查找装入新页面的主存块处理方式,不同系统采用的策略可能有所不同,这里采用局部置换算法,就是每个作业分得一定的主存块,只能在分得的主存块内查找空闲块,若无空闲主存块,则从该作业中选择一个页面淘汰出主存。实验中使用局部置换算法。 使用局部置换算法时,存在这样一个问题:就是在分配给作业主存空间时,装入哪些页?有的系统采取不装入任何一页,当执行过程中需要时才将其调入。有的系统采用页面预置的方法,就是估计可能某些页面会先用到,在分配主存块后将这些页面装入。实验中,采用第二种方法,分配主存空间时将前几页调入主存,假定系统中每个作业分得主存块m(m=4)块,则将第0~m-1页装入主存。 因为是模拟硬件工作,所以实验中如果访问的页不在主存时,则输出该页页号,表示硬件产生缺页中断,然后直接转去缺页中断处理;由于采用页面预置方法,在给定的主存块中一定无空闲块,只能淘汰已在主存的一页;没有启动磁盘的工作,淘汰的页需要写回磁盘时;用输出页号表示,调入新的一页时,将该页在页表中的存在标志置为“ 1”,输出页号表示将该页调入主存。 主存中无空闲块时,为装入一个页面,必须按某种算法从已在主存的页中选择一页,将它暂时调出主存,让出主存空间,用来存放需装入的页面,这个工作称为“页面调度”。如何选择调出的页是很重要的,常用的页面调度算法有先进先出算法、最近最少使用 28 算法和最近最不常用算法。实验中使用先进先出调度算法。先进先出调度算法总是选择驻留在主存时间最长的一页调出。先进先出算法简单,易实现。可以把在主存储器的页的页号按进入主存的先后次序排成队列,每次总是调出队首的页,当装入一个新页后,把新页的页号排入队尾。实验中,用一个数组存放页号的队列。假定分配给作业的主存块数为m,数组可由m个元素组成,p[0],p[1],??p[m-1];队首指针 head ;采用页面预置的方法,页号队列的长度总是 m,tail等于(head+1)%m。因此可以使用一个指针,只用 head即可。在装入一个新的页时,装入页和淘汰页同时执行,将其页号存入数组; 淘汰页的页号=p[head]; p[head]=新装入页的页号; head=(head十1)%m; 实验中,采用先进先出页面置换算法的缺页中断流程如图6-3所示。 图6-3 采用先进先出页面置换算法的缺页中断流程图 实验执行一条指令时,不模拟指令的执行,只是考虑指令执行是否修改页面,若修改页面,则将该页的页表中修改标志位置“1”,然后输出转换后的物理地址,并输出物理地址来表示一条 29 指令执行完成;如果访问的页不在主存时,则产生缺页中断,然后直接转去缺页中断处理,最后模拟中断返回,就是返回重新进行地址转换。一条指令执行的模拟流程如图6-4所示。 因为没有实际主存,所以在模拟程序中首先手工输入页表信息,创建该作业的页表:然后循环执行假定的指令,观察地址转换情况。 图6-4 一条指令执行的模拟流程图 实验习题 采用LRU页面调度算法编程实现上述虚拟页式存储管理的地址转换。 实验报告 写出实验内容和实验习题的主要程序清单并对执行结果进行分析。 30 实验6 并发程序设计 实验目的 1 掌握一个进程创建另一个进程的系统调用。 2 掌握父进程等待子进程的系统调用。 3 掌握执行文件的系统调用。 4 掌握进程终止的系统调用。 实验准备 1 用自己的用户号进入LINUX操作系统。 2 会用VI编辑文本文件。 3 熟悉C语言编程及进程管理的几个常用的系统调用。 实验内容 1创建一个进程的系统调用fork() fork()系统调用的功能是父进程创建一个子进程,当调用成功时,fork()有两次返回,一次是子进程返回,返回的是0,表示创建成功,一次是父进程返回,返回的是子进程的进程标识符。子进程继承父进程的一切资源与现场,包括程序代码、程序计数器等所有寄存器的值,除了进程号和进程状态不同。因此,在fork()后父子进程执行同一程序(当然在程序中所走的执行路径通常不同,且子进程可能会在其后通过系统调用execve()或exec()更换所执行的程序),而且由于子进程继承了程序计数器值,因此子进程不是从该程序起始处开始执行的,它在该程序中的开始执行点就是该程序中位于fork()语句之后的下一条语句(这个位置正是父进程在fork()返回时的指令计数寄存器值),这条语句的作用正是取fork()的返回值。 综上所述,由于子进程与父进程执行同一程序,而子进程开始执行点是在fork()返回点上,因此,子进程执行了对fork()的返回,而没有执行对fork()的调用。父进程执行fork()调用时,子进程尚未存在;而fork()返回时,子进程已经存在,并恰恰从返回点开始执行。因此,导致了“一次调用、两次返回”。 因此,一般在用fork() 创建子进程时,可以根据fork() 的返回值判断是父进程的代码还是子进程的代码,当发fork()返回0时,是子进程代码,fork()返回-1表示创建子进程失败,否则, 31 就是返回了子进程的pid,为父进程代码。 2 执行一个文件的调用exec() 当父进程创建子进程之后,子进程继承了父进程的数据段和正文段,从而限制了子进程可以执行的程序规模。那么,子进程如何来执行那些不属于父进程而是在一个指定文件中的的正文段和数据段呢?这就需要利用系统调用exec()、execve()、execlp()或exece(),这些系统调用的功能项同,均是引出另一个程序,它用一个可执行文件的副本覆盖调用进程的正文段和数据段,并以调用进程提供的参数去执行这个新的正文段程序。这组系统调用有六种不同的格式但完成的功能是一样的。常用的有execve()、execvp()和execlp(),我们这里只讲述execlp()。其格式如下: execlp(pathname,filename,arg0,arg1,......,(char *)0); 例:execlp(\ 其中pathname是要执行的文件的路径名, filename是要执行文件的文件名, argn是输入参数序列的指针, 0则是参数序列的结束标志。 例1 如果父进程要通过建立子进程在同一显示器上同时分别循环显示“大连水产学院”和“信息工程学院”5000次(具体次数根据机器的速度确定,如果机器速度快,次数可以增加,如果机器速度慢,次数可以减少,次数的设置主要是为了看到进程的切换),程序有几种编法? 第一种方法:一程序两进程。 # include < unistd. h > # include < stdio.h > main( ) { int pid, n,m ; n= l; m=1 if ((pid=fork())! =0) while (n<=5000 ) {printf(“%d”,n++); 32 printf(“大连水产学院”);} /*父进程*/ else while (m<=5000 ) {printf(“%d”,m++); printf(“信息工程学院”);} /*子进程*/ } 在这种方法中,子进程代码直接包含在父进程代码中,故只用fork()系统调用。这种方法适用于预先知道子进程代码且子代码不长的情况。 从屏幕提示中观察父子进程切换情况。若觉屏幕滚动太快,可用 Ctrl+S暂停(Ctrl+Q恢复),或用管道“f|more”逐页查看。若觉得进程切换太慢(相对于屏幕显示),可在每个printf前加入nanosleep(),使之每秒钟显示一次,这样就可以在一屏中清楚地看到多次进程切换情况同时屏幕滚动速度不是很快。 第二种方法:二程序二进程(为免混淆,将SHELL命令提示符改为$)。 $vi exam1.c # include < unistd. h > # include < stdio.h > main( ) { int pid,n;n= 1; if ((pid=fork())! =0) while (n<=5000 ) {printf(“%d”,n++); printf(“大连水产学院”);} /*父进程*/ else execve(“./son”,0,0); /*子进程*/ } $cat son. c # include < unistd.h > # include < stdio. h > main( ) {int n;n = 1; 33 while (m<=5000 ) {printf(“%d”,n++); printf(“信息工程学院”);} } $cc exam.c –o exam $cc son.c -o son $ ./exam 在这种方法中,子进程代码在一个不同的程序son中,故需用execve()。由于大多数情况下,父进程不知道子进程要执行什么程序,且子进程代码通常不短,所以这种由fork()和execve()共同配合使用的方法最普遍。 第三种及更多方法:三程序三进程、一程序三进程、两程序三进程、三程序两进程,这四种方法请读者自编程序。 注意,若用普通子程序调用方法而一个子进程都不建立(在此例中指一程序一进程、两程序一进程、三程序一进程),则不能起到并发的效果。 3 父进程等待子进程的系统调用wait() wait()系统调用的功能主要是为了保证父进程与子进程之间的同步,该系统调用一般在父进程中使用,保证父进程等待子进程。 4 进程终止的系统调用exit() 该系统调用自我终止当前进程,使其进入僵死状态,等待父进程进行善后处理。 5 取当前进程的进程标识符getpid() 可以使用getpid()来获得当前进程的进程标识符。 例2: main() {int sum=0,i,n; if (fork()==0) {int mul=1,i,j; for(i=1;i<=3;i++) mul*=i; printf(\ j=getpid(); printf(\ 34 exit(0);} else {wait(&n); for (i=1;i<=5;i++) sum+=i; printf(\ } } 实验习题 1 用C语言编程实现如下任务: (1) 一个父进程创建一个子进程,子进程又创建一个子子进程(孙进程),分别作出等待的情况和不等待的情况。 (2) 一个父进程创建两个子进程,分别作出等待的情况和不等待的情况。 (3)在linux/UNIX下编制一个程序,该程序通过建立子进程的方法,并发地分别循环显示“高山流水”和“蓝天白云”(并在每个“高山流水”和“蓝天白云”前显示遍数),观察父子进程哪个先开始运行,及每次轮到运行时显示的遍数,并据此大致推算出每个时间片的大小。试用ctrl-S暂停显示(Ctrl+Q恢复显示)或用 |more管道命令分页显示。用 Ctrl+C终止进程,或从另一终端用kill命令终止它。或者,重新以后台方式运行该程序,并将该程序重定向输出到文件中,在前台以kill命令终止它运行,并在输出文件中观察运行结果。 (4)在linux/UNIX下,分别用一程序两进程、两程序两进程、完成题(3)。分别观察并解释运行结果。 (5)修改题(3)程序,改为每秒种显示一次“高山流水”或“蓝天白云”,这样屏幕显示就少得多、慢得多,而进程切换就看得更清楚了。现在可以限定显示次数,循环显示10次就可以了。观察父子进程开始和结束的先后顺序,观察进程切换情况,解释运行结果。有几种方法可以做到每秒钟显示一次?分别实现之。 (6) 修改题(5)程序,父进程要等子进程结束时才结束。观察并解释运行结果。 (7) 修改题(5)程序,每次显示前加上本进程号的显示(“这是。。。号进程”)。观察并解释运行结果。 35 2 用C语言编程实现SHELL功能,出现DOS提示符>等待输入一个DOS命令(如:DIR,DATE),建立一个进程去执行这个命令 注意事项 注意区别一个父进程创建两个子进程和一个父进程创建一个子进程,再由子进程创建孙进程的区别 实验报告 写出实验习题的程序清单并分析实验运行结果。 36 实验8 进程通信 实验目的 1 掌握进程间的管道通信方法。 2 了解和熟悉Linux支持的进程间的消息通信的实现。 3 了解和熟悉Linux支持的进程间共享缓冲区通信的实现。 实验准备 1 用自己的用户号进入LINUX操作系统。 2 会用VI编辑文本文件。 3 熟悉C语言编程及进程通信的方法及相关的系统调用。 4 阅读Linux系统的msg.c、sem.c和shm.c等源码文件,熟悉Linux的三种通信机制。 实验内容 (1)管道通信 编制一段程序,实现进程的管道通信。 使用系统调用pipe()建立一条管道线;两个子进程P1和P2分别向管道各写一句话: Child 1 is sending a message! Child 2 is sending a message! 而父进程则从管道中读出来自于两个子进程的信息,显示在屏幕上。 要求父进程先接收子进程P1发来的消息,然后再接收子进程P2发来的消息。 (2)消息的创建,发送和接收。 ①使用系统调用msgget(),msgsnd(),msgrev()及msgctl()编制一长度为 1K的消息的发送和接收程序。 ②观察上面程序,说明控制消息队列系统调用 msgctl()在此起什么作用? (3)共享存储区的创建、附接和断接。 使用系统调用 shmget(),shmat(),sgmdt(),shmctl(),编制一个与上述功能相同的程序。 (4)比较上述(2),(3)两种消息通信机制中数据传输的时间。 37 实验提示 1 管道通信 无名管道为建立管道的进程及其子孙提供一条以字符流方式传送消息的通信管道。该管道在逻辑上被看作管道文件,在物理上则有文件系统在高速缓冲区构成而很少启动外设。发送进程利用文件系统的系统调用write(fd[1],buf,size)把buf中的长度为 size字符的消息送入管道入口 fd[1],接受进程则使用系统调用read(fd[0],buf,size)从管道出口fd[0]读出size字符的消息放入buf中。这里的管道按照先进先出方式传送消息,且只能单向传送消息。 完成管道传送的系统调用为pipe,其格式为: pipe (fd) int fd[2]; 这里,fd[1]为写入端,fd[0]为读出端。例: #includ int x,fd[2]; char buf[30],s[30]; pipe (fd); /*创建管道*/ while ((x=fork())==-1); /*创建失败时循环*/ if(x==0) { sprintf(buf,\ write(fd[1],buf,30); /*把buf中字符写入管道*/ exit(0); } else /*父进程返回*/ { wait(0); read(fd[0],s,30); /*父进程读管道中的字符*/ printf(\ } } 2 消息通信 消息通信提供四个系统调用,它们是msgget、msgsnd、msgrev、msgctl: msgqid=msgget(key,msgflg) 38 key-t key; int msgflg; msgctl (msgqid,cmd,buf) int msgqid,cmd; msgqid_ds #buf; msgsnd(msgqid,msgp,msgsz,msgflg) int msgqid; struct msgbuf *msgp; int msgsz,msgflg; msgcrv(msgqid,msgp,msgsz,msgtyp,msgflg) int msgqid; struct msgbuf *msgp; int msgsz; long msgtyp; int msgflg; 这些系统调用所需要的数据结构和表格都放在头文件 为了完成上面(2)所要求的实验内容,要按一下思路完成: (1)为了便于操作和观察结果,用一个程序作为“引子”,先后 fork()两个子进程,SERVER和CIJENT,进行通信。 (2)SERVER端建立一个Key为75的消息队列,等待其他进程发来的消息。当遇到类型为1的消息,则作为结束信号,取消该队列,并退出SERVER。SERVER每接收到一个消息后显示一句“(server)received。 (3)CLIENT端使用key为75的消息队列,先后发送类型从10到 1的消息,然后退出。最后的一个消息,即是SERVER端需要的结束信号。CLIENT每发送一条消息后显示一句“(client)sent”。 (4)父进程在SERVER和CLIENT均退出后结束。 3 共享存储区通信 共享缓冲区提供系统调用shmget(),sgmat(),smgdt (),shmctl(),功能的说明与上面类似,这里不再赘述。 为了完成上面(3)所要求的实验内容,要按一下思路完成: 39 (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均退出后结束。 实验习题 1 用C语言编程实现两个并发进程,一个父进程创建一个子进程,二者分别做10个数求和与求积,然后子进程将结果返回该父进程,父进程再求出两个结果相加的和。 2 自己编程实现消息的创建、发送和接收并分析程序的执行结果 3 自己编程实现共享存储区通信,实现共享存储区的创建、附接和断接,并分析程序的运行结果。 注意事项 考虑父进程是否应该等待子进程 实验报告 写出实验习题的程序清单和分析过程 40 附录 B LINUX系统下进程管理系统调用 表B-1 LINUX/UNIX的进程和CPU管理类命令 命令名 Ps Pstree Kill Killall Skill, snice Top Procinfo Idle 表B-2 LINUX/UNIX的进程和CPU管理类系统调用分类表 类别 进程建、撤 与状态改变 个数 列 举 Fork(), clone(), vfork(), execve(), exit(), nanosleep(), pause(), wait(), waitpid(), wait4(), wait3(), kill(), killpg() Getpriority, setpriority, nice, sched_setparam, sched_get_priority_max, sched_getparam, sched_get_priority_min, sched_setscheduler, sched_getscheduler, sched_π_get_interval, sched_yield Getpid, getppid, getpgid, setpgid, setpgrp, getpgrp, setsid Ptrace(), times Personality, vm86, prctl, acct, idle, vm86old 功 能 显示进程状态 显示进程树 给进程发信号(终止进程) 按名给进程发信号(按名终止进程) 报告进程状态 显示进程状态(显示top CPU 状态) 显示从/proc搜集的系统状态信息 使进程0进入idle状态 13 进程调度 优先级管理 11 进程号(组)的 设置与察看 进程跟踪调试和进程运行时间 7 2 其他与进程/CPU管6 理相关的系统调用 总计 39 41 表B-3 LINUX/UNIX的与进程创建、撤销和状态有关的系统调用 名/格式/参数 功能 创建一个子进程 参数/返回值解释 对父进程:返回子进程号,对子进程:成功返回0,不成功返回-1 其他说明 Linux下vfork()是Fork()的别名,二者完全相同,unix下二者有别。 Fork(),vfork() Int clone(int(*fn) (void *arg), void 创建一个*childstack, int 子进程 flags, void * arg ) Linux下有。 Int execve(const char *filename, char * const 执行程序 argv[], char *const envp[]) Exit() 终止当前进程 暂停执行指定的一段时间 等待进程终止 BSD风格的等待进程终止 向进程发送终止信号 向进程组发送终止信号 等待信号 功能:用指定的程序覆盖当前程序代码。 返回:是否成功 在库函数级对应execlp, execle, execl, execv, execvp. nanosleep() wait(), waitpid() wait4(), wait3() kill killpg() Pause 42 表B-4 LINUX/UNIX的与进程号、组有关的系统调用 名/格式 Getpid, getppid getpgid, setpgid, setpgrp, getpgrp setsid 表B-5 LINUX/UNIX的与进程调度有关的系统调用 名/格式 Getpriority, setpriority nice sched_get_priority_max sched_get_priority_min sched_getparam, sched_setparam sched_setscheduler, sched_getscheduler sched_π_get_interval 改变进程优先级 设置静态优先级范围 设置/查看调度参数 设置/查看调度算法和参数 查看指定进程的SCHED_RR值 功能 设置/查看程序调度优先级 功能 获得进程标识符 设置/查询进程组 设置一个会话并设置进程组号 表B-6 LINUX/UNIX的与进程跟踪、运行时间有关的系统调用 名/格式 Ptrace() times 表B-7 LINUX/UNIX的其他相关的系统调用 名/格式 prctl Personality acct idle vm86old,vm86 进程控制 设置进程执行域 开/关进程记帐 使进程0进入idle状态 进入虚拟8086方式 功能 进程跟踪 得到进程时间 功能 43 表B-8 LINUX/UNIX的线程相关的系统调用 名/格式 Pthread_create Pthread_cancel, Pthread_setcancelstate, Pthread_setcanceltype, Pthread_testcancel Pthread_join Pthread_exit Pthread_sigmask, Pthread_kill, sigwait 功能 建立一个新线程 撤销线程 等待另一个线程终止 退出当前线程 线程中的信号处理 44