




已阅读5页,还剩83页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Linux开发基础 原仓周 第二章Linux程序设计基础 C环境 Linux编程概述文本编辑器viLinux下函数库GCC及其使用调试工具gdbGNUmake和makefile Linux编程概述 Linux软件开发一直在Internet环境下讲行 这个环境是全球性的 编程人员来自世界各地 只要能够访问Web站点 就可以启动一个以Linux为基础的软件项目 Linux开发工作经常是在Linux用户决定共同完成一个项目时开始的 当开发工作完成后 该软件就被放到Internet站点上 任何用户都可以访问和下载它 由于这个活跃的开发环境 新的以Linux为基础的软件功能日益强大 而且呈现爆炸式的增长态势 大多数Linux软件是经过自由软件基金会 FreeSoftwareFoundation 提供的GNU GNU即GNU snotUNIX 公开认证授权的 因而通常被称作GNU软件 GNU软件免费提供给用户使用 并被证明是非常可靠和高效的 许多流行的Linux实用程序如C编译器 shell和编辑器都是GNU软件应用程序 Linux程序需要首先转化为低级机器语言即所谓的二进制代码以后 才能被操作系统执行 例如编程时 先用普通的编程语言生成一系列指令 这些指令可被翻译为适当的可执行应用程序的二进制代码 这个翻译过程可由解释器一步步来完成 或者也可以立即由编译器明确地完成 shell编程语言如BASH TCSH GAWK Perl Tcl和Tk都利用自己的解释器 用这些语言编制的程序尽管是应用程序文件 但可以直接运行 编译器则不同 它将生成一个独立的二进制代码文件然后才可以运行 Linux编程风格 GNU风格Linux内核编程风格 GNU风格 1 2 函数返回类型说明和函数名分两行放置 函数起始字符和函数开头左花括号放到最左边 尽量不要让两个不同优先级的操作符出现在相同的对齐方式中 应该附加额外的括号使得代码缩进可以表示出嵌套 按照如下方式排版do while语句 do while 每个程序都应该以一段简短的说明其功能的注释开头 请为每个函数书写注释 说明函数是做什么的 需要哪些入口参数 参数可能值的含义和用途 如果用了非常见的 非标准的东西 或者可能导致函数不能工作的任何可能的值 应该进行特殊说明 如果存在重要的返回值 也需要说明 不要声明多个变量时跨行 每一行都以一个新的声明开头 GNU风格 2 2 当一个if中嵌套了另一个if else时 应用花括号把if else括起来 要在同一个声明中同时说明结构标识和变量或者结构标识和类型定义 typedef 先定义变量 再使用 尽量避免在if的条件中进行赋值 请在名字中使用下划线以分割单词 尽量使用小写 把大写字母留给宏和枚举常量 以及根据统一惯例使用的前缀 例如 应该使用类似ignore space change flag的名字 不要使用类似iCantReadThis的名字 用于表明一个命令行选项是否给出的变量应该在选项含义的说明之后 而不是选项字符之后被命名 Linux内核编程风格 Linux内核缩进风格是8个字符 Linux内核风格采用K R标准 将开始的大括号放在一行的最后 而将结束的大括号放在一行的第一位 命名尽量简洁 不应该使用诸如ThisVariableIsATemporaryCounter之类的名字 应该命名为tmp 这样容易书写 也不难理解 命名全局变量 应该用描述性命名方式 例如应该命名 count active users 而不是 cntusr 本地变量应该避免过长 函数最好短小精悍 一般来说不要让函数的参数多于10个 否则应该尝试分解这个过于复杂的函数 通常情况 注释说明代码的功能 而不是其实现原理 避免把注释插到函数体内 而写到函数前面 说明其功能 如果这个函数的确很复杂 其中需要有部分注释 可以写些简短的注释来说明那些重要的部分 但是不能过多 文本编辑器vi vi的模式vi的进入命令模式插入模式末行模式 vi的模式 CommandMode 命令模式 这是执行vi后的缺省模式此时键盘输入当作命令命令有大小写之区分InputMode 插入模式 使用a i o c r s进入插入模式用户输入的任何字符都被vi当做文件内容保存起来 并将其显示在屏幕上按下ESC键即可回到CommandMode vi的模式 LastMode 末行模式 在CommandLine按下 即可进入该模式用来进行保存文件 打开文档或环境的设定命令有大小写之分 vi的进入和内容输入 进入 vi文件名输入文件内容 进入插入模式 新增 append a从光标所在位置后面开始新增内容A从光标所在行最后面的地方开始新增内容 插入 insert i从光标所在位置前面开始插入内容I从光标所在行的第一个非空白字符前面开始插入资料 开始 open o在光标所在行下新增一行并进入输入模式 O在光标所在行上新增一行并进入输入模式 命令模式 命令模式 1 光标的移动h左移一个字符l右移一个字符j下移一行k上移一行w W跳至后一个字的开头 W忽略标点 b B跳至前一个字的开头 B忽略标点 e移动到后一个字的末尾 至本行第一个非空字符 至行尾0至行首H移动到当前窗口的第一列M移动到当前窗口的中间列L移动到视窗的最后一列 光标所在位置到下个句子的第一个字母 光标所在位置到该句子的第一个字母 光标所在位置到该段落的最后一个字母 光标所在位置到该段落的第一个字母 命令模式 1 光标的移动 续 nH将光标移到屏幕的第n行nL将光标移到屏幕的倒数第n行CTRL d向下半页CTRL f向下一页CTRL u向上半页CTRL b向上一页n 减号移动到上一行的第一个非空白字符 前面加上数字可以指定移动到以上n行n 加号移动到下一行的第一个非空白字符 前面加上数字可以指定移动到以下n行 命令模式 2 删除x删除光标所在字符X删除光标前面的字符s删除光标所在字符 并进入输入模式S删除光标所在的行 并进入输入模式dd删除光标所在的行D从光标位置开始删除到行尾d与光标移动命令的组合 命令模式 3 修改r修改光标所在字符 r后接着要修改的字符 如 rc可以用字符 c 替换光标所指向的当前字符R进入替换状态 新增内容会覆盖原先内容 直到按 ESC 回到命令模式下为止cc修改光标所在行C修改从光标位置到该行末尾的内容c与光标移动命令的组合 命令模式 4 复制和移动yy复制当前行到内存缓冲区nyy复制n行内容到内存缓冲区y与光标移动的组合p将缓冲区的内容粘贴到光标的后面P将缓冲区的内容粘贴到光标的前面另 在末行模式下实现移动 n1 n2mn3 把n1到n2行内容搬到第n3行后 5 搜索字符串 pattern移至下一个包含pattern的行 pattern移至上一个包含pattern的行 往下重复查找 往上重复查找n在同一方向重复查找N在相反方向重复查找 pattern n移至下一个pattern所在行后的第n行 pattern n移至上一个Pattern所在行前的第n行6 其他u撤销前一条命令的结果 包含在查找字符串中 要用转义字符 命令模式 末行模式 1 文件的保存和退出 w保存 q退出 w 强制保存 q 强制退出 wq保存退出 wq 强制保存退出 末行模式 2 字符串的替换 s str1 str2 用字符串str2替换行中首次出现的字符串str1 s str1 str2 g用字符串str2替换行中所有出现的字符串str1 s str1 str2 g用字符串str2替换正文当前行到末尾所有出现的字符串str1 1 s str1 str2 g用字符串str2替换正文中所有出现的字符串str1 g str1 s str2 g功能同上 末行模式 其他 n将光标移到第n行编辑多个文件vifile1file2 n编辑下一个文件 efilename编辑指定文件 Linux下函数库 1 3 一个 程序函数库 就是一个文件包含了一些编译好的代码和数据 这些编译好的代码和数据可以在事后供其他的程序使用 程序函数库可以使整个程序更加模块化 更容易重新编译 而且更方便升级 可分为两种类型 静态函数库 staticlibraries 是一个普通的目标文件的集合 一般用 a 作为文件的后缀 静态函数库和共享函数库相比有很多的缺点 占用内存空间多 但使用ELF格式的静态库函数生成的代码可以比使用共享函数库的程序运行速度上快一些 可以用ar这个程序来创建一个静态函数库文件 或者往一个已经存在地静态函数库文件添加新的目标代码 例如 把file1 o和file2 o加入到my library a这个函数库文件 arrcsmy library afile1 ofile2 o然后运行ranlib 以给库加入一些索引信息 Linux下函数库 2 3 共享函数库 sharedlibraries 当一个可执行程序在启动的时候被加载的函数 每个共享函数库都有个特殊的名字 称作 soname soname名字命名必须以 lib 作为前缀 然后是函数库的名字 然后是 so 最后是版本号信息 优点 多进程使用同一函数库 修改函数库不需重新连编 安装一个新版本的函数库的时候 要先将这些函数库文件拷贝到一些特定的目录中 运行ldconfig就可以 ldconfig检查已经存在的库文件 然后创建soname的符号链接到真正的函数库 同时设置 etc ld so cache这个缓冲文件 例如 创建两个目标文件 a o和b o 然后创建一个包含a o和b o的共享函数库 gcc fPIC g c Walla cgcc fPIC g c Wallb cgcc shared Wl soname liblusterstuff so 1 oliblusterstuff so 1 0 1a ob o lc注 fPIC 是位置无关参数 g 和 Wall 参数不是必须的 Linux下函数库 3 3 函数库和头文件的保存位置a 函数库 lib 系统必备共享函数库 usr lib 标准共享函数库和静态函数库 usr i486 linux libc5 lib libc5兼容性函数库 usr X11R6 lib X11R6的函数库 usr local lib 本地函数库b 头文件 usr include 系统头文件 usr local include 本地头文件c 共享函数库的相关配置和命令 etc ld so conf 包含共享库的搜索位置ldconfig 共享库管理工具 一般在更新了共享库之后要运行该命令ldd 可查看可执行文件所使用的共享函数库 使用GNUcc开发应用程序 gcc的简介可执行文件的格式gcc的使用 gcc的简介 gcc是GNU的C和C 编译器 实际上 gcc能够编译多种语言 C C 和ObjectC等 利用gcc命令可同时编译并连接C和C 源程序 也可以对几个C源文件利用gcc编译 连接并生成可执行文件 gcc可以使程序员灵活地控制编译过程 编译过程一般可以分为下面四个阶段 每个阶段分别调用不同的工具进行处理 gcc的四个阶段 命令gcc首先调用cpp进行预处理 在预处理过程中 对源代码文件中的文件包含 include 预编译语句 如宏定义define等 进行分析 接着调用cc1进行编译 这个阶段根据输入文件生成以 o为后缀的目标文件 汇编过程是针对汇编语言的步骤 调用as进行工作 一般来讲 S或 s为后缀的汇编语言源代码文件汇编之后都生成以 o为后缀的目标文件 当所有的目标文件都生成之后 gcc就调用ld来完成最后的关键性工作 这个阶段就是连接 在连接阶段 所有的目标文件被安排在可执行程序中的恰当的位置 同时 该程序所调用到的库函数也从各自所在的函数库中连到合适的地方 可执行文件格式 Linux系统中可执行文件有两种格式 第一种格式是a out格式 这种格式用于早期的Linux系统以及Unix系统的原始格式 a out来自于UnixC编译程序默认的可执行文件名 当使用共享库时 a out格式就会发生问题 把a out格式调整为共享库是一种非常复杂的操作 因此 一种新的文件格式被引入Unix系统5的第四版本和Solaris系统中 它被称为可执行和连接的格式 ELF 这种格式很容易实现共享库 ELF格式已经被Linux系统作为标准的格式采用 gcc编译程序产生的所有的二进制文件都是ELF格式的文件 即使可执行文件的默认名仍然是a out 较旧的a out格式的程序仍然可以运行在支持ELF格式的系统上 GNUC的使用 基本语法gcc options filenames 说明 在gcc后面可以有多个编译选项 同时进行多个编译操作 很多的gcc选项包括一个以上的字符 因此你必须为每个选项指定各自的连字符 例如 下面的两个命令是不同的 gcc p gtest1 cgcc pgtest1 c当你不用任何选项编译一个程序时 GCC将会建立 假定编译成功 一个名为a out的可执行文件 gcc选项 o选项你能用 o编译选项来为将产生的可执行文件指定一个文件名来代替a out 例 gcc ocountcount c c选项 告诉GCC仅把源代码编译为目标代码而跳过汇编和连接的步骤 这个选项使用的非常频繁 因为它使得编译多个C程序时速度更快并且更易于管理 缺省时GCC建立的目标代码文件有一个 o的扩展名 例 gcc ctest2 c E只运行C预编译器 S编译选项告诉gcc在为C代码产生了汇编语言文件后停止编译 shared生成共享目标文件 通常用在建立共享库时 static禁止使用共享连接 警告选项 在gcc中用开关 Wall控制警告信息 使用示例命令如下 gcc Wall otest3 1test3 1 c w不生成任何警告信息 查找选项 gcc一般使用默认路径查找头文件和库文件 如果文件所用的头文件或库文件不在缺省目录下 则编译时要指定它们的查找路径 I选项 指定头文件的搜索目录例 gcc I export home st otest1test1 c L选项 指定库文件的搜索目录例 gcc L usr X11 R6 lib otest1test1 c 多个源文件生成一个可执行文件 问题 有多个源文件时 如何生成一个可执行文件 方法1 gcc Wall omytesttest1 ctest2 ctest3 c方法2 gcc Wall ctest1 cgcc Wall ctest2 cgcc Wall ctest3 cgcc omytesttest1 otest2 otest3 o 优化选项 优化选项可以使GCC在耗费更多编译时间和牺牲易调试性的基础上产生更小更快的可执行文件 这些选项中最典型的是 O和 O2选项 O0不进行优化处理 O选项 告诉GCC对源代码进行基本优化 这些优化在大多数情况下都会使程序执行的更快 O2选项 告诉GCC产生尽可能小和尽可能快的代码 O2选项将使编译的速度比使用 O时慢 但通常产生的代码执行速度会更快 O3选项 比 O2更进一步优化 包括inline函数 版本选项 v选项用户将会得到自己目前正在使用的gcc的版本及与版本相关的一些信息 gcc v将得到如下结果 Readingspecsfrom usr lib gcc lib i486 box linux 2 7 2 specsgccversion2 7 2 V选项如果安装了多个版本的gcc 并且想强制执行其中的某个版本 可以用命令通知系统用户要使用的版本 gcc V2 6 3 v 宏定义选项 DMACRO以字符串 1 定义MACRO宏 DMACRO DEFN以字符串 DEFN 定义MACRO宏 UMACRO取消对MACRO宏的定义 调试和剖析选项 使用调试选项后 gcc在进行编译的时候 在目标文件 o 和创建的可执行文件中插入额外信息 这些额外信息使gdb能够判断编译过的代码和源代码之间的关系 g选项 告诉GCC产生能被GNU调试器使用的调试信息以便调试你的程序 例 gcc g otest3test3 c pg选项 告诉GCC在你的程序里加入额外的代码 执行时 产生gprof用的剖析信息以显示你的程序的耗时情况 使用gdb调试工具 命令行如下 例 gcc ggdb3 otest3test3 c 调试工具gdb GDB调试器简介gdb的常用命令gdb应用实例 gdb简介 Linux系统中包含了GNU调试程序gdb 它是一个用来调试C和C 程序的调试器 可以使程序开发者在程序运行时观察程序的内部结构和内存的使用情况 gdb所提供的一些功能如下所示 运行程序 设置所有的能影响程序运行的参数和环境 控制程序在指定的条件下停止运行 当程序停止时 可以检查程序的状态 修改程序的错误 并重新运行程序 动态监视程序中变量的值 可以单步逐行执行代码 观察程序的运行状态 分析崩溃程序的产生的core文件 gdb的特点 gdb的功能非常强大到目前为止 gdb已能够支持Moduls 2 Chill Pascal和FORTRAN程序的调试 但是调试这些语言的源程序时有一些功能还不能使用 例如调试FORTRAN程序时还不支持表达式的输入 输出变量或类FORTRAN的词法 gdb程序调试的对象是可执行文件 而不是程序的源代码文件 然而 并不是所有的可执行文件都可以用gdb调试 如果要让产生的可执行文件可以用来调试 需在执行gcc指令编译程序时 加上 g参数 指定程序在编译时包含调试信息 调试信息包含程序里的每个变量的类型和在可执行文件里的地址映射以及源代码的行号 gdb利用这些信息使源代码和机器码相关联 gdb是一个用来调试C和C 程序的常用调试工具之一 gdb的启动 在命令行上输入gdb并按回车键就可以运行gdb了 如果一切正常的话 将启动gdbgdb filename 出现 gdb 在这里 可以输入调试命令在可以使用gdb调试程序之前 必须使用 g选项编译源文件 可在makefile中如下定义CFLAGS变量 CFLAGS g运行 获取帮助信息 启动gdb后 可以在命令行上指定很多的选项 输入 help可以获得gdb的帮助信息 如果想要了解某个具体命令 比如break 的帮助信息 在gdb提示符下输入下面的命令 break屏幕上会显示关于break的帮助信息 从返回的信息可知 break是用于设置断点的命令 另一个获得gdb帮助的方法是浏览gdb的手册页 在LinuxShell提示符输入 mangdb可以看到man的手册页 gdb命令的分类 在gdb提示符处键入help 将列出命令的分类 主要的分类有 aliases 命令别名breakpoints 断点定义 data 数据查看 files 指定并查看文件 internals 维护命令 running 程序执行 stack 调用栈查看 statu 状态查看 tracepoints 跟踪程序执行 后跟命令的分类名 可获得该类命令的详细清单 基本gdb命令 1 2 file命令 装入想要调试的可执行文件 cd命令 改变工作目录 pwd命令 返回当前工作目录 run命令 执行当前被调试的程序 kill命令 停止正在调试的应用程序 list命令 列出正在调试的应用程序的源代码 break命令 设置断点 watch命令 设置监视点 监视表达式的变化 awatch命令 设置读写监视点 当要监视的表达式被读或写时将应用程序挂起 它的语法与watch命令相同 rwatch命令 设置读监视点 当监视表达式被读时将程序挂起 等侍调试 此命令的语法与watch相同 next命令 执行下一条源代码 但是不进入函数内部 也就是说 将一条函数调用作为一条语句执行 执行这个命令的前提是已经run 开始了代码的执行 基本gdb命令 2 2 step命令 执行下一条源代码 进入函数内部 如果调用了某个函数 会跳到函数所在的代码中等候一步步执行 执行这个命令的前提是已经用run开始执行代码 display命令 在应用程序每次停止运行时显示表达式的值 infobreak命令 显示当前断点列表 包括每个断点到达的次数16 infofiles命令 显示调试文件的信息 17 infofunc命令 显示所有的函数名 18 infolocal命令 显示当前函数的所有局部变量的信息 19 infoprog命令 显示调试程序的执行状态 20 print命令 显示表达式的值 21 delete命令 删除断点 指定一个断点号码 则删除指定断点 不指定参数则删除所有的断点 22 Shell命令 执行LinuxShell命令 23 make命令 不退出gdb而重新编译生成可执行文件 24 Quit命令 退出gdb gdb使用实例 1 2 一个有错误的C源程序 include includestaticcharbuff 256 staticchar string intmain printf Pleaseinputastring gets string printf nYourstringis s n string 上面这个程序非常简单 其目的是接受用户的输入 然后将用户的输入打印出来 该程序使用了一个未经过初始化的字符串地址string 因此 编译并运行之后 将出现SegmentFault错误 gcc otest gtest c testPleaseinputastring asfdSegmentationfault coredumped gdb使用实例 2 2 为了查找该程序中出现的问题 我们利用gdb 并按如下的步骤进行 1 运行gdbbugging命令 装入bugging可执行文件 2 执行装入的bugging命令 3 使用where命令查看程序出错的地方 4 利用list命令查看调用gets函数附近的代码 5 唯一能够导致gets函数出错的因素就是变量string 用print命令查看string的值 6 在gdb中 我们可以直接修改变量的值 只要将string取一个合法的指针值就可以了 为此 我们在第11行处设置断点 7 程序重新运行到第11行处停止 这时 我们可以用setvariable命令修改string的取值 8 然后继续运行 将看到正确的程序运行结果 GNUmake和makefile GNUmake概述Makefile的基本结构Makefile中的变量GNUmake的主要预定义变量Makefile的隐含规则make命令行选项使用automake和autoconf产生Makefile GNUmake概述 在大型的开发项目中 人们通常利用make工具来自动完成编译工作 这些工作包括 如果仅修改了某几个源文件 则只重新编译这几个源文件 如果某个头文件被修改了 则重新编译所有包含该头文件的源文件 利用这种自动编译可大大简化开发工作 避免不必要的重新编译 实际上 make工具通过一个称为makefile的文件来完成并自动维护编译工作 makefile需要按照某种语法进行编写 其中说明了如何编译各个源文件并连接生成可执行文件 并定义了源文件之间的依赖关系 当修改了其中某个源文件时 如果其他源文件依赖于该文件 则也要重新编译所有依赖该文件的源文件 默认情况下 GNUmake工具在当前工作目录按如下顺序搜索makefile GNUmakefilemakefileMakefile makefile举例 在UNIX中 习惯使用makefile作为makfile文件 Linux程序员使用第三种文件名Makefile 因为第一个字母是大写 通常被列在一个目录的文件列表的最前面 如果要使用其他文件作为makefile 则可利用类似下面的make命令选项指定makefile文件 make fMakefile debug例1 一个简单的makefileprog prog1 oprog2 ogccprog1 oprog2 o oprogprog1 o prog1 clib hgcc c I oprog1 oprog1 cprog2 o prog2 cgcc cprog2 c Makefile的基本结构 1 2 Makefile是一个文本形式的数据库文件 其中包含一些规则来告诉make处理哪些文件以及如何处理这些文件 规则主要是描述哪些文件 称为target目标文件 不要和编译时产生的目标文件相混淆 是从哪些别的文件 称为dependency依赖文件 中产生的 以及用什么命令 command 来执行这个过程 依靠这些信息 make会对磁盘上的文件进行检查 如果目标文件的生成或被改动时的时间 称为该文件时间戳 至少比它的一个依赖文件还旧的话 make就执行相应的命令 以更新目标文件 目标文件不一定是最后的可执行文件 可以是任何一个中间文件并可以作为其他目标文件的依赖文件 Makefile的基本结构 2 2 Makefile规则的一般形式如下 target dependencydependency tab 一个Makefile文件主要含有一系列的规则 每条规则包含以下内容 一个目标 target 即make最终需要创建的文件 如可执行文件和目标文件 目标也可以是要执行的动作 如 clean 一个或多个依赖文件 dependency 列表 通常是编译目标文件所需要的其他文件 一系列命今 command 是make执行的动作 通常是把指定的相关文件编译成目标文件的编译命令 每个命令占一行 且每个命令行的起始字符必须为TAB字符 除非特别指定 否则make的工作目录就是当前目录 target是需要创建的二进制文件或目标文件 dependency是在创建target时需要用到的一个或多个文件的列表 命令序列是创建target文件所需要执行的步骤 比如编译命令 Makefile实例 1 3 以 开头的为注释行test prog ocode ogcc otestprog ocode oprog o prog cprog hcode hgcc cprog c oprog ocode o code ccode hgcc ccode c ocode oclean rm f o上面的Makefile文件中共定义了四个目标 test prog o code o和clean 目标从每行的最左边开始写 后面跟一个冒号 如果有与这个目标有依赖性的其他目标或文件 把它们列在冒号后面 并以空格隔开 然后另起一行开始写实现这个目标的一组命令 在Makefile中 可使用续行号 将一个单独的命令行延续成几行 但要注意在续行号 后面不能跟任何字符 包括空格和键 Makefile实例 2 3 一般情况下 调用make命令可输入 maketargettarget是Makefile文件中定义的目标之一 如果省略target make就将生成Makefile文件中定义的第一个目标 对于上面Makefile的例子 单独的一个 make 命令等价于 maketest因为test是Makefile文件中定义的第一个目标 make首先将其读入 然后从第一行开始执行 把第一个目标test作为它的最终目标 所有后面的目标的更新都会影响到test的更新 第一条规则说明只要文件test的时间戳比文件prog o或code o中的任何一个旧 下一行的编译命令将会被执行 Makefile实例 3 3 但是 在检查文件prog o和code o的时间戳之前 make会在下面的行中寻找以prog o和code o为目标的规则 在第三行中找到了关于prog o的规则 该文件的依赖文件是prog c prog h和code h 同样 make会在后面的规则行中继续查找这些依赖文件的规则 如果找不到 则开始检查这些依赖文件的时间戳 如果这些文件中任何一个的时间戳比prog o的新 make将执行 gcc cprog c oprog o 命令 更新prog o文件 以同样的方法 接下来对文件code o做类似的检查 依赖文件是code c和code h 当make执行完所有这些套嵌的规则后 make将处理最顶层的test规则 如果关于prog o和code o的两个规则中的任何一个被执行 至少其中一个 o目标文件就会比test新 那么就要执行test规则中的命令 因此make去执行gcc命令将prog o和code o连接成目标文件test 在上面Makefile的例子中 还定义了一个目标clean 它是Makefile中常用的一种专用目标 即删除所有的目标模块 make的工作过程 现在来看一下make做的工作 首先make按顺序读取makefile中的规则 然后检查该规则中的依赖文件与目标文件的时间戳哪个更新如果目标文件的时问戳比依赖文件还早 就按规则中定义的命令更新目标文件 如果该规则中的依赖文件又是其他规则中的目标文件 那么依照规则链不断执行这个过程 直到Makefile文件的结束 至少可以找到一个不是规则生成的最终依赖文件 获得此文件的时间戳然后从下到上依照规则链执行目标文件的时间戳比此文件时间戳旧的规则 直到最顶层的规则通过以上的分析过程 可以看到make的优点 因为 o目标文件依赖 c源文件 源码文件里一个简单改变都会造成那个文件被重新编译 并根据规则链依次由下到上执行编译过程 直到最终的可执行文件被重新连接 例如 当改变一个头文件的时候 由于所有的依赖关系都在Makefile里 因此不再需要记住依赖此头文件的所有源码文件 make可以自动的重新编译所有那些因依赖这个头文件而改变了的源码文件 如果需要 再进行重新连接 Makefile中的变量 Makefile里的变量就像一个环境变量 事实上 环境变量在make中也被解释成make的变量 这些变量对大小写敏感 一般使用大写宇母 几乎可以从任何地方引用定义的变量 变量的主要作用如下 保存文件名列表 在前面的例子里 作为依赖文件的一些目标文件名出现在可执行文件的规则中 而在这个规则的命令行里同样包含这些文件并传递给gcc做为命令参数 如果使用一个变量来保存所有的目标文件名 则可以方便地加入新的目标文件而且不易出错 保存可执行命令名 如编译器 在不同的Linux系统中存在着很多相似的编译器系统 这些系统在某些地方会有细微的差别 如果项目被用在一个非gcc的系统里 则必须将所有出现编译器名的地方改成用新的编译器名 但是如果使用一个变量来代替编译器名 那么只需要改变该变量的值 其他所有地方的命令名就都改变了 保存编译器的参数 在很多源代码编译时 gcc需要很长的参数选项 在很多情况下 所有的编译命令使用一组相同的选项 如果把这组选项使用一个变量代表 那么可以把这个变量放在所有引用编译器的地方 当要改变选项的时候 只需改变一次这个变量的内容即可 变量的定义和使用 Makefile中的变量是用一个文本串在Makefile中定义的 这个文本串就是变量的值 只要在一行的开始写下这个变量的名字 后面跟一个 号 以及要设定这个变量的值即可定义变量 下面是定义变量的语法 VARNAME string使用时 把变量用括号括起来 并在前面加上 符号 就可以引用变量的值 VARNAME make解释规则时 VARNAME在等式右端展开为定义它的字符串 变量一般都在Makefile的头部定义 按照惯例 所有的Makefile变量都应该是大写 如果变量的值发生变化 就只需要在一个地方修改 从而简化了Makefile的维护 Makefile变量举例 现在利用变量把前面的Makefile重写一遍 OBJS prog ocode oCC gcctest OBJS CC otest OBJS prog o prog cprog hcode h CC cprog c oprog ocode o code ccode h CC ccode c ocode oclean rm f o 变量的类型 除用户自定义的变量外 make还允许使用环境变量使用环境变量的方法很简单 在make启动时 make读取系统当前已定义的环境变量 并且创建与之同名同值的变量 因此用户可以像在shell中一样在Makefile中方便的引用环境变量 需要注意的是 如果用户在Makefile中定义了同名的变量 用户自定义变量将覆盖同名的环境变量自动变量预定义变量 GNUmake的主要预定义变量 1 2 不包含扩展名的目标文件名称 所有的依赖文件 以空格分开 并以出现的先后为序 可能包含重复的依赖文件 第一个依赖文件的名称 所有的依赖文件 以空格分开 这些依赖文件的修改日期比目标的创建日期晚 目标的完整名称 所有的依赖文件 以空格分开 不包含重复的依赖文件 如果目标是归档成员 则该变量表示目标的归档成员名称 例如 如果目标名称为mytarget so image o 则 为mytarget so 而 为image o AR归档维护程序的名称 默认值为ar ARFLAGS归档维护程序的选项 AS汇编程序的名称 默认值为as ASFLAGS汇编程序的选项 GNUmake的主要预定义变量 2 2 CCC编译器的名称 默认值为cc CFLAGSC编译器的选项 CPPC预编译器的名称 默认值为 CC E CPPFLAGSC预编译的选项 CXXC 编译器的名称 默认值为g CXXFLAGSC 编译器的选项 FCFORTRAN编译器的名称 默认值为f77 FFLAGSFORTRAN编译器的选项 Makefile的隐含规则 在上面的例子中 几个产生目标文件的命令都是从 c 的C语言源文件和相关文件通过编译产生 o 目标文件 这也是一般的步骤 实际上 make可以使工作更加自动化 也就是说 make知道一些默认的动作 它有一些称作隐含规则的内置的规则 这些规则告诉make当用户没有完整地给出某些命令的时候 应该怎样执行 例如 把生成prog o和code o的命令从规则中删除 make将会查找隐含规则 然后会找到并执行一个适当的命令 由于这些命令会使用一些变量 因此可以通过改变这些变量来定制make 象在前面的例子中所定义的那样 make使用变量CC来定义编译器 并且传递变量CFLAGS 编译器参数 CPPFLAGS C语言预处理器参数 TARGET ARCH 目标机器的结构定义 给编译器 然后加上参数 c 后面跟变量 第一个依赖文件名 然后是参数 o加变量 目标文件名 综上所述 一个C编译的具体命令将会是 CC CFLAGS CPPFLAGS TARGET ARCH c o 隐含规则举例 在上面的例子中 利用隐含规则 可以简化为 OBJS prog ocode oCC gcctest OBJS CC o prog o prog cprog hcode hcode o code ccode hclean rm f o make命令行选项 直接在make命令的后面键入目标名可建立指定的目标 如果直接运行make 则建立第一个目标 还可以用make fmymakefile这样的命令指定make使用特定的makefile 而不是默认的GNUmakefile makefile或Makefile GNUmake命令还有一些其他选项 下面是GNUmake命令的常用命令行选项命令行选项含义 CDIR在读取makefile之前改变到指定的目录DIR fFILE以指定的FILE文件作为makefile h显示所有的make选项 i忽略所有的命令执行错误 IDIR当包含其他makefile文件时 可利用该选项指定搜索目录 n只打印要执行的命令 但不执行这些命令 p显示make变量数据库和隐含规则 s在执行命令时不显示命令 w在处理makefile之前和之后 显示工作目录 WFILE假定文件FILE已经被修改 使用automake和autoconf产生Makefile 在开始使用Automake和autoconf之前 请先确认系统已经安装以下的软件 GNUAutomakeGNUAutoconfGNUm4PerlGNULibtool 如果你需要产生sharedlibrary Automake所产生的Makefile除了可以做到程序的编译和连接 也已经把如何产生程序文件的操作 以及把安装程序都考虑进去了 所以源程序所存放的目录架构最好符合GNU的标准惯例 下面用hello c来作为例子进行说明 生成一个源程序 在工作目录下建立一个新的子目录devel 再在devel下建立一个hello的子目录 这个目录将作为存放hello这个程序及其相关文件的地方 用编辑器写个hello c文件 includeintmain intargc char argv printf Hello GNU n return0 使用Autoconf及Automake来产生Makefile文件的步骤 autoscan产生一个configure in的模板 执行autoscan后会产生一个configure scan的文件 可以用它做为configure in文件的模板 编辑configure scan文件 如下所示 并且把文件名改成configure in执行aclocal和autoconf 分别会产生aclocal m4及configure两个文件 编辑Makefile am文件 内容如下 执行automake add missing Automake会根据Makefile am产生一些文件 包含最重要的Makefile in 最后执行 configure 使用RCS CVS来管理源代码 RCS的使用CVS的使用 RCS的使用 RCS RevisionControlSystem 即程序改版控制系统 主要功能是用来管理文件的版本 可以节省空间和时间 这样就不需要在每个程序开发到某一个阶段就将数据拷贝到其他的地方备份起来了 RCS提供了如下几个最重要的指令的 ci指令 将文件放入RCS目录下的控制系统co指令 从RCS目录下将文件取出rcs指令 用来对RCS文件进行参数的设置 基本操作方式 一般而言 RCS所产生出来的文件会放在RCS目录中 所以第一步必须要在当前的目录下制作一个文件 root wyhlinux mkdirRCS接下来只要使用ci指令 就可以把文件备份到RCS改版控制系统中 root wyhlinux citest c若要将文件取出 可以使用下列指令 root wyhlinux cotest c取出来的文件是只读文件 若要取出可以写入的工作文件 可以加上 l参数来锁定它 root wyhlinux co ltest c此外将文件放入RCS控制系统时 可以使用 l参数锁定文件 那么目录下的文件依然存在 root wyhlinux ci ltest c若要比较当前的文件和RCS中最新版本的文件 可以使用下列指令 root wyhlinux rcsdifftest c 指定版本 若不指定版本编号时 co会从RCS取得最新的版本 如果要以特定的版本号码写入RCS或读出 可以使用 r参数选项 root wyhlinux ci l r3 25test c 以3 25作为版本编号 root wyhlinux co l r1 2test c 将RCS中1 2版的test c读出此外 rcsdiff也可以用来指定任何一个版本和当前程序代码进行比较 root wyhlinux rcsdiff r3 25test c 取出3 25版与test c进行比较 关键词的使用 在RCS中可以将关键词变量放入程序代码中 这些变量经过RCS会变成版本的注解 用户可以将这些关键词说明当作是程序中的批注 常用的关键词如下 Author 将版本放入RCS的用户名称 Data 记录程序代码放入RCS时的日期和时间 Header 记录文件的标头 包括RCS路径名称 版本号码 日期 作者等 ID 和 Header 相同 但不包括RCS路径名称 Locker 记录锁定本版本的用户名称 Log 记录将RCS锁住的时间 所输入的文本语句 RCSfiles 记录RCS文件名称 Rivision 指定版本号码 Source RCS文件名称 包括其路径 State 使用 s选项所指定的特殊状态 使用关键词的步骤 在程序代码中加入任一关键词 root wyhlinux vitest c将程序代码放入RCS版本控制系统 root wyhlinux ci ltest c将文件再次取出 在取出的过程中 co会将每个关键词展开成其对应的值 root wyhlinux co ltest c root wyhlinux cattest c CVS的使用 CVS ConcurrentVersionSystem 是个版本控制系统 利用该系统可以记录源代码文件的历史 例如 当软件修改时会产生Bug 并且可能在做这次修改后很长时间不会发现这些问题 使用CVS就可以容易地回顾老的代码版本去发现哪一次的修改导致这些问题 如果CVS保留每一次的代码版本 会浪费很多的空间 因此CVS使用一种比较聪明的办法保存多个版本在一个文件中 它仅仅保留版本间的不同内容 如果很多人在同一个项目上工作 则CVS使用让不同开发者独立工作的方式解决了这个问题 每一个开发者的工作都在他自己的目录内 并且CVS将在每个开发者的工作完成后进行合并工作 在Linux下 CVS的使用一般是以命令行方式 通常 CVS有两种使用方式 一是本机方式 一是远程执行方式 CVS的命令格式是 cvs cvs的选项 cvs的动作 选项 可以用cvs Hcommand列出命令command的使用方法 开始项目 用CVS管理代码 首先要创建一个 信息仓库 信息仓库 简单来说包含一个目录结构 它包括要管理的源代码和用于管理源代码的各种管理文件先设置环境变量CVSROOT 指向信息仓库的绝对路径 然后调用CVS的init命令 CVSROOT usr local cvsroot exportCVSROOT cvsinit ls l CVSROOT 添加项目的文件 目录到信息仓库 要将需要管理的项目的文件加入到信息仓库 并做上标志 如果从头开始一个新的项目 就需要创建一个单独的目录 并把所有要使用的文件做一个有效的组织 而如果在开始使用源文件之前该目录就已经存在 则只需进入该目录就行了 然后 就可以输入源文件目录 cvsimport m CreateSourceDir cvstest cwucvstest这样会生成 CVSROOT cvstest c目录 其中 m用来指定注释信息 如果后面在命令行不指定注释信息 则会启动缺省编辑器 vi 要求输入注释信息 cvstest c是项目名称 实际上是仓库名 在CVS服务器上会存储在以这个名字命名的仓库里 wu cvstest分别标识了作者和发行标识 命令简介 导出源文件 1 2 cvscheckout rrev Ddate ddir jmerg1 jmerg2 modules r导出指定版本的模块 D导出指定日期的模块 d导出指
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- Wells 静脉血栓评分应用试卷及答案
- AB801-生命科学试剂-MCE
- 2025秋九年级上册历史上课课件 第11课 古代日本
- 2025年毛概题库及答案
- DB61T 322.5-2011 核桃坚果丰产指标
- 吉林省长春九台市师范高级中学2025年数学高三第一学期期末教学质量检测模拟试题
- 上海市重点名校2025-2026学年高三数学第一学期期末质量检测模拟试题
- 江苏省连云港市海庆中学2025-2026学年数学高三第一学期期末经典试题
- 禁毒知识培训会通知课件
- 八年级下册物理试卷及答案
- 统计造假弄虚作假自查范文(通用5篇)
- 更换双电源更换施工方案
- 煤化工气化工艺系统知识课件
- 创业指导师(二级)理论考试题库附答案
- 空调器喷涂工艺规范
- 2023学年完整公开课版中国疆域
- 07HBP与OD、TD、LD区别培训课件
- 机械加工安全隐患排查表
- 12K101-3 离心通风机安装
- 2023年安徽师范大学医院高校医学专业毕业生招聘考试历年高频考点试题含答案解析
- 高二下学期主题班会课件-开学第一课
评论
0/150
提交评论