资源描述
计算机科学与技术学院
系统软件开发实践报告
姓 名: 王冬升
学 号: 08123228
专 业: 计算机科学与技术
班 级: 计科12-1班
指导教师: 张 博
2015年 4月
摘 要
操作系统和编译原理是大学计算机专业的必修课程。在这些课程的教学教程中,容易偏重于理论的介绍,而忽视了实践环节。
本课程设计是在完成C++程序设计、数据结构、操作系统、编译技术教学后所进行的,全面综合训练学生的系统软件开发能力
关键字:编译系统、Flex、Bison、操作系统、启动、内存管理
计算机科学与技术学院系统软件开发实践报告 第17页
目 录
1 借助Flex进行词法分析 …………………………………………………2
1.1实验内容 ………………………………………………………………2
1.2 实验要求 ………………………………………………………………2
1.3 程序代码 ………………………………………………………………2
1.4 实验结果 ………………………………………………………………4
2 借助Flex/Bison进行语法分析 …………………………………………5
2.1 实验内容 ………………………………………………………………5
2.2 实验要求 ………………………………………………………………5
2.3 程序代码 ………………………………………………………………5
2.4 实验步骤 ………………………………………………………………5
2.5 实验结果 ………………………………………………………………7
3 Flex/Bison综合实验 ……………………………………………………7
3.1 实验内容 ………………………………………………………………7
3.2 计算器具体需要实现的功能 …………………………………………7
3.3 实验要求 ………………………………………………………………7
3,4 程序代码 ………………………………………………………………8
3.5 实验结果 ………………………………………………………………11
4(操作系统实验)lib0:熟悉实验环境 …………………………………11
4.1 实验目的 ………………………………………………………………11
4.2 实验环境搭建 …………………………………………………………11
4.3 GCC编译练习 …………………………………………………………12
4.4 GDB调试练习 …………………………………………………………12
5(操作系统实验)lib1:启动操作系统 …………………………………13
5.1 实验目的 ………………………………………………………………13
5.2 实验内容 ………………………………………………………………14
5.3 实验步骤 ………………………………………………………………14
5.4 操作系统启动过程 ……………………………………………………15
5.5 实模式与保护模式 ……………………………………………………16
6(操作系统实验)lib2:物理内存管理 …………………………………16
6.1 实验目的 ………………………………………………………………16
6.2 实验内容 ………………………………………………………………16
6.3 ………………………………………………………………
7实验体会 ……………………………………………………………………
8参考文献 ……………………………………………………………………
1借助Flex进行词法分析
1.1实验内容
给定C语言的一个子集,具体内容如下:
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. 注释用通常的C语言符号/* . . . */围起来。注释可以放在任何空白出现的位置(即注释不能放在标记内)上,且可以超过一行。注释不能嵌套
1.2实验要求
编写 LEX 源文件,实现C 语言子集的词法分析功能,最后上机调试。
要求编写一个测试程序,以给定的测试文件作为输入,输出运行结果到输出文件中
1.3程序代码
Lex.l源码:
%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int yywrap();
int lineno=1;
%}
delim [ \t]
ws {delim}+
letter [A-Za-z]
digit [0-9]
id {letter}+
number {digit}+
enter [ \n]
spchar ("{"|"}"|"["|"]"|"("|")"|";"|"="|","|"+"|"-"|"*"|"/"|"<"|"<="|">"|">="|"=="|"!=")
comment \/\*(\*[^/]|[^*])*\*\/
kwd (else|if|switch|for|int|float|return|void|while)
%%
{ws} {}
{comment} {}
{enter} {lineno++;}
{kwd} {fprintf(yyout,"%d行\tkeywod\t%s\n",lineno,yytext);}
{spchar} {fprintf(yyout,"%d行\tspchar\t%s\n",lineno,yytext);}
{id} {fprintf(yyout,"%d行\tidenti\t%s\n",lineno,yytext);}
{number} {fprintf(yyout,"%d行\tnumber\t%s\n",lineno,yytext);}.
%%
int yywrap() {return 1;}
int main(void)
{
char infilename[100];
printf("输入文件名:");
scanf("%s",infilename);
yyin = fopen(infilename,"r");
yyout = fopen("out","w");
yylex();
return 0;
}
测试程序:1-1.cpp
#include<iostream>
using namespace std;
int main() {
int a;
int d=123456;
float b=123.45;
int e=9;
a=(1+2-1)*4/2;
if(a>b)cout<<"a>b"<<endl;
else cout<<"a<=b"<<endl;
cout<<a<<endl;
cout<<b<<endl;
cout<<"Welcome to c++"<< endl;
return 0;
}
1.4实验结果
实验结果输出到.out文件中,截图示例:
2借助Flex/Bison进行语法分析
2.1实验内容
利用附录提供的C语言文法的相关参考资料,利用Yacc/Bison编写一个C语言分析器。具体内容:
1) 利用语法分析器生成工具Bison编写一个语法分析程序,与词法分析器结合能够根据语言的上下文无关文法,识别输入的单词序列是否文法的句子
2) 利用附录提供的C语言文法的相关参考资料,利用Yacc/Bison编写一个C语言分析器。
2.2 实验要求
1) 编写 Bison源文件,实现C 语言的语法分析功能,最后上机调试。
2) 要求编写一个测试程序,以给定的测试文件作为输入,输出运行结果到输出文件中。
2.3程序代码
由于本实验的lex源文件和yacc源文件代码较长而且在“实验附录:C语言相关文法”文件夹中已经给出相关源码的链接,由于报告篇幅限制不再贴出源码
Test.测试文件源码:
void main()
{ int i = 0;int j = 0;
}
void t1()
{ int i = 0;
}
typedef unsigned int uint;
uint xx;
uint yy;
2.4实验步骤
1、flex -l input.lex
bison -d cgrammar-new.y (产生头文件和.c文件)
2、comment函数调用yyinput,编译会有链接错误,解决方法一,将lex.yy.c中的yyinput函数定义拷贝一份到input.lex, 重命名为my_yyinput,
或者将lex.yy.c中的
#ifdef __cplusplus
static int yyinput()
#else
static int input()
#endif
改为static int yyinput()
上述问题出现的情形:词法分析器的后缀名为.c, 而不是.cpp
3、cgrammar-new.tab.c中如下程序段
/*----------.
| yyparse. |
`----------*/
#ifdef YYPARSE_PARAM
# if defined (__STDC__) || defined (__cplusplus)
int yyparse (void *YYPARSE_PARAM)
# else
int yyparse (YYPARSE_PARAM)
void *YYPARSE_PARAM;
# endif
#else /* ! YYPARSE_PARAM */
#if defined (__STDC__) || defined (__cplusplus)
int
yyparse (void)
#else
int
yyparse () ;
#endif
#endif
改为
int yyparse () 或者 int yyparse (void)
4、lex.yy.c中,将下面语句注释
if ( ! yyin )
yyin = stdin;
5、解析之前,设置yyin为输入文件指针
extern FILE *yyin;
if(!(yyin = fopen(filename,"r"))) {
printf("the file not exist\n");
exit(0);
}
2.5实验结果
实验结果输出到.out文件中,截图示例:
3 Flex/Bison综合实验
3.1实验内容
使用flex和bison开发了一个具有全部功能的桌面计算器,能够支持变量,过程,循环和条件表达式,使它成为一个虽然短小但是具有现实意义的编译器。
重点学习抽象语法树的用法,它具有强大而简单的数据结构来表示分析结果
3.2计算器具体需要实现的功能
变量命名;实现赋值功能;实现比较表达式(大于、小于、等于等等)实现if/then/else和do/while的流程控制;用户可以自定义函数;简单的错误恢复机制
3.3实验要求
编写 Flex/Bison源文件,实现C 语言的语法分析功能,最后上机调试。
要求编写一个测试程序:
1) 首先自定义两个函数sq和avg,sq函数使用Newton方法来迭代计算平方根;avg函数计算两个数值的平均值。
2) 利用定义好的函数进行计算,得到计算结果并显示出来
3.4程序代码
Cal.l源码
%{
#include "cal.tab.h"
%}
%option noyywrap
integer [0-9]+
dreal ([0-9]*"."[0-9]+)
ereal ([0-9]*"."[0-9]+[EedD][+-]?[0-9]+)
real {dreal}|{ereal}
nl \n
plus "+"
minus "-"
times "*"
divide "/"
lp "("
rp ")"
module "%"
power "^"
%%
[ \t] ; /*skip any blanks */
{integer} { sscanf(yytext, "%d", &yylval.integer);
return INTEGER;}
{real} { sscanf(yytext, "%lf", &yylval.real);/*yylval = atof(yytext); it doesn't work under MSVSC*/
return REAL; }
{plus} { return PLUS;}
{minus} { return MINUS;}
{times} { return TIMES;}
{divide} { return DIVIDE;}
{module} { return MODULE;}
{power} { return POWER;}
{lp} { return LP;}
{rp} { return RP;}
{nl} { return NL;}
. { return yytext[0];}
Cal.y源码
%{
#include <stdio.h>
#include <math.h>
%}
%union{ double real; /* real value */
int integer; /* integer value */}
%token <real> REAL
%token <integer> INTEGER
%start lines
%token NUMBER NL
%token PLUS MINUS TIMES DIVIDE MODULE POWER LP RP
%type <real> rexpr
%type <integer> iexpr
%left PLUS MINUS /*left associative */
%left TIMES DIVIDE MODULE /*left associative */
%left POWER
%left UNARYMINUS
%%
lines: /* nothing */
| lines line NL
| lines error NL
{ yyerror();yyerrok; };
line : iexpr
{printf("%d\n",$1);}
| rexpr
{printf("%lf\n",$1);} ;
iexpr: INTEGER
{ $$ = $1; }
| iexpr PLUS iexpr
{ $$ = $1 + $3;}
| iexpr MINUS iexpr
{ $$ = $1 - $3;}
| iexpr TIMES iexpr
{ $$ = $1 * $3;}
| iexpr DIVIDE iexpr
{ if($3)
$$ = $1 / $3;
else { $$ = $1;
printf (stderr, "%d.%d-%d.%d: division by zero",
@3.first_line, @3.first_column,
@3.last_line, @3.last_column); }}
| iexpr MODULE iexpr
{ $$ = $1 % $3; }
| iexpr POWER iexpr
{ $$ = pow($1, $3);}
| MINUS iexpr %prec UNARYMINUS
{ $$ = - $2;}
| LP iexpr RP
{ $$ = $2;}
| LP iexpr error
{ $$ = $2; yyerror("missing ')'"); yyerrok;}
| PLUS iexpr %prec UNARYMINUS
{ $$ = $2;};
rexpr :REAL
{ $$ = $1; }
| rexpr PLUS rexpr
{ $$ = $1 + $3; }
| rexpr MINUS rexpr
{ $$ = $1 - $3; }
| rexpr TIMES rexpr
{ $$ = $1 * $3; }
| rexpr DIVIDE rexpr
{if ($3)$$ = $1 / $3;
else
{$$ = $1;
printf (stderr, "%d.%d-%d.%d: division by zero",
@3.first_line, @3.first_column,
@3.last_line, @3.last_column);} }
| rexpr POWER rexpr
{ $$ = pow($1,$3); }
| LP rexpr RP
{ $$ = $2; }
| LP rexpr error
{ $$ = $2; yyerror("missing ')'"); yyerrok;}
| MINUS rexpr %prec UNARYMINUS
{ $$ = -$2; }
| PLUS rexpr %prec UNARYMINUS
{ $$ = $2;}
| iexpr PLUS rexpr
{ $$ = (double)$1 + $3;}
| iexpr MINUS rexpr
{ $$ = (double)$1 - $3;}
| iexpr TIMES rexpr
{ $$ = (double)$1 * $3;}
| iexpr DIVIDE rexpr
{ if($3) $$ = (double)$1 / $3;
else
{ $$ = $1;
printf (stderr, "%d.%d-%d.%d: division by zero",
@3.first_line, @3.first_column,
@3.last_line, @3.last_column);}}
| iexpr POWER rexpr
{ $$ = pow((double)$1,$3); }
| rexpr PLUS iexpr
{ $$ = $1 + (double)$3;}
| rexpr MINUS iexpr
{ $$ = $1 - (double)$3;}
| rexpr TIMES iexpr
{ $$ = $1 * (double)$3;}
| rexpr DIVIDE iexpr
{ if($3)
$$ = $1 / (double)$3;
else
{ $$ = $1;
printf (stderr, "%d.%d-%d.%d: division by zero",
@3.first_line, @3.first_column,
@3.last_line, @3.last_column);}}
| rexpr POWER iexpr
{ $$ = pow($1,(double)$3); };
%%
void main()
{
yyparse();}
int yyerror(char* msg)
{printf("Error: %s encountered \n", msg);}
3.5实验结果
实验结果截图:
4(操作系统实验)lib0:熟悉实验环境
4.1实验目的
1) 了解操作系统开发实验环境
2) 熟悉命令行方式的编译、调试工程
3) 掌握基于硬件模拟器的调试技术
4) 熟悉C语言编程和指针的概念
5) 了解X86汇编语言
4.2实验环境搭建
1) 在线实验--基于"实验楼"在线平台
2) Windows下基于MingW进行实验
3) Windows下基于VirtualBox or VMWare进行实验
4) 在MAC OS下进行实验
5) 手动在物理PC中安装环境
4.3 GCC编译练习
采用实验环境:在线实验--基于"实验楼"在线平台
gcc示例:
#include<stdio.h>
int main(void)
{
printf("hello!\n");
return 0;
}
在实验环境中右键->从模板创建->空文件,将上述示例代码输入
1) 保存为hello.c
打开命令行,输入以下命令:
2) 编译:gcc -Wall hello.c -o hello
3) 执行:./hello
操作及结果截图:
4.4 GDB调试练习
1)输入命令:gcc -o hello hello.c –g进行编译
2)输入命令:gdb进入调试
3)依次输入以下命令熟悉gdb调试
file hello:进入hello文件
run:运行hello
break 1:在第一行插入断点
next:单步执行下一行语句
clear 1:清除第一行的断点
list :显示hello文件的内容
quit:退出gdb调试
对应命令执行结果截图:
GDB的其它调试命令补充:
next :单步到程序源代码的下一行,不进入函数。
nexti :单步一条机器指令,不进入函数。
step :单步到下一个不同的源代码行(包括进入函数)。
stepi :单步一条机器指令
他们功能各不相同,区别在于单步的“跨度”上。
5(操作系统实验)lib1:启动操作系统
5.1实验目的
操作系统是一个软件,也需要通过某种机制加载并运行它。在这里我们将通过另外一个更加简单的软件-bootloader来完成这些工作。为此,我们需要完成一个能够切换到x86的保护模式并显示字符的bootloader,为启动操作系统ucore做准备。lab1提供了一个非常小的bootloader和ucore OS,整个bootloader执行代码小于512个字节,这样才能放到硬盘的主引导扇区中。通过分析和实现这个bootloader和ucore OS,读者可以了解到:
1)计算机原理
CPU的编址与寻址: 基于分段机制的内存管理
CPU的中断机制
外设:串口/并口/CGA,时钟,硬盘
2)Bootloader软件
编译运行bootloader的过程
调试bootloader的方法
PC启动bootloader的过程
ELF执行文件的格式和加载
外设访问:读硬盘,在CGA上显示字符串
3)ucore OS软件
编译运行ucore OS的过程
ucore OS的启动过程
调试ucore OS的方法
函数调用关系:在汇编级了解函数调用栈的结构和处理过程
中断管理:与软件相关的中断处理
外设管理:时钟
5.2实验内容
lab1中包含一个bootloader和一个OS。这个bootloader可以切换到X86保护模式,能够读磁盘并加载ELF执行文件格式,并显示字符。而这lab1中的OS只是一个可以处理时钟中断和显示字符的幼儿园级别OS。
5.3实验步骤
熟悉以下命令,掌握操作系统的启动过程:
1) make(ucore.img的生成过程)
2) make V=(gcc把c的源代码编译成.o的目标文件 ld会把这些目标文件呢会转换成一个执行程序,这里面会转换成这个bootblock.out这个实际上是可以理解为是一个Bootloader一个执行程序,第一个是Bootloader 第二叫kernel)
3)启动系统:make qemu
4)启动并调试:
make debug:进入debug模式
n(执行下一行代码)
p ph(显示当前值)
continue(继续执行)
ctrl+c(中断)
quit(推出
5.4操作系统启动过程
当X86一开始加电时候,启动是实模式(实地址寻址模式)(早期的为了ss向下兼容
以前的80X86一开始启动是16位的实模式)
第一条地址:
段地址寄存器CSCS和EIP结合在一起来决定它启动的第一条地址:
按实模式的寻址方式:cs(base)基址+EIP->bios(指向bios)
这个bios固件会去加载磁盘或者硬盘的第一个主引导扇区(即执行0号扇区 lab1中的bootloader)它完成来对我们说的这个操作系统 uCore的进一步加载
Bootloader的工作:
1)要从实模式切换到保护模式,就是从实模式的16位的寻址空间切换到了32位的寻址空间寻址空间:从1M(物理地址=左移4位的段地址+偏移地址)变为4G,段机制可以正常工作了。对系统寄存器CRO(或者叫控制寄存器 )把它的第0号bit置成1那么就意味着现在的系统我们的CPU会进入到保护模式
2)就是读取kernel 就是uCore的代码,CS EIP的值指向我们操作系统内核所在内存中的起始点,之后就相当于是把控制权交给了uCore OS去执行
5.5实模式与保护模式
(1) 实模式
在bootloader接手BIOS的工作后,当前的PC系统处于实模式(16位模式)运行状态,在这种状态下软件可访问的物理内存空间不能超过1MB,且无法发挥Intel 80386以上级别的32位CPU的4GB内存管理能力。
实模式将整个物理内存看成分段的区域,程序代码和数据位于不同区域,操作系统和用户程序并没有区别对待,而且每一个指针都是指向实际的物理地址。这样,用户程序的一个指针如果指向了操作系统区域或其他用户程序区域,并修改了内容,那么其后果就很可能是灾难性的。通过修改A20地址线可以完成从实模式到保护模式的转换
(2) 保护模式
只有在保护模式下,80386的全部32根地址线有效,可寻址高达4G字节的线性地址空间和物理地址空间,可访问64TB(有2^14个段,每个段最大空间为2^32字节)的逻辑地址空间,可采用分段存储管理机制和分页存储管理机制。这不仅为存储共享和保护提供了硬件支持,而且为实现虚拟存储提供了硬件支持。通过提供4个特权级和完善的特权检查机制,既能实现资源共享又能保证代码数据的安全及任务的隔离。
6(操作系统实验)lib2:物理内存管理
6.1实验目的
理解基于段页式内存地址的转换机制
理解页表的建立和使用方法
理解物理内存的管理方法
6.2实验内容
本次实验包含三个部分。首先了解如何发现系统中的物理内存;然后了解如何建立对物理内存的初步管理,即了解连续物理内存管理;最后了解页表相关的操作,即如何建立页表来实现虚拟内存到物理内存之间的映射,对段页式内存管理机制有一个比较全面的了解。本实验里面实现的内存管理还是非常基本的,并没有涉及到对实际机器的优化,比如针对 cache 的优化等
7实验体会
在老师的指导下和与同学之间互相讨论学习,经过六周的时间终于完成了有关系统软件开发实践课程的学习,感觉无论从专业知识还是个人能力上都收获很多。
首先是有关本实验课程六个小实验的收获与总结,第一部分编译原理实验中第一个flex词法分析器的实验相对比较简单,完成的相对顺利,第二个bison语法分析器实验比较难,虽然实验指导中已经给出了源码链接,第三个编译综合实验计算器也只完成了部分基础运算符的功能,有关自定义函数的功能部分没有完成;第二部分操作系统实验因为有详细的实验指导书和相关的视频,通过视频课程的学习和参考实验指导书完成了基本的实验要求,但是对操作系统的深入学习和了解还有待进一步的学习,对于如何写出一个小型的操作系统的能力还是相差甚远的,仅仅是对操作系统理论课程上讲的知识有了进一步的相对实际的理解和认识。
然后是对以后的学习上的收获与经验,理论知识的学习要与实践性的课程学习相互结合,在实践课中巩固和进一步理解理论知识,只有这样才能真正做到学以致用,才不会遗忘的更快。特别是我们理工科的课程,学完之后不能停留在书本上,毕竟转化为自己实际的编程能力才是对将来就业和工作或者继续深入学习最好的帮助。同时我们要学会利用网络学习,不能仅仅局限于老师和课堂,利用网络学习的新的学习方式是我们今后主要的学习方式和最好的自学方式,要学会自己想法去解决问题总结知识而不是等待知识的灌输。
8 参考文献
【1】Chen Yu.ucore实验指导书.清华大学出版社
展开阅读全文