文档1

更新时间:2023-12-15 02:53:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

中国矿业大学计算机学院

2012 级本科生实验报告

课程名称 系统软件开发实践

报告时间 2015-4-28 学生姓名 闫呈祥 学 号 08123272 专 业 计算机科学与技术 任课教师 刘晋

《系统软件开发实践》 实验报告 第1页

实验一(第一周) 词法分析器(flex实验)

一、实验目的

1. 2. 3. 4.

通过实验了解flex的基本语法,加深对词法分析程序的功能及实现方法的理解; 使用flex实现词法分析程序;

理解编译器的构造机制,掌握构造器的构造方法; 掌握词法分析器生成工具LEX的用法;

二、实验要求

通过对flex的学习编制具有下面功能的词法分析器:

1、可以匹配识别关键字:else if switch for int float return void while(所

有的关键字都是保留字,并且必须是小写);

2、可以匹配识别专用符号: + - * / < <= > >= == != = ; , ( ) [ ] { } /* */ 3、标识符(ID) 和数字(NU )通过下列正则表达式定义:

ID = letter letter* NUM = digit digit* letter = a|..|z|A|..|Z digit = 0|..|9

4、可以匹配识别空格(空格由空白、换行符和制表符组成,空格通常被忽略,,除了它

必须分开ID、NUM 关键字);

5、可以识别简单的注释(/* 注释内容*/);

三、实验环境

Windows系统、Visual C++6.0、flex.exe等

四、实验原理及分析

Lex工具是一种词法分析程序生成器,它可以根据词法规则说明书的要求来生成单词识别程序,由该程序识别出输入文本中的各个单词。一般可以分为<定义部分><规则部分><用户子程序部分>。其中规则部分是必须的,定义和用户子程序部分是任选的。

(1)定义部分:定义部分起始于 %{ 符号,终止于 %} 符号,其间可以是包括include语句、声明语句在内的C语句。这部分跟普通C程序开头没什么区别。

(2)规则部分:规则部分起始于\符号,终止于\符号,其间则是词法规则。词法规则由模式和动作两部分组成。模式部分可以由任意的正则表达式组成,动作部分是由C语言语句组成,这些语句用来对所匹配的模式进行相应处理。需要注意的是,lex将识别出来的单词存放在yytext[]字符数据中,因此该数组的内容就代表了所识别出来的单词的内容。类似yytext这些预定义的变量函数会随着后面内容展开一一介绍。动作部分如果有多行执行语句,也可以用{}括起来。

(3)用户子程序部分:最后一个%%后面的内容是用户子程序部分,可以包含用C语言编写的子程序,而这些子程序可以用在前面的动作中,这样就可以达到简化编程的目的。这里需要注意的是,当编译时不带-ll选项时,是必须加入main函数和yywrap(yywrap将下后面说明)。

Lex其实就是词法分析器,通过配置文件*.l,依据正则表达式逐字符去顺序解析文件,并动态更新内存的数据解析状态。Lex善长于模式匹配。

词法分析的基本任务是从字符串表示的源程序中识别出具有独立意义的单词符号,其

-

《系统软件开发实践》 实验报告 第2页

基本思想是根据扫描到单词符号的第一个字符的种类,拼出相应的单词符号。词法分析阶段是编译过程的第一个阶段,是编译的基础。这个阶段的任务是从左到右一个字符一个字符地读入源程序,即对构成源程序的字符流进行扫描然后根据构词规则识别单词(也称单词符号或符号)。

词法分析是编译程序的第一个阶段且是必要阶段;词法分析的核心任务是扫描、识别单词且对识别出的单词给出定性、定长的处理;实现词法分析程序的常用途径:自动生成,手工生成。而本次实验用的是自动生成工具flex,相对于手动生成可以极大地减少工作量。

单词的描述也就是模式(Lexical Pattern),模式一般用正规表达式进行精确描述。FLEX通过读取一个有规定格式的文本文件,输出一个如下所示的C语言源程序。

| 输入文件*.l |------>|flex工具 |------>|输出文件lex.yy.c |

FLEX的输入文件为LEX源文件,它内含正规表达式和对相应模式处理的C语言代码。FLEX通过对.l源文件的扫描自动生成相应的词法分析函数 int yylex(),并将之输出到lex.yy.c的文件中。该文件即为LEX的输出文件或输出的词法分析器。

LEX的源文件由三个部份组成,每个部分之间用顶行的“%%”分割,其格式如下:

定义部份 %%

规则部份 %%

用户附加C语言部份

其中,定义部分由C语言代码、模式的宏定义、条件模式的开始条件说明三部份组成。C代码部份由顶行的%{和}%引入,LEX扫描源文件时将%{和}%之间的部分原封不动的拷贝到输出文件lex.yy.c中。而模式宏定义则是一个正则表达式的定义。正则表达式的匹配如下:

第二部分规则部份是LEX源文件的核心部份,它包括一组模式和在生成分析器识别相应模式后对相应模式进行处理的C语言动作(Action)。

LEX对第三部分不作任何处理,仅仅将之直接拷贝到输出文件lex.yy.c的尾部。在些部份,可定义对模式进行处理的C语言函数、主函数和yylex要调用的函数yywrap()等。如果用户在其它C模块中提供这些函数,用户代码部份可以省略。

yylex()函数被调用之后,它首先检查全局文件指针变量yyin是否有定义,如有,则将之设置为将要扫描的文件指针。如无,则设置为标准输入文件stdin。同理,如全局文件指针变量yyout无定义,则将之设置为标准输出文件stdout。若有多个模式与被扫描文件中的字符串相匹配,则yylex()执行能匹配最长字符串的模式,称为“最长匹配原则”;若还有多个模式匹配长度相同的字符串, 则yylex()选择在LEX源文件中排列最前面的模

-

《系统软件开发实践》 实验报告 第3页

式进行匹配,称为“最先匹配原则”。yylex()常通过超前搜索一个字符来实现这样的原则,如果使 用超前搜索匹配了某一模式,则yylex()在进行下一次分析前,将回退一个字符。

另外,LEX提供控制模式在一定状态下使用的功能,称为条件模式。LEX首先在定义部份通过%start来定义条件句。在规则部份可通过宏BEGIN 条件名来激活条件。BEGIN INITIAL或BEGIN 0将休眠所有的条件模式,使分析器回到开始状态。

五、实验步骤

1、lex源代码编写

通过对flex的语法学习,掌握了编写的基本原则和步骤,因为实验要求编写一个简单地词法分析器,根据实验所要求实现检查分析的功能,实验代码较为简单,以下是自己编写的实验代码: %{

#include #include #include #include int lineno=1; %}

letter[A-Za-z] digit[0-9]

id({letter}|[_])({letter}|[_])* error_id({digit})+({letter})+ num{digit}+ whitespace[\\t]+ enter[\\n]+ %%

\{Upper(yytext,yyleng);

printf(\行 \

printf(\{num}

{printf(\行 \printf(\

\]\

{printf(\行 \

printf(\{id}

{printf(\行 \ printf(\{error_id}

{ printf(\行 \printf(\{whitespace} {/* skip whitespace */}

-

《系统软件开发实践》 实验报告 第4页

{enter} {lineno++;} %%

Upper(char *s,int l){ int i;

for(i=0;i

main(void) {

char infilename[400]; printf(\输入文件名:\ scanf(\

yyin = fopen(infilename,\ printf(\开始词法分析: \\n\

return yylex(); }

最后将这些代码按照flex语法进行整合得到完整flex源码,得到源程序 lex1.l; 2、打开Visual Studio 2010 命令行; 3、生成 lex1.yy.c: (1)进入flex安装目录 > cd F:\\GnuWin32\\bin; (2)调用flex.exe

> flex.exe -o\4、调用VS2010编译器cl.exe:

5、编写测试文件(命名为1—1.cpp)并与lex.yy.exe放于同一文件夹内;

1—1.cpp:

#include using namespace std; int main {

int a=33,b=1123; c=12; c=a+b;

cout<<\return 0; }

4、运用命令行的lex.yy.exe <1—1.cpp运行得到结果:

-

《系统软件开发实践》 实验报告 第15页

8.文件系统,了解文件系统的具体实现,与进程管理等的关系,了解缓存对操作系统IO访问的性能改进,了解虚拟文件系统(VFS)、buffer、cache和disk、driver之间的关系。

其中每个开发步骤都是建立在上一个步骤之上的,一步步完成从理解操作系统原理到实践操作系统设计与实现的探索过程。

实验五(第五周) 操作系统实验(Lab1实验)

一、实验目的

通过分析和实现这个bootloader和ucore OS我们可以了解:

1、计算机原理中CPU的编址与寻址、基于分段机制的内存管理CPU的中断机制以及外设(串口/并口/CGA,时钟,硬盘);

2、Bootloader软件中编译运行bootloader的过程、调试bootloader的方法、PC启动bootloader的过程、

ELF执行文件的格式和加载、外设访问:读硬盘,在CGA上显示字符串;

3、ucore OS软件编译运行ucore OS的过程、ucore OS的启动过程、调试ucore OS的方法、函数调用关系(在汇编级了解函数调用栈的结构和处理过程)、中断管理(软件相关的中断处理)、外设管理(时钟)。

二、实验说明

ucore的运行环境可以是真实的X86计算机,不过考虑到调试和开发的方便,我们可采用X86硬件模拟器。

-

《系统软件开发实践》 实验报告 第16页

(ucore系统结构图)

操作系统是一个软件,也需要通过某种机制加载并运行它。 在这里我们将通过另外一个更加简 单的软件-bootloader来完成这些工作。为此, 我们需要完成一个能够切换到x86的保护模式并显示字符的bootloader, 为启动操作系统ucore做准备。lab1提供了一个非常小的bootloader和ucore OS, 整个bootloader执行代码小于512个字节,这样才能放到硬盘的主引导扇区中。

而lab1中包含一个bootloader和一个OS。 这个bootloader可以切换到X86保护模式, 能够读磁盘并加载ELF执行文件格式, 并显示字符。 而这lab1中的OS只是一个可以处理时钟中断和显示字符的简单的OS。 ├── boot 引导扇区的代码 ├── kern 操作系统内核代码 ├── libs JOS的C代码库

Lab1代码树

├── tools 代码测试工具和脚本 ├── Makefile 代表整体组装运行的过程

三、OS启动过程分析

PC加电启动:Bochs模拟硬件,可以通过修改Bochs的配置调整硬件。BIOS运行,BIOS程序被Bochs加载到0xF0000-0x100000处于实模式下,寻址空间1M检查和初始化硬件,加载引导扇区到0000:7c00 –0000:7dff;

引导扇区(即BootLoader)执行:BIOS的最后一条指令是跳转到0x7c00处,把CPU

-

《系统软件开发实践》 实验报告 第17页

控制权交给了BootLoader程序,实模式转换为保护模式,加载内核文件;

内核开始执行:初始化内核数据结构,运行终端。 CPU加电 BIOS从0xffff0开始执行,进行硬件测试、初始化设备等? 将boot loader拷贝于0x7c00,跳转后进入boot loader 执行BIOS boot/boot.s完成实模式到保护模式的切换; boot/main.c载入内核于0x10000跳转后执行内核 执行boot loader

kern/entry.S设置GDT,初始化堆栈

进入kernel

(操作系统启动流程图)

三、实验过程与详细分析

(1)理解通过make生成执行文件的过程

首先说一下操作系统镜像文件 ucore.img 是如何一步一步生成的:linux系统下在命令行中输入“make V=”即可看到生成过程,下面我以流程图的方式分步骤一一说明下操作系统镜像文件的生成过程:

首先把C语言的源代码进行编译成为.o文件,也就是目标文件

ld命令将这些目标文件转变成可执行文件,比如此处的bootblock.out

dd命令把bootloder放到ucore.img count的虚拟硬盘之中

还生成两个软件,一个是Bootloader,另一个是kernel

分析lab1/tools/sign.c中的代码一个被系统认为是符合规范的硬盘主引导扇区

的特征可以从以下代码看出,即一个规范的硬盘引导扇区的大小为512字节,硬盘结束标志位55AA。

-

《系统软件开发实践》 实验报告 第18页 fclose(ifp); buf[510] = 0x55; buf[511] = 0xAA; FILE *ofp = fopen(argv[2], \ size = fwrite(buf, 1, 512, ofp); if (size != 512) { fprintf(stderr, is %d.\\n\ return -1; } fclose(ofp); printf(\512 bytes boot sector: '%s' success!\\n\ return 0; \'%s' error, size 顺便谈一下Makefile,它是用于自动编译和链接的,一个工程有很多文件组成,每一个文件的改变都会导致工程的重新链接,但是不是所有的文件都需要重新编译,Makefile中纪录有文件的信息,在make时会决定在链接的时候需要重新编译哪些文件。Makefile就是让编译器知道要编译一个文件需要依赖其他的哪些文件。当那些依赖文件有了改变,编译器会自动的发现最终的生成文件已经过时,而重新编译相应的模块。

使用automake,程序开发人员只需要写一些 简单的含有预定义宏的文件,由autoconf根据一个宏文件生成configure,由automake根据另一个宏文件生成Makefile.in, 再使用configure依据Makefile.in来生成一个符合惯例的Makefile。

主引导扇区包括硬盘主引导记录和分区表DPT。其中主引导记录的作用就是检查分区表是否正确以及确定哪个分区为引导分区,并在程序结束时把该分区的启动程序,也就是操作系统引导扇区调入内存加以执行。

主引导扇区记录硬盘上每一个分区的数据的地方,这个地方不存放实际的数据,只保存每一个扇区的信息。类似图书馆里面的书和图书馆的藏书目录,主引导扇区是目录,每一个扇区里面存放的是书,如果目录丢失了,你将无法找到想要的书了,当然这个目录在某种情况下是能用软件恢复的的。 完成这个操作的方法有两种,一种是使用系统的格式化程序,我们知道在安装系统的时候我们最开的的操作就是划出分区,格式化硬盘才可以,这个操作就能完成。还有一种就是使用专业的硬盘操作软件,在DOS下完成。

} (2)使用qemu执行并调试lab1中的软件

本次练习主要是了解从CPU加电后执行的第一条指令开始, 单步跟踪BIOS的执行并在初始化位置0x7c00设置实地址断点,测试断点正常、从0x7c00开始跟踪代码运行,将单步跟踪反汇编得到的代码与bootasm.S和 bootblock.asm进行比较。然后自己找一个bootloader或内核中的代码位置,设置断点并进行测试。

在初始化位置0x7c00 设置实地址断点,测试断点正常,下面是我在linux环境下运行的一张截图:

在tools/gdbinit结尾加上 set architecture i8086

b *0x7c00 //在0x7c00处设置断点。 continue

x /2i $pc //显示当前eip处的汇编指令

-

《系统软件开发实践》 实验报告 第19页

我们主要通过硬件模拟器qemu来进行各种实验。在实验的过程中我们可能会遇上各种各样的问题,调试是必要的。qemu支持使用gdb进行的强大而方便的调试。所以用好qemu和gdb是完成各种实验的基本要素。

将执行的汇编代码与bootasm.S和 bootblock.asm 进行比较可知Notice:在q.log中进入BIOS之后的跳转地址与实际应跳转地址不相符,汇编代码也与bootasm.S和 bootblock.asm不相同,这是由于在gdb之中调试的原因,可以直接输入make debug,在生成的qemu虚拟机之中进行调试可以看到在虚拟机中运行的汇编代码,之后再与bootasm.S和 bootblock.asm 进行比较与bootasm.S和bootblock.asm中的代码相同。

(3)分析bootloader进入保护模式的过程

本次以注释和代码相结合的方式分析bootloader进入保护模式的过程: globl start start: .code16

# 关中断,并清除方向标志,即将 DF 置“0”,这样(E)SI 及(E)DI 的修改为增量 cli cld

# 清零各数据段寄存器:DS、ES、FS xorw %ax, %ax movw %ax, %ds movw %ax, %es movw %ax, %ss

# 使能 A20 地址线,这样 80386 就可以突破 1MB 访存现在,而可访问 4GB 的 32 位地址空间 seta20.1:

inb $0x64, %al # 等待8042键盘控制器不忙testb $0x2, %al jnz seta20.1 movb $0xd1, %al outb %al, $0x64 seta20.2:

inb $0x64, %al # 等待8042键盘控制器不忙 testb $0x2, %al jnz seta20.2

movb $0xdf, %al # 打开A20

-

本文来源:https://www.bwwdw.com/article/ewm5.html

Top