前言
当你勤勤恳恳完成需求后,要交付你的成果时,你突然发现了一个问题,如果直接把源代码给乙方,他就可以直接进入你的代码,然后狠狠的学习再“借鉴”,甚至修改,你的头发的产物就被别人盗取了。这该如何是好?
一、库是个啥?
①最开始的库是用来解决啥问题?
由程序员第一准则:绝不造重复的轮子。我们可以推出了在编程中,一旦一个东西被广泛的应用,甚至成为标配时,就意味着这个东西一定有种不可替代或者使用面广的的功能。
就如“前言”中所说,在用户端的代码如果不隐藏和高程度的封装,都是不行的,对于编写者不能保护其知识产权,对于用户也不能方便的使用。
而“库”这一概念就被引了出来,库的编写者们将各种各样的需求转化为各种函数或对象,在把它们如何使用的接口放入一个.h头文件中,再把这些接口的实现封装在一起,在打包或者加密隐藏其实现的源代码,而这个就是库的基本模型了。
②库的基本构成
在①的最后,我们知道了库的基本实现原理,那如果我们想手动搞一个简单的库呢?
想本质的了解库,我们先来看看编译是如何的。
对于编译的过程想必大家都了然于心,分别是 预处理、编译、汇编、链接,这四部曲每一步都会生产对应的文件。
各步骤对应的gcc 命令(我们平时使用-o命令,是直接将所有步骤合在一起了)
不同步骤时产生的对应文件后缀
从上图我们可以知道,预处理、编译、汇编、链接生成的文件后缀分别是,.i、.s、.o和最后的可执行文件.exe,而库的打包就是在Linking(链接)这一步完成的。
简而言之,库 = .o文件的集合(接口的实现)+ .h接口文件(告诉用户我有那些接口)
③动态库与静态库
在了解库的本质后,后续我们根据库的使用方法和形成方式分为了2种库:动态库(.so)与静态库(.a)
静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
特别的:
- 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
- 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)
- 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。
二、如何生成库
为了方便演示以下用Linux命令行的形式做演示。
0、相关知识
库搜索路径
- 从左到右搜索-L指定的目录。
- 由环境变量指定的目录 (LIBRARY_PATH)
- 由系统指定的目录(计算机上的默认库的存放)
- /usr/lib
- /usr/local/lib
①生成静态库
[root@localhost linux]# ls
add.c add.h main.c sub.c sub.h
[root@localhost linux]# gcc -c add.c -o add.o
[root@localhost linux]# gcc -c sub.c -o sub.o
生成静态库
[root@localhost linux]# ar -rc libmymath.a add.o sub.o
ar是gnu归档工具,rc表示(replace and create)
查看静态库中的目录列表
[root@localhost linux]# ar -tv libmymath.a
rw-r--r-- 0/0 1240 Sep 15 16:53 2017 add.o
rw-r--r-- 0/0 1240 Sep 15 16:53 2017 sub.o
t:列出静态库中的文件
v:verbose 详细信息
[root@localhost linux]# gcc main.c -L. -lmymath
-L 指定库路径
-l 指定库名
测试目标文件生成后,静态库删掉,程序照样可以运行。
其中libmymath.a即是我们依赖add.o和sub.o形成的静态库了。
②生成动态库
动态库不同于静态库,该库在使用时还需要OS去参与外部的链接,来实现共享,所以我们除了把相关的.o文件打包以外,还需要给其加一个共享的格式,又因为动态库的内部查找是偏移量的方式,所以还要加一个与位置无关码。
- shared: 表示生成共享库格式
- fPIC:产生位置无关码(position independent code)
- 库名规则:libxxx.so(生成动态库命令时,必须要前面的lib和后面的.so)
[root@localhost linux]# gcc -fPIC -c sub.c add.c [root@localhost linux]# gcc -shared -o libmymath.so*.o
[root@localhost linux]# ls add.c add.h add.o libmymath.so main.c sub.c sub.h sub.o
三、库的使用
由于静态库是直接在编译链接的时候把库的代码链接到可执行文件中。所以直接拷贝库到系统共享库路径下即可(一般指/usr/lib),程序运行的时候将不再需要静态库,即可直接使用。
但是静态库由于编译链接时不把库的代码链接到可执行文件中,而是在运行时,由操作系统从磁盘上的该动态库中复制到内存中(动态链接),所以除了我们在生成动态库告诉可执行文件库的存在,还需要告诉操作系统库的存在与路径。
而告诉操作系统主要分为3个方式
①修改环境变量
在环境变量中,有着与外部库的路径这一变量(LD_LIBRARY_PATH)。
[root@localhost linux]# export LD_LIBRARY_PATH=.
不过默认的为了环境变量的纯净,每一次启动环境变量都会重写,这就导致了我们的第三方库每一次启动机器后都要改(当然也可以修改环境变量的配置去实现永久修改,不过就跟麻烦了)。
②拷贝.so文件到系统共享库路径下, 一般指/usr/lib
和静态库的使用一样,也是拷贝到这个路径下。
③ldconfig 配置/etc/ld.so.conf.d/,ldconfig更新(配置文件)
直接写入库的对应路径即可
[root@localhost linux]# cat /etc/ld.so.conf.d/bit.conf
/root/tools/linux
[root@localhost linux]# ldconfig
特别的:这些库是用来支持项目的相关接口,所以库的配置其实也就是我们熟悉的“安装”的一个重要部分,将相应的库拷贝到系统的相应目录。