Linux 程序设计入门教程
1. 基础知识
Linux 应用程序表现为两种特殊类型的文件:可执行文件和脚本文件。
- 可执行文件是计算机可以直接运行的程序
- 脚本文件是一组指令的集合,这些指令将由另一个程序(即解释器)来执行
Linux 并不要求可执行文件或脚本文件具有特殊的文件名或扩展名。文件属性用来指明一个文件是否可执行的程序。
当登录进 Linux 系统时,你与一个 shell 程序(通常是 bash)进行交互。
它在一组指定的目录路径下按照你给出的程序名搜索与之同名的文件。搜索的目录路径存储在 shell 变量 PATH 里,搜索路径(可以手动添加)通常包含如下一些存储系统路径的标准路径:
- /bin:二进制文件目录,用于存放启动系统时用到的程序
- /usr/bin:用户二进制文件目录,用于存放用户使用的标准程序
- /usr/local/bin:本地二进制文件目录,用于存放软件安装的程序
系统管理员(例如 root 用户)登录后使用的 PATH 变量可能还包含存放系统管理程序的目录,如 /sbin 和 /usr/sbin。
可选的操作系统组件和第三方应用程序可能被安装在 /opt 目录下,安装程序可以通过用户安装脚本将路径添加到 PATH 环境变量中。
Linux 像 UNIX 一样,使用冒号(:)分隔 PATH 变量里的条目。
1 | /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/ibiu |
Linux 用正斜线(/)分隔文件名里的目录名,而不是像 Windows 那样用反斜线(\)。
2. 简单源码示例
hello.c 程序源码
1 |
|
编译、链接和运行程序
1 | gcc -o hello hello.c |
以上,你调用了 GNU C 语言编译器将 C 语言源代码转换成可执行文件 hello。然后运行这个程序,它将打印出欢迎信息。
hello 程序很可能在你的家目录中。如果 PATH 变量不包含指向你的家目录的条目,shell 就找不到 hello 程序。如果 PATH 变量中包含的其中一个目录中包含另一个名为 hello 的程序,shell 就会执行那个程序。
如果你忘记用 -o name 选项告诉编译器可执行程序的名字,编译器就会把程序放在一个名为 a.out 的文件里(a.out 的含义是 assembler output,即汇编输出)。
3. 开发系统导引
简单介绍一些重要的目录和文件:
1. 应用程序
应用程序通常存放在系统为之保留的特定目录中。系统为正常使用提供的程序,包括用于程序开发的工具,都可在目录 /usr/bin 中找到;
系统管理员为某个特定的主机或者本地网络添加的程序通常可在目录 /usr/local/bin 或 /opt 中找到。
2. 头文件
在 C 语言及其他语言进行程序设计时,你需要用头文件类提供对常量的定义和对系统函数及库函数调用的声明。
对于 C 语言来说,这些头文件几乎总是位于 /usr/include 目录及其子目录中。
在调用 C 语言编译器时,你可以使用 -I
标志来包含保存在子目录或非标志位置中的头文件。
1 | gcc -I/usr/openwin/include fred.c |
用 grep 命令来搜索包含某些特定定义和函数原型的头文件是很方便的。
3. 库文件
库是一组预先编译好的函数的集合,这些函数都是按照可重用的原则编写的。它们通常由一组互相关联的函数组成以执行某项常见的任务。
标准系统库文件一般存储在 /lib 和 /usr/lib 中,C 语言编译器(链接程序)需要知道要搜索哪些库文件。库文件必须遵循特定的命名规范并且需要在命令行中明确指定。
库文件的名字总是以 lib 开头,随后的部分指明这是什么库。文件名的最后部分以 . 开始,然后给出库文件的类型:
- .a 代表传统的静态函数库
- .so 代表共享函数库
函数库通常同时以静态库和共享库两种格式存在,你可用 ls /usr/lib
命令查看。可以通过给出完整的库文件路径名或者用 -l 标志来告诉编译器要搜索的库文件。例如:
1 | gcc -o fred fred.c /usr/lib/libm.a |
这条命令要求编译器编译文件 fred.c,将编译产生的程序文件命名为 fred,并且除了搜索标准的 C 语言函数库外,还搜索数学库以解决函数引用问题。下面命令也能产生类似结果:
1 | gcc -o fred fred.c -lm |
-lm(在字母 l 和字母 m 之间没有空格)是简写方式(简写方式在 UNIX 环境里很有用),它代表的是标准库目录中的名为 libm.a 的函数库。-lm 标志的另一个好处是如果有共享库,编译器会自动选择共享库。
4. 静态库
库函数最简单的形式是一组处于”准备好使用”状态的目标文件。当程序需要使用函数库中的某个函数时,它包含一个声明该函数的头文件。编译器和连接器负责将程序代码和函数库结合在一起以组成一个单独的可执行文件。你必须使用 -l 选项指明除标准 C 语言运行库外还需使用的库。
静态库,也称作归档文件(archive),按照惯例它们的文件名都以 .a 结尾。
你可以很容易的创建和维护自己的静态库,只要使用 ar(代表 archive,即建立归档文件)程序和使用 gcc -c 命令对函数分别进行编译。你应该尽可能把函数分别保存到不同的源文件中。如果函数需要访问公共数据,你可以把它们放到同一个源文件中,并使用在该文件中声明的静态变量。
5. 共享库
静态库的一个缺点是,当你同时运行许多应用程序并且它们都使用来自同一个函数库的函数时,内存中就会有同一函数的多份副本,而且在程序自身中也有多份同样的副本。
共享库的保存位置与静态库是一样的,但共享库有不同的文件名后缀。在一个典型的 Linux 系统中,标准数学库的共享版本是 /usr/lib/libm.so。
当一个程序使用共享库时,它的链接方式是这样的:程序本身不再包含函数代码,而是引用运行时可访问的共享代码。当编译好的程序被装载到内存中执行时,函数引用被解析并产生对共享库的调用,如果有必要,共享库才被加载到内存中。
通过这种方法,系统可以只保留共享库的一份副本供许多应用程序同时使用,并且在磁盘上也仅保留一份。另一个好处是共享库的更新可以独立于依赖它的应用程序。
对 Linux 系统来说,负责装载共享库并解析客户程序函数引用的程序(动态装载器)是 ld.so。