linux编程学习笔记(四) 编译工具和动态库

来源:互联网 发布:mysql驱动配置 编辑:程序博客网 时间:2024/04/29 10:37

原地址:http://blog.csdn.net/a8887396/article/details/8996297


1 gcc 通用选项

-o 输出的文件名
-O -O1 -O2 -O3编译优化 -O默认情况下是最优化的
-g -g0 -g1 -g2 -g3  产生调试信息
-W两个选项all  error   
-Wall显示所有警告  
-Werror 所有警告当错误
-w关闭警告
-E 预编译  直接会打印出来 所以一般和-o配合使用
gcc map.c -E -o map.i
-c只编译不连接
gcc map.c -c -o map.o
-S 汇编 
gcc map.c -S -o map.s

编译过程 -E -c -S 自动调用连接器ld


-D 在命令行定义宏 

[cpp] view plaincopy
  1. int printf(const char*,...);  
  2.     main()  
  3.     {  
  4.         printf("%d\n",NUM);  
  5.     }  

[cpp] view plaincopy
  1. zhao@ubuntu:~/unix/two$ gcc gcc.c -DNUM=50  
  2. zhao@ubuntu:~/unix/two$ ./a.out   
  3. 50  

还可以在代码中定义宏


-x 指定编译的语言类型
c
assembler 
gcc -xassembler map.s
none 自动判定
gcc -xnone map.c

-std= 设置c语言标准
-std=c89
-std=c99
c99一些特性:
restrict 关键字 优化指针寻址
告诉编译器 所有修改该指针指向内存的操作都必须通过该指针,以便于编译器进行优化。
但判断有没有其他指针或其他方式修改该内存,是程序员的工作。
for(int i=0;i <10;i++) { }  for中可以设置变量了
main 需要返回int,否则警告


补充 文件类型
.so动态库
.a静态库
.i  预编译文件
.o 二进制文件
.s 汇编文件




2 静态库的编译(.a)

不使用库的时候直接编译可执行程序
gcc callku.c ku1.c ku2.c -omain

2.1编译成目标文件

-static  可选

gcc -static -c 代码.c        生成.o文件
gcc -static -c ku1.c
gcc -static -c ku2.c

2.2归档成静态库

ar工具(创建归档文件)
ar -r 静态库文件 被归档的文件
zhao@ubuntu:~/unix/two$ ar -r libku.a ku1.o  ku2.o
ar: creating ku.a
ar -t 静态库文件 查看归档文件包含
zhao@ubuntu:~/unix/two$ ar -t libku.a 
ku1.o
ku2.o

nm工具 (查看函数符号表)
nm 静态库或者动态库或者目标文件或者执行文件
zhao@ubuntu:~/unix/two$ nm libku.a  可以看到目标中包含的函数
ku1.o:
00000000 T add


ku2.o:
00000000 T sub


2.3使用静态库

gcc 代码 库归档
gcc callku.c libku.a -omain 
实际上是将静态库作为代码的一部分在编译
gcc callku.c -lku -L.
标准调用方式
如果其他组的人写了很多的文件,那么你使用他们的函数时要编译大量的代码
这时不如使用他们的库文件

2.4 使用静态库完成如下程序:

输入一个菱形的半径,打印一个菱形
输入一个整数,封装成IOTool
菱形的打印封装成Graphics
计划:
1 实现输入
2 实现菱形
3 编译成静态库
4  调用静态库

IOTool.c

[cpp] view plaincopy
  1. #include <stdio.h>  
  2. int inputInt(const char *info)  
  3. {  
  4.     printf("%s:",info);  
  5.     int ret;  
  6.     scanf("%d",&ret);  
  7.     return ret;  
  8. }  

Graphics.c

[cpp] view plaincopy
  1. #include <stdio.h>  
  2. void diamond(int r)  
  3. {  
  4.     int x,y;  
  5.     for(y=0;y<=2*r;y++)  
  6.     {  
  7.         for(x=0;x<=2*r;x++)  
  8.         {  
  9.             if(y==x+r || y==x-r || y==-x+r || y==3*r-x)  
  10.             {  
  11.                 printf("*");  
  12.             }  
  13.             else  
  14.             {  
  15.                 printf(" ");  
  16.             }  
  17.               
  18.         }  
  19.         printf("\n");  
  20.     }  
  21. }  
call_diaomond.c

[cpp] view plaincopy
  1. main()  
  2. {  
  3.     int r =  inputInt("请输入菱形的半径");  
  4.      diamond( r);  
  5. }  

gcc -static -c  Graphics.c 
gcc -static -c IOTool.c
ar -r libdemo.a Graphics.o IOTool.o

gcc  call_diaomond.c libdemo.a   //这里要先写代码 后写库 交换会无法正确编译
zhao@ubuntu:~/unix/two$ ./a.out 

请输入菱形的半径:3
   *   
  * *  
 *   * 
*     *
 *   * 
  * *  
   *  

2.5总结

1什么是库?
函数等代码封装的二进制已编译的归档文件
2 ar归档工具
3 采用库的方式管理代码优点:
容易组织代码
复用
保护代码版权
4 静态库的静态含义:
编译好的程序运行的时候不依赖库
库作为程序的一部分编译和连接
5 静态库本质:
就是目标文件的集合(归档)
6 -static 可选 (可以不写)

库命名规则:
lib库名.a.主版本号.副版本号.批号
lib库名.a
库的使用规则:
-l库名(不写lib)  -L制定库所在路径
gcc call_diaomond.c -ldemo -L.
这样的调用方式需要你使用库的标准命名规则(lib库名.a)




3 动态库的编译

3.1 什么是动态库?

动态库是可以执行的,静态库不能执行(静态库只编译未连接)
但动态库没有main,不能独立执行
动态库不会连接成程序的一部分
程序执行的时候,必须需要动态库文件。

3.2工具

ldd 查看程序需要调用的动态库文件ldd 只能查看可执行文件(ELF格式,
file 查看文件格式 
readelf -h 查看elf文件头格式
nm 查看库中的函数符号

3.3动态库的编译

3.1 编译
-c -fpic(可选)  position indenpendet code 
gcc -c -fpic IOTool.c 
 gcc -c -fpic Graphics.c
3.2 连接
-shared
gcc -shared  IOTool.o Graphics.o -o libdemo2.so 

3.4 使用动态库

gcc 代码 动态库文件名
gcc 代码 -l库名  -L动态库路径
gcc call_diaomond.c  -ldemo2 -L.
问题:
1 执行程序怎么加载动态库?
找到动态库
加载动态库到内存
映射到用户的内存空间
系统对动态库查找规则:
/lib 
/usr/lib
到环境变量LD_LIBRARY_PATH指定的路径查找
缓冲机制:
预先把/lib:/usr/lib:LD_LIBRARY_PATH中的动态库加载到缓存中
ldconfig -v 刷新缓存中的动态库
ldconfig -v | grep "libdemo2.so" 查看缓存中是否有需要的库

2 动态库没有作为程序的一部分,为何在连接时需要它?
连接器需要确定函数在动态库中的偏移位置

3 cannot open shared object file  解决方法:
zhao@ubuntu:~/unix/two$ gcc call_diaomond.c  -ldemo2 -L.
zhao@ubuntu:~/unix/two$ ./a.out 
./a.out: error while loading shared libraries: libdemo2.so: cannot open shared object file: No such file or directory
zhao@ubuntu:~/unix/two$ ldd a.out 
linux-gate.so.1 =>  (0x00242000)
libdemo2.so => not found
libc.so.6 => /lib/libc.so.6 (0x00dc1000)
/lib/ld-linux.so.2 (0x0055a000)
编译时指定的路径是做为连接使用。
因为动态库使用时,只在上述3个位置中查找。(/lib /usr/lib LD_LIBRARY_PATH)
此时在将动态库拷贝到前两个路径之一去可解决。(需要管理员权限)
或者修改环境变量
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH (冒号隔开多个路径,只在当前terminal中有效)
zhao@ubuntu:~/unix/two$ gcc call_diaomond.c  -ldemo2 -L. 



-L  与 LD_LIBRARY_PATH的区别:

-L是在编译时将查找.so 或者 .a 连接生成需要的程序

LD_LIBRARY_PATH是环境变量, 程序运行时查找库



3.5 总结

动态库编译与使用方法:
1编译
gcc -c -fpic IOTool.c 
 gcc -c -fpic Graphics.c
2连接
gcc -shared -olibdemo2.so IOTool.o Graphics.o
3 使用(注意动态库的路径)
gcc call_diaomond.c  -ldemo2 -L.


3.6 综合应用

输入两个数 计算两个数的和
要求:输入与计算两个数的和封装成动态库的调用

[cpp] view plaincopy
  1. //InputInt.c  
  2.     #include <stdio.h>  
  3.     void inputInt(int *a)  
  4.     {  
  5.         printf("请输入两个整数");  
  6.         scanf("%d%d",a,a+1);  
  7.     }  
  8. // sum.c  
  9.      int sum(int a,int b)  
  10.     {  
  11.         return a+b;  
  12.     }  
  13.       
  14.   
  15. // call_add.c  
  16.      #include <stdio.h>  
  17.     main()  
  18.     {  
  19.         int a[2];  
  20.         inputInt(a);  
  21.         printf("%d+%d=%d\n",a[0],a[1],sum(a[0],a[1]));  
  22.     }  

gcc -fpic -c InputInt.c
gcc -fpic  -c sum.c
gcc -shared  -o libdemo3.so InputInt.o sum.o
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH

gcc call_add.c -ldemo3 -L.
zhao@ubuntu:~/unix/two$ ./a.out 
请输入两个整数



4 使用libdl.so库

调用动态库有两种方式:
1 像前面一样 通过-l -L来由系统调用
2 手动调用动态库


动态库加载的原理

动态库中的函数的查找已经封装成库 libdl.so
dlopen 打开一个动态库
dlsym在打开动态库中找一个函数
dlclose 关闭动态库
dlerror 返回错误


      #include <dlfcn.h>
      Link with -ldl.
       void *dlopen(const char *filename, int flag);
filename :动态库路径
        flag :RTLD_LAZY 使用时加载 ,RTLD_NOW 立即加载
       返回值 void* 返回动态库句柄
      
       void *dlsym(void *handle, const char *symbol);
handle 已打开的动态库的句柄
symbol 函数名字
返回值: 返回函数指针
       int dlclose(void *handle);
       不是用库后 关闭动态库
       char *dlerror(void);
   

[cpp] view plaincopy
  1.       dldemo.c  
  2.       #include <dlfcn.h>  
  3. main()  
  4. {  
  5.     void *handle = dlopen("./libdemo2.so",RTLD_LAZY); //注意这里不能写静态库  
  6.     void (* func)(int) = dlsym(handle,"diamond");  
  7.     func(5);  
  8.     dlclose(handle);  
  9.       
  10. }  



zhao@ubuntu:~/unix/two$ gcc dldemo.c -ldl
zhao@ubuntu:~/unix/two$ ./a.out 
     *     
    * *    
   *   *   
  *     *  
 *       * 
*         *
 *       * 
  *     *  
   *   *   
    * *    
     *     


总结:
1 编译连接动态库
2 使用动态库
3 怎么配置让程序调用动态库
4 掌握某些工具的使用: nm ldd lddconfig
objdump
strip 去掉多余信息

zhao@ubuntu:~/unix/two$ strip  libdemo2.so 
zhao@ubuntu:~/unix/two$ nm libdemo2.so 
nm: libdemo2.so: no symbols
0 0