Linux程序开发 - 简介

AI 摘要: 本文介绍了开发应用程序时的目录结构和常用头文件的使用,包括应用程序存放位置、头文件的存放位置以及预定义 include 的`<>`与`""`的区别。

开发引导

  • 应用程序
  • 头文件
  • 库文件
  • 静态库
  • 共享库
  • 其他

应用程序

linux 应用程序通常在系统中的存放目录是有讲究的:

  • 系统正常使用的程序,在/usr/bin
  • 系统管理员为特定的主机或者本地网络添加的程序,通常在/usr/local/bin或者/opt
  • 开发应用或个人应用,通常在自家/home/user目录下

头文件

用 C 语言进行程序设计时,需要用头文件来提供对常量的定义和对系统函数以及库函数的声明,这些头文件所在,/usr/include目录以及其子目录

  • linux 版本头文件:/usr/include/sys、/usr/include/linux
  • 其他的/usr/include/X11、/usr/include/c++等

在调用 c 语言编译器时,使用-I来包含保存在标准的子目录或者非标准位置中的头文件。

1
    gcc -I/usr/openwin/include fred.c

库文件

库是一组预先编译好的一组相关联函数集合组成以执行某项常见的任务(比如数据库访问、文件访问等),目标是可重用。标准库函数通常存储在/lib/usr/lib两个目录中。仅把库文件放在标准目录中不够,还需要遵循特定的命名规范,并且需要在命令行中明确指定。

当程序需要使用函数库中的某个函数时候,它包含一个声明该函数的头文件,编译器和链接器负责将程序代码和库函数集合在一起,以组成一个独立可执行的文件。若有用到标准 C 语言运行库外的其他库,则需要使用-l方式指明。

库名:以 lib 开头,随后指明是什么库(c:C 语言库,m:数学库),后缀类型(.a:传统静态函数库,.so:共享函数库) 编译:可以用完整库名,或者-l加库简写(如-lm,代表编译过程中,需要从标准库目录中搜索名为 libm.a 的数学函数库)。-l标志的另一个好处,有共享库会优先使用共享库。

  • gcc -o fred fred.c -lm
  • gcc -o fred fred.c /usr/lib/libm.a

可以基于大写的-L,使编译器增加库的搜索目录,比如gcc -o x11fred -L/usr/openwin/lib x11fred.c -lX11,表示用/usr/openwin/lib目录中的 libX11 库来编译和链接程序 x11fred.

静态库

静态库,也叫归档文件(archive),因此按惯例得到.a结尾后缀的库文件(比如/usr/lib/libc.a

自己制作静态库:基于ar程序和gcc -c(编译、汇编到目标代码,不进行链接)对函数分别进行编译,应该尽可能的分别保存到不同的源文件中,公共数据保存在同一个源文件中(基于声明的静态变量)

自己创建静态库

fred.c

1
2
3
4
5
#include <stdio.h>
void fred(int arg)
{
    printf("fred: we passwd %d \n", arg);
}

bill.c

1
2
3
4
5
#include <stdio.h>
void bill(char *arg)
{
    printf("bill: we passwd %s\n", arg);
}

bf.h

1
2
3
//declare bill.c and fred.c function for user
void fred(int);
void bill(char *);

porg.c

1
2
3
4
5
6
7
#include <stdlib.h>
#include "bf.h"
int main()
{
    bill("hey man, cool");
    exit(0);
}

命令行链接 & 打包

1
2
3
4
5
6
7
8
9
gcc -c *.c  #编译成object,用于链接和做成静态库
gcc -c prog.c bill.o #编译prog成object
gcc -o prog prog.o bill.o #链接输出到prog执行文件
# 制作静态库
ar crv libfoo.a bill.o fred.o #创建库文件
ranlib libfoo.a #为函数库生成内容表,后续可以基于`nm libfoo.a`或者`nm prog`查看
# 编译链接
gcc -o prog2 prog.c -L. -lfoo 或者
gcc -o prog1 prog.c -L. libfoo.a

查看库文件中的内容

1
2
3
4
5
6
7
[Terry@home-mac 1-introduction]$ nm libfoo.a
libfoo.a(bill.o):
0000000000000000 T _bill
                 U _printf
libfoo.a(fred.o):
0000000000000000 T _fred
                 U _printf

共享库

静态库的一个缺点是,当同时运行多个应用程序,并且他们都使用来至同一个函数库的函数时候,内存中就会有同一个函数的多个副本,而且程序文件自身也有多个同样的副本,占用磁盘和内存的开销,共享库就是解决重复库包含的问题的。

共享库保存位置和静态库一致,但后缀名是基于.so区分的,比如(/usr/lib/libm.so)。

当一个程序使用共享库时,它的链接方式是:程序不再包含函数代码,而是应用运行时刻访问的共享代码。当编译好的程序被装载入内存中执行时候,函数引用被解析并产生对共享库的函数调用,如果有必要,共享库才会被加载到内存中(符合按需加载的原则),这样做的好处:系统可以只保存一份副本供所有有需要的程序共同使用;另外,共享库函数版本升级时候,仅需要修订一份文件即可(比如/lib/libm.so 就是/lib/libm.so.N,版本的符号链接)。

linux 系统负责装载共享库并解析执行程序所引用函数的程序是ld.so,相关共享库搜索路径配置/etc/ld.so.conf,修改该配置后,需要通过ldconfig来重载配置。

最后,可以基于ldd命令查看某个程序所依赖的共享库。

其他

gcc 参数几个说明

1
2
3
4
5
gcc --help
-E                       仅作预处理,不进行编译、汇编和链接
-S                       编译到汇编语言,不进行汇编和链接
-c                       编译、汇编到目标代码,不进行链接
-o <文件>                输出到 <文件>

预定义 include 的<>""的区别

#include <stdio.h>#include "bf.h"差别:使用尖括号表示在系统头文件目录(/usr/include)下找,而不在源文件目录来找;使用双引号则表示首先在当前目录寻找,找不到再到系统头文件目录查找。