库是一种可以执行代码的二进制形式。可以被操作系统载入内存执行。
linux环境下的库分为静态库和动态库(又称为共享库)。
使用静态库,将来目标的体积大,升级复杂(是对使用者说的),但是可移植性好,浪费磁盘空间和内存空间;
使用动态库,将来目标的体积小,升级简单(是对使用者说的),但是可移植性不好,节约磁盘空间和内存空间,还有一个好处是,不同应用程序如果调用相同的库,那么在内存中只需要一份该共享库的实例。
一、静态库的制作
先看一下目录结构
其中,libtest/main.c 用于测试静态库,可以看做库的使用者,main.c是测试程序。joseph/和list/目录下面是将要制作成库的文件。
先看一下这些文件中的部分内容:
main.c
#include "joseph.h" //注意:这里的头文件路径的具体写法要看测试时main.c和得到的头文件的具体放置位置,如果他们在同一级目录下,就是左边这种写法。
int main(void)
{
joseph();
return 0;
}
joseph.h
1: #ifndef __JOSEPH_H__
2: #define __JOSEPH_H__
3:
4: #define N 8
5: #define K 3
6: #define M 4
7: extern void joseph(void);
8:
9: #endif
joseph.c
#include "joseph.h"
#include "../list/listlinkloop.h" //注:这里的头文件路径不用改,因为是库的开发者,各个模块都有自己的目录,生成.o 文件时,会自动根据这个路径把相应的头文件拷贝过来
#include <stdlib.h>
#include <stdio.h>
void joseph(void)
{
listlinkloop *l = listcreate();
listlinkloop *temp1, *temp2;
int i;
for(i=1; i<=N; i++)
{
listinserttail(l, i);
}
temp1 = listcuthead(l);
for(i=1; i<K; i++)
{
temp1 = temp1->next;
}
while(temp1->next != temp1)
{
for(i=2; i<M; i++)
{
temp1 = temp1->next;
}
temp2 = temp1->next;
temp1->next = temp2->next;
printf("%d ", temp2->data);
free(temp2);
temp1 = temp1->next;
}
printf("%d\n",temp1->data);
free(temp1);
}
listlinkloop.h
1: #ifndef __LISTLINKLOOP_H__
2: #define __LISTLINKLOOP_H__
3:
4: typedef int listdata;
5:
6: typedef struct list
7: {
8: listdata data;
9: struct list *next;
10: }listlinkloop;
11:
12:
13: extern void listprintf(listlinkloop *l);
14: extern void listinserttail(listlinkloop *l, listdata data);
15: extern listlinkloop *listcreate(void);
16: extern void listinserthead(listlinkloop *l, listdata data);
17: extern listlinkloop *listcuthead(listlinkloop *l);
18: #endif
listlinkloop.c
#include "listlinkloop.h"
#include <stdlib.h>
#include <stdio.h>
listlinkloop *listcreate(void)
{
listlinkloop *l = (listlinkloop *)malloc(sizeof(listlinkloop));
l->next = l;
return l;
}
void listinserthead(listlinkloop *l, listdata data)
{
listlinkloop *temp1 = (listlinkloop *)malloc(sizeof(listlinkloop));
temp1->data = data;
temp1->next = l->next;
l->next = temp1;
}
listlinkloop *listcuthead(listlinkloop *l)
{
listlinkloop *temp = l;
while(temp->next != l)
{
temp = temp->next;
}
temp->next = l->next;
free(l);
return (temp->next);
}
void listinserttail(listlinkloop *l, listdata data)
{
listlinkloop *temp1 = (listlinkloop *)malloc(sizeof(listlinkloop));
listlinkloop *temp2 = l;
temp1->data = data;
temp1->next = l;
while(temp2->next != l)
{
temp2 = temp2->next;
}
temp2->next = temp1;
}
void listprintf(listlinkloop *l)
{
listlinkloop *temp = l;
while(temp->next != l)
{
printf("%d \n", temp->next->data);
temp = temp->next;
}
printf("\n");
}
void listloopprintf(listlinkloop *l)
{
listlinkloop *temp = l;
printf("%d ",temp->data);
while(temp->next != l)
{
printf("%d ", temp->next->data);
temp = temp->next;
}
printf("\n");
}
步骤:
- 进入joseph/,然后执行命令
gcc –c joseph.c –o joseph.o //-c 只激活预处理、编译和汇编,目的是生成目标文件
- 在进入list/,执行命令:
gcc -c listlinkloop.c –o listlinkloop.o //-c 只激活预处理、编译和汇编,目的是生成目标文件
- 然后就是生成库文件,后缀是 .a 。先进入tmp/,然后执行命令
ar crs libpeng.a joseph/joseph.o list/listlinkloop.o
- 将生成的库文件libpeng.a 和 joseph.h 、listlinkloop.h 移动或者复制到测试目录下,即libtest/,进入libtest/目录,然后执行命令
gcc main.c –L . –l peng //-L . 表示库在当前目录下,如果在其他目录下,如 /home/linux下,则 –L /home/linux -l是小写的L,peng是将libpeng.a去掉lib和后缀.a后的部分,编译器自动会在peng的前面加上lib,在peng的后面加上.so,从而构成库的名称。
- 会生成可执行文件main
- 最后执行命令 ./main 进行验证
实际上,上面的一些列操作都可以通过Makefile来完成。
在tmp/ 目录下,编写Makefile
CFLAGS=-Wall
CC=gcc
export CC
libpeng.a:joseph.o listlinkloop.o
ar crs $@ $^
mv libpeng.a ../libtest/
cp joseph/joseph.h ../libtest/
cp list/listlinkloop.h ../libtest/
make -C ../libtest/
joseph.o:joseph/joseph.c
$(CC) $(CFLAGS) -c $^ -o $@
listlinkloop.o:list/listlinkloop.c
$(CC) $(CFLAGS) -c $^ -o $@
clean:
$(RM) ./*.o
$(RM) ./*.a
$(RM) ../libtest/*.a
$(RM) ../libtest/*.out
$(RM) ../libtest/*.h
$(RM) ../libtest/main
.PHONY:clean
然后再在libtest/目录下编写Makefile如下:
main:main.c
$(CC) $^ -L . -l peng -o $@
最后在tmp/目录下输入 make 命令即可完成全部工作。
静态库升级时,库的开发者需要重新生成新的静态库,库的使用者需要重新进行编译,过程比较麻烦。
二、动态库的制作
还是以上面的程序为例。
第一步、生成目标文件
进入tmp/joseph/,执行命令:
gcc –fPIC -Wall –c joseph.c
进入tmp/listlinkloop/,执行命令:
gcc –fPIC -Wall –c listlinkloop.c
第二步、程序动态库
在tmp/下执行命令
gcc –shared –Wl,-soname,libpeng.so joseph/joseph.o list/listlinkloop.o –o libpeng.so.1
//其中,参数-shared 指定生成动态链接库。-Wl,-soname 将-soname 传递给链接器ld。其中libpeng.so是soname的名字,前者不带版本号。joseph/joseph.o list/listlinkloop.o 是用到的两个目标文件,就是将他们两个制作成动态库。libpeng.so.1 是最终生成的动态库的名字。
第三步、测试
将生成的动态库文件libpeng.so.1和joseph.h 、listlinkloop.h 移动或者复制到测试目录下,即libtest/,进入libtest/目录,然后执行命令
- ln –s /home/linux/jg/libtest/libpeng.so.1 libpeng.so //目的:生成动态库libpeng.so 的软连接,注意:目标前面必须是绝对路径!
- gcc main.c –L . –l peng –o main //生成可执行文件main,编译器会自动将peng补充完整,变成libpeng.so ,他就是刚才制作的动态库的软连接。-L . 表示动态库的软连接在当前目录下,也就是-L 后跟的是动态库软连接的路径,而非动态库的路径
- sudo cp –a libpeng.so /lib //目的:将软连接本身复制到系统的/lib 目录下,应该以管理员权限完成此项操作。或者 sudo mv libpeng.so /lib。此时在执行时,动态载入器就可以找到动态库的软连接libpeng.so,进而就会找到动态库libpeng.so.1
上面所完成的一些列操作可以通过Makefile来完成。
进入tmp/目录下,编写Makefile:
CFLAGS=-Wall -fPIC
CC=gcc
export CC
libpeng.a:joseph.o listlinkloop.o
$(CC) -shared -Wl,-soname,libpeng.so joseph.o listlinkloop.o -o libpeng.so.1
mv libpeng.so.1 ../libtest/
cp joseph/joseph.h ../libtest/
cp list/listlinkloop.h ../libtest/
make -C ../libtest/
joseph.o:joseph/joseph.c
$(CC) $(CFLAGS) -c $^ -o $@
listlinkloop.o:list/listlinkloop.c
$(CC) $(CFLAGS) -c $^ -o $@
clean:
$(RM) ./*.o
$(RM) ./*.a
$(RM) ../libtest/*.a
$(RM) ../libtest/*.so
$(RM) ../libtest/*.out
$(RM) ../libtest/*.h
$(RM) ../libtest/main
sudo rm /lib/libpeng.so
.PHONY:clean
进入libtest/下,编写另一个Makefile:
main:main.c
ln -s /home/linux/jg/libtest/libpeng.so.1 libpeng.so
$(CC) $^ -L . -l peng -o $@
sudo cp -a libpeng.so /lib
然后再tmp/ 目录下执行 make 命令即可。
动态库的升级方法:
对于库的开发人员需要重新生成新的动态库(按照前面的方法),如libpeng.so.2,对于库的使用者,拿到新的库之后,需要只需要在新库所在的目录下面执行:
ln –s 新库所在的绝对路径/libpeng.so.2 libpeng.so ,然后把生成的新的链接文件libpeng.so放到/lib下即可。