1、操作系统结构分析实验指导书南京邮电大学计算机学院信息安全系2007.10目 录第 1 部分 Linux 简明操作手册 .11.1 登录服务器(在 Windows 远程登录 Linux 服务器) .11.1.1 登录 FTP 服务器 .11.1.2 使用 putty 程序登录远程服务器 .11.2 vi 简易手册 .11.3 gcc 简易手册 .21.3.1 gcc 简述 .21.3.2 gcc 的基本用法和选项 .31.3.3 如何用 gcc 进行代码调试 .41.3.4 gcc 的错误类型及对策 .7第 2 部分 实验部分 .92.1 实验一 进程的创建与管道通信实验 .92.1.1 实验目
2、的 .92.1.2 实验环境 .92.1.3 实验原理 .92.1.4 实验内容 .102.1.5 实验任务 .102.2 实验二 进程的同步和互斥实验 .132.2.1 实验目的 .132.2.2 实验环境 .132.2.3 实验原理 .132.2.4 实验内容 .152.2.5 实验任务 .152.3 实验三 内存管理算法实验 .192.3.1 实验目的 .192.3.2 实验环境 .192.3.3 实验原理 .192.3.4 实验内容 .202.3.5 实验任务 .202.4 实验四 Linux 文件系统模拟实验 .222.4.1 实验目的 .222.4.2 实验环境 .222.4.3
3、实验原理 .222.4.4 实验内容 .242.4.5 实验任务 .252.5 实验五 Windows 进程同步实验 .262.5.1 实验目的 .262.5.2 实验环境 .262.5.3 实验原理 .262.5.4 实验内容 .282.5.5 实验任务 .28第 3 部分 实验报告 .291第 1 部分 Linux 简明操作手册1.1 登录服务器(在 Windows 远程登录 Linux 服务器)1.1.1 登录 FTP 服务器登陆步骤:1、点击“开始”“运行” ,输入“cmd” 回车后进入 COMMAND 命令行状态;2、输入命令:ftp HostIP (HostIP:10.20.79.
4、10) ;3、输入学号为用户名,密码亦为学号,成功后出现“ftp”提示符;4、练习使用操作命令:1) ls :列出当前目录下所有的文件和目录2) put FilePathFileName:上传本地文件至服务器目录3) get FileName :下载指定的文件名至运行 ftp 命令的目录中(注意文件名 的大小写)4) ? command:列出当前可用的命令名称,或列出指定命令的使用方法5) 使用公用帐号 public(密码也是 public) 登录后可以下载到远程登录终端程序 putty.exe 和本实验指导书。1.1.2 使用 putty 程序登录远程服务器登陆步骤:1、在 windows
5、环境里直接双击 putty.exe 图标;2、输入远程服务器地址:10.20.79.10,并用学号进行登录,密码相同。登录成功后会出现类似提示符“学号localhost 学号$” ;用 Windows 自带的 telnet 程序也可完成登陆过程,但是该程序运行和使用不如 putty 方便和稳定,所以推荐使用 putty 程序,telnet 的使用过程与 putty 相似。1.2 vi 简易手册vi 是 Linux 中使用频率最高的文字编辑工具,一般我们就用他来编写源代码。此工具的使用习惯和方式与 Windows 有较大区别,希望多加练习。vi 分为两种状态,即命令状态和编辑状态,在命令状态下,
6、所键入的字符系统均作命令来处理,如:q 代表退出,而编辑状态则是用来输入文字资料的。当你进入 vi 时,会首先进入命令状态。要进入 vi,直接在系统提示符下键入 vi ,当你键入的文件名是已2有文件时,则系统自动打开此文件,否则将建立一个新文件。这时你将会看到屏幕左边会出现波浪线,这就代表该行是空的,没有任何文字,这时系统正在命令状态,按键盘上的 a 键或 i 键即可切换到编辑状态,下面就是一些功能键的说明,有些键在某些版本的 vi 中可能不被支持。=说明 功能键=向下翻一页 Page Down向上翻一页 Page Up删除光标所在位置字符 Delete删除光标所在位置前面的字符 Backsp
7、ace移动光标 =返回命令状态只需按“ESC” 键,存盘需在命令状态下完成,按:w 按后回车即完成了存盘工作,而退出 vi 返回到 Linux 的命令是“:q” ,这两个命令也可以组合使用,如“:wq”代表存盘退出。退出但不保存命令是“:q!” 。1.3 gcc 简易手册1.3.1 gcc 简述Linux 系统下的 gcc(GNU C Compiler)是 GNU 推出的功能强大、性能优越的多平台编译器,是 GNU 的代表作品之一。gcc 编译器能将 C、C+语言源程序、汇程式化序和目标程序编译、连接成可执行文件,如果没有给出可执行文件的名字,gcc 将生成一个名为 a.out 的文件。在 L
8、inux 系统中,可执行文件没有统一的后缀,系统从文件的属性来区分可执行文件和不可执行文件。而gcc 则通过后缀来区别输入文件的类别,下面我们来介绍 gcc 所遵循的部分约定规则。 .c 为后缀的文件, C 语言源代码文件.a 为后缀的文件,是由目标文件构成的档案库文件.C,.cc 或.cxx 为后缀的文件,是 C+源代码文件.h 为后缀的文件,是程序所包含的头文件.i 为后缀的文件,是已经预处理过的 C 源代码文件.ii 为后缀的文件,是已经预处理过的 C+源代码文件.m 为后缀的文件,是 Objective-C 源代码文件.o 为后缀的文件,是编译后的目标文件.s 为后缀的文件,是汇编语言
9、源代码文件.S 为后缀的文件,是经过预编译的汇编语言源代码文件虽然我们称 gcc 是 C 语言的编译器,但使用 gcc 由 C 语言源代码文件生成3可执行文件的过程不仅仅是编译的过程,而是要经历四个相互关联的步骤预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编(Assembly)和连接(Linking)。 命令 gcc 首先调用 cpp 进行预处理,在预处理过程中,对源代码文件中的文件包含(include) 、预编译语句(如宏定义 define 等)进行分析。接着调用 cc1 进行编译,这个阶段根据输入文件生成以.o 为后缀的目标文件。汇编过程是针对汇编语
10、言的步骤,调用 as 进行工作,一般来讲,.S 为后缀的汇编语言源代码文件和汇编、.s 为后缀的汇编语言文件经过预编译和汇编之后都生成以 .o 为后缀的目标文件。当所有的目标文件都生成之后,gcc 就调用 ld 来完成最后的关键性工作,这个阶段就是连接。在连接阶段,所有的目标文件被安排在可执行程序中的恰当的位置,同时,该程序所调用到的库函数也从各自所在的档案库中连到合适的地方。 1.3.2 gcc 的基本用法和选项 gcc 的基本语法是:gcc options filenames其中 options 就是编译器所需要的参数,filenames 给出相关的文件名称,下面就是几种常用的选项参数。
11、-c,只编译,不连接成为可执行文件,编译器只是由输入的.c 等源代码文件生成.o 为后缀的目标文件,通常用于编译不包含主程序的子程序文件。 -o output_filename, (小写 o)确定输出文件的名称为 output_filename,同时这个名称不能和源文件同名。如果不给出这个选项,gcc 就给出预设的可执行文件 a.out。 -g,产生符号调试工具(GNU 的 gdb)所必要的符号资讯,要想对源代码进行调试,我们就必须加入这个选项。 -O, (大写 O)对程序进行优化编译、连接,采用这个选项,整个源代码会在编译、连接过程中进行优化处理,这样产生的可执行文件的执行效率可以提高,但是
12、,编译、连接的速度就相应地要慢一些。 -O2,比 -O 更好的优化编译、连接,当然整个编译、连接过程会更慢。 -I dirname,将 dirname 所指出的目录加入到程序头文件目录列表中,是在预编译过程中使用的参数。C 程序中的头文件包含两种情况 A)#include B)#include “myinc.h” 其中,A 类使用尖括号(),B 类使用双引号(“ ”)。对于 A 类,预处理程序 cpp 在系统预设包含文件目录(如/usr/include) 中搜寻相应的文件,而对于 B类,cpp 在当前目录中搜寻头文件,这个选项的作用是告诉 cpp,如果在当前目录中没有找到需要的文件,就到指定的
13、 dirname 目录中去寻找。在程序设计中,如果我们需要的这种包含文件分别分布在不同的目录中,就需要逐个使用-I 选项给出搜索路径。 -L dirname,将 dirname 所指出的目录加入到程序函数档案库文件的目录列表中,是在连接过程中使用的参数。在预设状态下,连接程序 ld 在系统的预4设路径中(如/usr/lib)寻找所需要的档案库文件,这个选项告诉连接程序,首先到-L 指定的目录中去寻找,然后到系统预设路径中寻找,如果函数库存放在多个目录下,就需要依次使用这个选项,给出相应的存放目录。 -l name,在连接时,装载名字为“libname.a”的函数库,该函数库位于系统预设的目录或
14、者由-L 选项确定的目录下。例如,-lm 表示连接名为“libm.a”的数学函数库。 1.3.3 如何用 gdb 进行代码调试Linux 包含了一个叫 gdb 的 GNU 调试程序。gdb 是一个用来调试 C 和 C+程序的强力调试器。它使你能在程序运行时观察程序的内部结构和内存的使用情况. 以下是 gdb 所提供的一些功能:1、它使你能监视你程序中变量的值。 2、它使你能设置断点以使程序在指定的代码行上停止执行。 3、它使你能一行行的执行你的代码。在命令行上键入 gdb 并按回车键就可以运行 gdb 了,如果一切正常的话, gdb 将被启动并且你将在屏幕上看到类似的内容: GDB is fr
15、ee software Copyright 1995 Free Software Foundation, Inc. 当你启动 gdb 后,你能在命令行上指定很多的选项,你也可以以下面的方式来运行 gdb:gdb 当你用这种方式运行 gdb,你能直接指定想要调试的程序。这将告诉 gdb 装入名为 fname 的可执行文件。你可以参考 gdb 指南页或在命令行上键入 gdb -h得到一个有关这些选项的说明的简单列表。 为调试编译代码(Compiling Code for Debugging) ,为了使 gdb 正常工作,你必须使你的程序在编译时包含调试信息。调试信息包含你程序里的每个变量的类型和在
16、可执行文件里的地址映射以及源代码的行号。gdb 利用这些信息使源代码和机器码相关联。 在编译时用 -g 选项打开调试选项 。 gdb 支持很多的命令使你能实现不同的功能。 这些命令从简单的文件装入到允许你检查所调用的堆栈内容的复杂命令,下表列出了你在用 gdb 调试时会用到的一些命令。基本 gdb 命令file 装入想要调试的可执行文件。 kill 终止正在调试的程序。 list 列出产生执行文件的源代码的一部分。 next 执行一行源代码但不进入函数内部。 step 执行一行源代码而且进入函数内部。 run 执行当前被调试的程序 quit 终止 gdb watch 使你能监视一个变量的值而不
17、管它何时被改变。 break 在代码里设置断点, 这将使程序执行到这里时被挂起。make 使你能不退出 gdb 就可以重新产生可执行文件。 5shell 使你能不离开 gdb 就执行 UNIX shell 命令。gdb 应用举例 被调试的程序相当的简单,但它展示了 gdb 的典型应用。下面列出了将被调试的程序。这个程序被称为 greeting,它显示一个简单的问候,再用反序将它列出。 #include main () char my_string = “hello there“; my_print (my_string); my_print2 (my_string); void my_prin
18、t (char *string) printf (“The string is %sn“, string); void my_print2 (char *string) char *string2; int size, i; size = strlen (string); string2 = (char *) malloc (size + 1); for (i = 0; i 2 3 main () 4 5 char my_string = “hello there“; 6 7 my_print (my_string); 8 my_print2 (my_string); 9 10 如果按下回车,
19、 gdb 将再执行一次 list 命令, 给出下列输出: 11 my_print (char *string) 12 13 printf (“The string is %sn“, string); 14 15 16 my_print2 (char *string) 17 18 char *string2; 19 int size, i; 20 再按一次回车将列出 greeting 程序的剩余部分: 21 size = strlen (string); 22 string2 = (char *) malloc (size + 1); 23 for (i = 0; i size; i+) 24
20、string2size - i = stringi; 25 string2size+1 = 0; 26 printf (“The string printed backward is %sn“, string2); 727 根据列出的源程序, 你能看到要设断点的地方在第 24 行, 在 gdb 命令行提示符下键入如下命令设置断点: (gdb) break 24 gdb 将作出如下的响应: Breakpoint 1 at 0x139: file greeting.c, line 24 现在再键入 run 命令, 将产生如下的输出: Starting program: /root/greeting
21、 The string is hello there Breakpoint 1, my_print2 (string = 0xbfffdc4 “hello there“) at greeting.c :24 24 string2size-i=stringi 你能通过设置一个观察 string2size - i 变量的值的观察点来看出错误是怎样产生的, 做法是键入: (gdb) watch string2size - i gdb 将作出如下回应: Watchpoint 2: string2size - i 现在可以用 next 命令来一步步的执行 for 循环了: (gdb) next 经过第一
22、次循环后, gdb 告诉我们 string2size - i 的值是 h。gdb 用如下的显示来告诉你这个信息: Watchpoint 2, string2size - i Old value = 0 000 New value = 104 h my_print2(string = 0xbfffdc4 “hello there“) at greeting.c:23 23 for (i=0; i 这个值正是期望的。 后来的数次循环的结果都是正确的。 当 i=10 时, 表达式 string2size - i的值等于e , size - i 的值等于 1, 最后一个字符已经拷到新串里了。 如果你再
23、把循环执行下去, 你会看到已经没有值分配给 string20了, 而它是新串的第一个字符, 因为 malloc 函数在分配内存时把它们初始化为空(null)字符。所以 string2 的第一个字符是空字符。这解释了为什么在打印 string2 时没有任何输出了。 现在找出了问题出在哪里, 修正这个错误是很容易的。你得把代码里写入 string2 的第一个字符的的偏移量改为 size - 1 而不是 size。这是因为 string2 的大小为 12,但起始偏移量是 0,串内的字符从偏移量 0 到偏移量 10,偏移量11 为空字符保留。为了使代码正常工作有很多种修改办法。 一种是另设一个比串的实
24、际大小小 1 的变量,具体解法略。1.3.4 gcc 的错误类型及对策 gcc 编译器如果发现源程序中有错误,就无法继续进行,也无法生成最终的8可执行文件。为了便于修改,gcc 给出错误资讯,我们必须对这些错误资讯逐个进行分析、处理,并修改相应的语言,才能保证源代码的正确编译连接。gcc给出的错误资讯一般可以分为四大类,下面我们分别讨论其产生的原因和对策。第一类C 语法错误 错误资讯文件 source.c 中第 n 行有语法错误(syntex errror)。这种类型的错误,一般都是 C 语言的语法错误,应该仔细检查源代码文件中第 n 行及该行之前的程序,有时也需要对该文件所包含的头文件进行检
25、查。有些情况下,一个很简单的语法错误,gcc 会给出一大堆错误,我们最主要的是要保持清醒的头脑,不要被其吓倒,必要的时候再参考一下 C 语言的基本教材。 第二类头文件错误 错误资讯找不到头文件 head.h(Can not find include file head.h)。这类错误是源代码文件中的包含头文件有问题,可能的原因有头文件名错误、指定的头文件所在目录名错误等,也可能是错误地使用了双引号和尖括号。 第三类档案库错误 错误资讯连接程序找不到所需的函数库,例如 ld: -lm: No such file or directory 这类错误是与目标文件相连接的函数库有错误,可能的原因是函数
26、库名错误、指定的函数库所在目录名称错误等,检查的方法是使用 find 命令在可能的目录中寻找相应的函数库名,确定档案库及目录的名称并修改程序中及编译选项中的名称。 第四类未定义符号 错误资讯有未定义的符号(Undefined symbol)。这类错误是在连接过程中出现的,可能有两种原因一是使用者自己定义的函数或者全局变量所在源代码文件,没有被编译、连接,或者干脆还没有定义,这需要使用者根据实际情况修改源程序,给出全局变量或者函数的定义体;二是未定义的符号是一个标准的库函数,在源程序中使用了该库函数,而连接过程中还没有给定相应的函数库的名称,或者是该档案库的目录名称有问题,这时需要使用档案库维护命令 ar 检查我们需要的库函数到底位于哪一个函数库中,确定之后,修改 gcc 连接选项中的-l 和-L 项。