课程笔记,随便记记
对应PPT 1-2章
INTRODUCTION
Linux简介
An Introduction to UNIX, Linux, and GNU
Unix操作系统最初是由贝尔实验室开发的,当时的贝尔实验室是电信业巨头—-AT&T(美国电报电话公司)旗下的一员。
UNIX是在20世纪70年代为DEC(数字设备公司)的PDP系列计算机开发的。
UNIX Philosophy
Simplicity(简单性)
Focus(集中性)
Reusable Components(可重用组件)
Filters(过滤器)
Open File Formats(开放的文件格式)
Flexibility(灵活性)
What Is Linux?
Linux是类UNIX内核(操作系统的低级内核)的免费分布式实现。
Linux是由赫尔辛基大学的Linus Torvalds在互联网上的UNIX程序员的帮助下开发的。
现在,使用许多不同类型的CPU的Linux版本可用于各种计算机系统。
发行版简介
分发通常在CD-ROM上,它不仅包含内核,还包含许多其他编程工具和实用程序。
发行版通常附带安装程序和附加文档,以帮助您安装自己的Linux系统。
一些著名的发行版(尤其是在Intelx86和奔腾系列处理器上)是Red Hat Linux、SuSE Linux和Debian GNU/Linux。
Linux编程
Linux系统可以使用大量的编程语言,其中许多语言都是免费的,可以从CD-ROM集合或Internet上的FTP存档站点获得。
下面是Linux程序员可用的编程语言的部分列表:
Ada、 C /C++、Eiffel 、 Forth 、 Fortran、Icon 、 Java JavaScript.
Linux程序
Linux应用程序由两种特殊类型的文件:可执行文件和脚本。
可执行文件是可由计算机直接运行的程序;
脚本是另一个程序(解释器)要遵循的指令集合。(脚本文件类似于Windows中的.bat文件、.cmd文件)
C程序
常用gcc选项
1 | -ansi 只支持ANSI标准的C语法。 |
开发系统路线图
应用
系统提供的用于一般用途的应用程序,包括程序开发,可以在
/usr/bin
中找到。系统管理员为特定主机或本地网络添加的应用程序通常位于
/usr/local/bin
或/opt
中。功能特性和编程系统可能有自己的目录结构和程序目录。
头文件
对于C,它们几乎总是位于
/usr/include
及其子目录中。其他编程系统也将包括存储在目录中的文件,这些文件由适当的编译器自动搜索。
通过向C编译器指定-I标志,我们可以在子目录或非标准位置使用include文件。例如,
$gcc-I/usr/openwin/include fred.c
库文件
库是为可重用而编写的预编译函数的集合。通常,它们由一组相关函数组成,以执行一个共同的任务。
标准系统库通常存储在
/lib
和/usr/lib
中。库文件名总是以lib开头。名称的最后一部分以点开头,并指定库的类型:
.a用于传统的静态库
.so用于共享库
静态库
当程序需要使用存储在库中的函数时,它包含一个声明该函数的头文件。
把程序和链接器结合成一个程序库和一个可执行程序。
静态库,也被称为archives(归档文件)的名称,通常以.a结尾。
我们可以很容易地用我们自己的程序库来创建和维护gcc的静态库。
共享库
- 共享库与静态库存储在相同的位置,但共享库具有不同的文件名后缀。
- 系统可以安排一个共享库的单个副本同时供多个应用程序使用,并只在磁盘上存储一次。
- 另一个好处是共享库可以独立于依赖它的应用程序进行更新。
- 通过运行ldd,我们可以看到程序需要哪些共享库。
Linux命令摘要
Starting & Stopping
1 | shutdown -h now 现在关闭系统,不要重新启动 |
Files and Directory
1 | cd /home 进入目录'/ home’ |
Finding files and text within files
1 | find / -name fname 从根目录开始,查找名为fname的文件 |
Moving, copying, deleting & viewing files
1 | ls -l 使用长格式列出当前目录中的文件 |
Installing software for Linux
1 | rpm -ihv name.rpm 安装名为name的rpm包 |
User Administration
1 | adduser accountname 创建一个名为accountname的新用户 |
文件权限
使用命令 ls -l
,会出现文件权限信息,例如:drwxr-xr-x
Read = 4 Write = 2 Execute = 1
通过为每个用户类型提供 chmod
命令和适当的八进制代码,可以更改文件许可权。 例如 chmod 7 6 4 filename
将使文件名为R + W + X的文件名为所有者,R + W为组,其他为R。
chmod 7 5 5
拥有者的完全权限,拥有对群组和其他人的读取和执行访问权限。
chmod +x filename
使名为filename的文件对所有用户可执行。
Process Management
1 | ps 显示您当前活动的进程 |
System Info
1 | date 显示当前日期和时间 E.G date 041217002007.00 |
Vi使用指南
Vi是一个流行的文本编辑器,保证在所有Linux和Unix发行版上都能使用,它非常适合从命令行快速编辑配置文件。
有两种方法可以启动Vi:
vi—-打开未命名的空白文件。
vi filename—-开始编辑空白命名文档或打开文件进行编辑。
Vi有3种模式:
- 命令模式
- 插入模式
- 末行模式
Vi以命令模式开始。
- 在命令模式下按
i
进入插入模式。 - 按
Esc
返回命令模式 - 按
:
从命令模式到末行模式
Insert/Replace mode
像在任何文本编辑器中一样输入。
Command mode/Last-line mode
- 保存
1 | :w 将已命名的文件写入磁盘 |
- 退出
1 | :q 退出Vi,但仅当文档未被修改时 |
- 打开
1 | :e filename 打开文件进行编辑,或开始新的命名文档 |
- 移动
1 | [PageUp] 上移1页。在前面加上一个数字可以向上移动那么多页 |
- 撤销和重做
1 | u 撤消 |
- 删除(剪切)
1 | dd 删除(剪切)当前行 |
- 拖拉(复制)并粘贴
1 | yy or Y 拖拉(复制)当前行 |
- 搜索
1 | /text 从光标中查找下一个文本实例,其中text是要查找的字符串。如果找不到任何内容,那么Vi将从文档的开始处折回光标 |
- 替换
1 | :s/old/new 用new替换old的下一个实例 |
Debugging and Make
错误类型
功能定义错误:如果程序的功能被错误地定义了,它就肯定不能完成预定的工作。所以,在开始设计之前,你必须确认自己知道并理解这个程序究竟是用来干什么的。
设计规划错误:无论程序的规模大小,在创建它们之前都需要有一个设计规划的阶段。对程序员来说,一定要花点时间思考:如何构造程序,需要什么样的数据结构,它又应该如何在程序中使用。
代码编写错误:当然,每个人都会出现键入错误。在程序中遇到错误时,要重新阅读源代码或与其他人进行讨论。
可以试着在纸上执行程序的核心代码,这个过程被称为干运行。针对那些重要的例程,先记下它们的输入值,然后逐步手工计算出结果。
一般调试技术
一般方法是先运行程序并观察其输出结果,如果不能正常工作,我们就需要决定应该采取哪些措施。
(1)可以修改程序然后重新尝试(代码检查-试运行-出错法)
(2)在程序中增加一些语句以获得更多关于程序内部运行情况的信息(取样法)
(3)可以直接检查程序的运行情况(受控执行法)
调试的5个阶段
Testing(测试): 找出存在哪些缺陷或错误
Stabilization(固化): 使错误可重现
Localization(定位): 标识负责的代码行
Correction(纠正): 修复代码
Verification(验证): 确保修复程序有效
取样法
检测是向程序中添加代码,目的是收集有关程序运行时行为的更多信息。
添加 printf
调用以在程序执行的不同阶段打印出变量的值是很常见的。
但是我们应该知道,只要更改程序,该过程就需要进行额外的编辑和编译,当然,在修复错误之后,我们将需要删除代码。
第一种技巧是用C语言的预处理器有选择的包括取样代码,这样只需重新编译程序就可以达到包含或去除调试代码的目的。
我们可以使用编译器标志 -DDEBUG
来编译程序,以定义DEBUG符号并包括多余的代码,也可以不排除它。
C语言预处理器定义的一些宏可以帮助我们进行调试。这些宏在扩展后会提供当前编译操作的相关信息。
Macro | Description |
---|---|
__LINE__ | A decimal representing the current line number |
__FILE__ | A string representing the current file name |
__DATE__ | A string of the form “Mmm dd yyyy”,the current date |
__TIME__ | A string of the form”hh:mm:ss”,the current time |
使用GDB调试
gdb本身是一个基于文本的应用程序,但是它确实提供了一些捷径来帮助执行重复性任务。
许多版本具有历史记录的命令行编辑,因此您可以向后滚动并再次执行相同的命令(尝试使用光标键)。
所有版本都支持“空命令”。 按Enter键将再次执行最后一个命令。 当使用step或next命令逐行浏览程序时,此功能特别有用。
多个源文件的问题
在编写小型程序时,许多人只需在编辑后通过重新编译所有文件即可重建其应用程序。
但是,对于较大的程序,这种简单方法的一些问题变得很明显。 编辑-编译-测试周期的时间将增加。
当只更改了一个文件时,即使是最有耐心的程序员也希望避免重新编译所有文件。
当创建多个头文件并将其包含在不同的源文件中时,可能会出现更困难的问题。
make命令和Makefiles
您必须提供一个文件,告诉您如何构造您的应用程序。 该文件称为makefile。
生成文件最常与项目的其他源文件位于同一目录中。
如果您有一个大型项目,则可以选择对项目的不同部分使用单独的Makefile对其进行管理。
make命令和makefile的组合为管理项目提供了非常强大的工具。
The Syntax of Makefiles
一个makefile由一组依赖关系和规则组成。
- 依赖项具有目标(要创建的文件)和依赖项依赖的一组源文件。
- 规则描述了如何从相关文件中创建目标。
- 通常,目标是单个可执行文件。
make文件由make命令读取,该命令确定要创建的目标文件,然后比较源文件的日期和时间,以决定需要调用哪些规则来构造目标。
通常,必须先创建其他中间目标,然后才能制定最终目标。
make命令使用makefile来确定目标的生成顺序以及要调用的规则的正确顺序。
Options and Parameters to make
make程序本身有几个选项。 三种最常用的是
1 | -k 它告诉make在发现错误时继续进行。 |
要告诉make建立一个特定的目标,您可以传递目标名称作为参数。 如果不这样做,make会尝试创建makefile中列出的第一个目标。
许多程序员将所有目标都指定为其makefile中的第一个目标,然后将其他目标列为所有目标的依赖项。
当没有指定目标时,此约定使makefile在默认情况下应尝试构建的目标明确。
- Dependencies(依赖关系)
依赖关系指定最终应用程序中的每个文件与源文件的关系。
在makefile中,我们通过编写目标名称,冒号,空格或制表符,然后用空格或制表符分隔的用于创建目标文件的文件列表来编写这些规则。
我们的示例的依赖项列表是
1 | myapp: main.o 2.o 3.o |
这表示myapp依赖于main.o,2.o和3.o,main.o依赖于main.c和a.h,依此类推。
这组依赖关系提供了一个层次结构,显示了源文件之间的关系。
如果我们希望制作多个文件,则可以全部使用phony目标。 假设我们的应用程序由二进制文件myapp和手册页myapp.1组成。
我们可以用以下行来指定
1 | all: myapp myapp.l |
- Rules(规则)
makefile的第二部分指定描述如何创建目标的规则。
在上一节的示例中,在make命令确定2.o需要重建之后,应使用哪个命令?
如果我们需要指定一个include目录或设置符号信息选项以进行以后的调试该怎么办?
我们可以通过在makefile中指定显式规则来做到这一点。
大多数规则由一个简单的命令组成,该命令可以在命令行中键入。 对于我们的示例,我们将创建第一个makefile,
Makefile1:
1 | myapp: main.o 2.o 3.o |
我们使用-f选项调用make命令,因为我们的makefile没有通常的默认名称makefile或Makefile。
如果我们在不包含源代码的目录中调用此代码,则会收错误消息。
Comments in a Makefile
生成文件中的注释以#开头,并继续到该行的末尾。
像在C源文件中一样,makefile中的注释可以帮助作者和其他人了解编写文件时的意图。
Macros(宏) in a Makefile
但是,对于包含大量文件的项目,它们也往往很大且不灵活。
我们通过写入MACRONAME = value来在makefile中定义一个宏,然后通过写入 $(MACRONAME) 或 $ {MACRONAME}来访问MACRONAME的值。
通过将行的其余部分保留在=空白之后,可以将宏的值设置为blank(空)。
1 | all: myapp |
实际上,make具有几个特殊的内部宏,您可以使用这些宏使makefile更加简洁。
这些宏中的每一个仅在使用前就展开,因此宏的含义可能会随着Makefile的进行而变化。
1 | $^ List of prerequisites of the current |
Multiple Targets
通常,制作多个目标文件或将几组命令收集到一个位置通常很有用。 我们可以扩展我们的makefile文件来做到这一点。
让我们添加一个“清除”选项以删除不需要的对象,并添加一个“安装”选项以将完成的应用程序移动到另一个目录。
1 | all: myapp |
1 | clean: |
首先,当我们执行make而不指定目标时,默认行为是构建目标myapp。
下一个要点与清洁和安装这两个附加目标有关。 该命令以-开头,它告诉make忽略命令的结果。
安装目标取决于myapp,因此要知道在执行其他安装命令之前,它必须先创建myapp。
该命令以@符号开头,告诉make在执行规则之前不要在标准输出上打印命令。
安装目标一个接一个地执行几个命令,以将应用程序安装在其最终的存放位置。 在执行下一个命令之前,它不会检查每个命令是否成功。
如果仅在前一个命令成功后才执行后续命令非常重要,那么我们可以编写由&&连接的命令,如下所示:
1 | @if [ -d $(INSTDIR) ]; \ |
Suffix and Pattern Rules
我们看到内置规则在使用时都有后缀,所以给出带有某个特定后缀的文件时,make命令知道应该用哪个规则来创建带有另一个不同后缀名的文件。
- 最常见的一条规则是,通过一个以.c为后缀名的文件创建出一个以.o为后缀名的文件。
要添加新的后缀规则,我们首先在makefile中添加一行,以告知make有关新后缀的信息; 然后,我们可以使用该新后缀编写一条规则。
nmake uses the special syntax
.<old_suffix>.<new_suffix>:
定义用于从具有相同基本名称但具有旧后缀的文件中创建具有新后缀的文件的一般规则。
这是我们制作文件的一部分,其中包含将.cpp文件转换为.o文件的新通用规则:
1 | .SUFFIXES: .cpp |
make的最新版本包括实现相同效果的替代语法,以及更多其他功能。
1 | %.cpp: %o |
Managing Libraries with make
在处理较大的项目时,通常可以使用库来管理多个编译产品。
语法为lib(file.o),这表示目标文件file.o,存储在库lib.a中。
make命令具有一个用于管理库的内置规则,该规则通常等效于以下内容:
1 | .c.a: |
对于大型项目,一种比较方便的做法是用函数库来管理多个编译产品。
$*不包括后缀名的当前依赖文件的名字
宏 \$(AR)
和 \$(ARFLAGS)
通常分别默认为命令ar和选项rv。
简洁的语法告诉我们,要从.c文件到.a库,它必须应用两个规则:
- 第一条规则说,它必须编译源文件并生成一个对象。
- 第二条规则说使用ar命令修改库,添加新的目标文件。
Managing a Library
更改应用程序,使文件2.o和3.o保存在名为mylib.a的库中
1 | all: myapp |
Makefiles and Subdirectories
对于大型的项目,有时我们希望能把组成一个函数库的几个文件从主文件中分离出来,并将它们保存到一个子目录中。
首先在子目录中编写出第二个makefile文件,它的作用是编译该子目录下的源文件并将它们保存到一个函数库中,然后将该库文件拷贝到上一级的主目录中。
在主目录中的makefile文件包含一个用于制作函数库的规则,该规则会调用第二个makfile文件。
Make命令调用这条规则来创建函数库时,它将切换到子目录mylibdirectory中,然后调用一个新的make命令来管理函数库。由于make会针对每个命令调用一个新的shell,使用第二个makefile文件的make命令本身又没有执行cd命令,但它又必须在一个不同的目录下创建函数库,为解决这一问题,我们用括号将这两个命令括起来,以使它们只被一个单独的shell处理
1 | mylib.a: |