Linux Chapter3 Exercise

本科时期Linux 程序设计的实验文档整理。

一.实验内容

T1

1.编写程序对一个长为10的数组进行排序,分别使用冒泡排序法、选择排序和二分法排序,三种排序方案分别编译成为动态库和静态库文件,在主函数进行调用,编写实验报告,提交源代码。

简单记录了以下做实验的过程,这里以bubblesort为例(不见得全与题目相关,还带有一些ppt上基本内容的尝试记录)。

1.单个源文件生成可执行程序

步骤1:编辑程序

1
vim bubblesort.C

编辑好程序以后,进入到目录文件夹,可以查看自己编辑好的程序。
在Linux中,一般情况下我们认为后缀名是【.C】【.cc】【.cxx】是C++源文件。

Image

步骤2:编译程序

1
g++ bubblesort.C

编译完成以后,在对应的文件夹里生成了a.out的可执行文件。
Image

步骤3:运行程序

接下来执行文件:

1
./a.out

执行效果如下所示:
Image

2.静态库编译:

静态库,也称作归档库(archive)。按惯例函数库的文件名都以.a结尾。

1、首先将源文件夹编译成目标文件;

1
g++ -c bubblesort.C -o bubblesort

注意 -c 参数是必须带,生成目标代码(机器代码),如果不带该参数,则是编译链接,由于没有main   函数,编译时会报错。
执行完上面的命令后,用ls命令看看目标文件是不是已经存在。

Image

2、建立一个名mySort.h的库文件,此库文件包含三个排序函数原型;

Image

Image

3、用ar 工具将目标文件打包成 .a 静态库 ;

1
2
3
ar crv libsort.a bubblesort.o selectsort.o mergesort.o
**4、设计主程序并编译;**
g++ -o linux3T1 linux3T1.C -L. -lsort

Linux下使用静态库,只需要在编译的时候,指定静态库的搜索路径(-L选项)、指定静态库名(不需要lib前缀和.a后缀,-l选项)。
Image

结果展示:

Image

3.动态库编译:

linux 下动态库的命名规则为 lib+库名+.so

针对于实际库文件,每个共享库都有个特殊的名字“soname”。在程序启动后,程序通过这个名字来告诉动态加载器该载入哪个共享库。

生成动态链接库的命令:

(1)使用gcc 使用-c和 -fPIC参数生成目标文件

(2)生成动态库,此时需要加链接器选项-shared

1
g++ -shared -fPIC -o libsort.so bubblesort.o selectsort.o mergesort.o

接下来再写一个新的主程序linux3T1d来进行编译测试:
Image

然后报错了…

Image

Image

查了一下报错的原因:

对于自行编译的库,如opus,opencv等,其自带的可执行程序在使用时有时候会报cannot open shared object file: No such file or directory的错误,事实上,相关的依赖库已经安装或编译了,这是由于大部分的自编译库都是默认存放在/usr/local/lib中的,而Linux系统通常只会去/usr/lib中寻找库文件,这就导致无法加载库文件导致报错。

解决方案:

1
2
ln -s /your install path/xxx.so /usr/lib
sudo ldconfig

Image

现在可以正常运行了:

Image


2.编写程序对两个3*3的矩阵进行矩阵乘法、矩阵加法、矩阵减法、矩阵转置、矩阵求逆的操作,每种运算写成单独的函数文件和头文件,由主函数调用。要求编写makefile文件对程序进行编译。提交所有源码,实验报告和makefile文件。

T2

1.编写程序

编写程序对两个3*3的矩阵进行矩阵乘法、矩阵加法、矩阵减法、矩阵转置、矩阵求逆的操作,这里就不多说了,除了矩阵求逆稍微复杂点,其他几个就很基础,算是复习了c++了(话说java用久了有时候语法就串了,还花了我好一些时间)。

2.编写makefile文件

make工程管理器是一个“自动编译管理器”,这里的“自动”是指它能够根据文件时间戳自动发现更新过的文件而减少编译的工作量,同时,它通过读入makefile文件的内容来执行大量的编译工作。用户只需编写一次简单的编译语句就可以了。它大大的提高了实际的工作效率。

Makefile文件由一系列规则rules构成,每条规则形式如下:

1
2
<target>: <prerequisites>
[Tab]<commands>

看完ppt和各种博客以后,总结一下就是感觉makefile就像是一个有一定逻辑顺序的编译指令集。target是目标文件,prerequisites是先决条件,满足先决条件的情况下就执行commands。

执行make的过程中遇到不少错误,除去c++本身的语法错误,就是类似于下面的错误:

这种错误基本是makefile文件格式问题引起的,非常低级= = 。

Image

所以说写代码的时候一定要仔细。

Image

3.测试:

完成编码以后简单测试了一下两个矩阵的加法。

Image

测试结果:

Image


3.练习代码中对程序进行优化的代码,将实验结果,写入实验报告。

T3

查了下资料熟悉了下优化的概念。

gcc提供了从O0-O3以及Os这几种不同的优化级别供选择,在这些选项中,包含了大部分有效的编译优化选项,可以在这个基础上,对某些选项进行屏蔽或添加,降低使用的难度。

测试代码:

Image

-O0: 不做任何优化,这是默认的编译选项。

测试结果:

Image

-O和-O1: 对程序做部分编译优化。

对于大函数,优化编译占用稍微多的时间和相当大的内存。使用本项优化,编译器会尝试减小生成代码的尺寸,以及缩短执行时间,但并不执行需要占用大量编译时间的优化。

测试结果:

Image

-O2: 是比O1更高级的选项,进行更多的优化。

Gcc将执行几乎所有的不包含时间和空间折中的优化。当设置O2选项时,编译器并不进行循环打开()loop unrolling以及函数内联。与O1比较而言,O2优化增加了编译时间的基础上,提高了生成代码的执行效率。

测试结果:可以看出跟上面的比还是快了的,有人说O2是比较常用的优化选项。

Image

-O3: 比O2更进一步的进行优化。

测试结果:优化幅度已经不太大了。

Image


4.设计一个程序,在屏幕上随机显示一个个位数,并开始等待用户的键盘输入字符,计算从字符显示到用户输入所用的时间,多次重复此过程,最后输出用户的平均反映时间(输入错误的不计入)以及正确率。

要求:使用gdb调试,将调试的几种命令实验结果及截图写入实验报告。

产生随机数和计算用户反映时间的代码,请参考第四章的代码ex4-3.c和ex4-7.c。

T4

**1.**设计编辑源程序代码

2.用gcc编译程序

gdb进行调试的是可执行文件,而不是如“.c”这样的源代码文件,所以要先对编辑好的程序进行编译才行。

Image

1
g++ avgReactTime.C -o avgReactTime -g

3: 进入gdb调试环境

1
gdb avgReactTime

成功进入调试环境的话就会出现下面的内容:
Image

按照老师给的课件顺序把几个常用的功能都试了一遍。

(1)查看源文件

Image

(2)设置断点

Image

(3)查看断点信息

Image

(4)运行程序:可以输入“r”(run)开始运行程序

注意:gdb默认从第一行开始运行,如果要从程序中指定行开始运行,只需输入“r”+行号。

Image

(5)查看变量值

调试程序主要工作就是查看断点处的变量值,程序运行到断点处会自动暂停,此时输入“p”(print)+变量名。

Image

(6)单步运行

很多情况下,调试的时候要单步运行,在断点处输入 “n”(next)或者“s”(step) 。

Image

(7)继续运行程序

在查看完变量或堆栈情况后,可以如前单步运行,也可以输入“c”(continue)命令恢复程序的正常运行,把剩余的程序执行完,并显示执行结果。

(自己偶然间尝试发现遇到断点直接回车也等于按了“continue”会继续执行程序。)

Image

(8)退出gdb环境:只要输入“q”(quit)命令即可。

Image

最后我又直接执行测试了一下整个程序:

Image


二.实验总结

1.makefile:目标模式不含有xxx

Image

很low的错误,检查makefile文件格式就没错了。

三.实验感想

写一点碎碎念。

做实验的时候就感觉以前的操作系统一通理论是白学了。以前学理论的时候昏昏欲睡根本听不明白一个程序跑起来的流程是怎样的,太枯燥太抽象了。学linux自己去做的时候反而明白了些,理论和操作还是要结合起来才便于理解。

做第三章作业的时候明显比第二章舒服一点,因为了解了基本机理以后,对照ppt操作起来比之前熟练一些了,流畅多了。

建议老师提前布置作业,这样或许就可以在linux课上边听讲边写作业了,效率翻倍。🐮