目录
[1. 什么是库](#1. 什么是库)
[2. 静态库](#2. 静态库)
[2.1 静态库生成](#2.1 静态库生成)
[2.2 静态库使用](#2.2 静态库使用)
[3. 动态库](#3. 动态库)
[3.1 动态库生成](#3.1 动态库生成)
[3.2 动态库使用](#3.2 动态库使用)
[3.3 库运行搜索路径](#3.3 库运行搜索路径)
[3.3.1 问题:](#3.3.1 问题:)
[3.3.2 解决方案:](#3.3.2 解决方案:)
正文开始:
1. 什么是库
库是写好的、现有的、成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。
本质上来说库是⼀种可执行代码的⼆进制形式,可以被操作系统载入内存执行。库有两种:
- 静态库 .aLinux、.libwindows
- 动态库 .soLinux、.dllwindows
2. 静态库
- 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中,程序运行的时候将不再需要静态库。
- ⼀个可执行程序可能用到许多的库,这些库运行有的是静态库,有的是动态库,而我们的编译默认为动态链接库,只有在该库下找不到动态.so的时候才会采用同名静态库。我们也可以使用 gcc 的 -static 强转设置链接静态库。
头文件和源文件示例准备:
mystdio.h:
cpp
#pragma once
#include <stdio.h>
#define MAX 1024
#define NONE_FLUSH (1<<0)
#define LINE_FLUSH (1<<1)
#define FULL_FLUSH (1<<2)
typedef struct IO_FILE
{
int fileno;
int flag;
char outbuffer[MAX];
int bufferlen;
int flush_method;
}MyFile;
MyFile *MyFopen(const char *path, const char *mode);
void MyFclose(MyFile *);
int MyFwrite(MyFile *, void *str, int len);
void MyFFlush(MyFile *);
mystdio.c:
cpp
#include "mystdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
static MyFile *BuyFile(int fd, int flag)
{
MyFile *f = (MyFile*)malloc(sizeof(MyFile));
if(f == NULL) return NULL;
f->bufferlen = 0;
f->fileno = fd;
f->flag = flag;
f->flush_method = LINE_FLUSH;
memset(f->outbuffer, 0, sizeof(f->outbuffer));
return f;
}
MyFile *MyFopen(const char *path, const char *mode)
{
int fd = -1;
int flag = 0;
if(strcmp(mode, "w") == 0)
{
flag = O_CREAT | O_WRONLY | O_TRUNC;
fd = open(path, flag, 0666);
}
else if(strcmp(mode, "a") == 0)
{
flag = O_CREAT | O_WRONLY | O_APPEND;
fd = open(path, flag, 0666);
}
else if(strcmp(mode, "r") == 0)
{
flag = O_RDWR;
fd = open(path, flag);
}
else
{
//TODO
}
if(fd < 0) return NULL;
return BuyFile(fd, flag);
}
void MyFclose(MyFile *file)
{
if(file->fileno < 0) return;
MyFFlush(file);
close(file->fileno);
free(file);
}
int MyFwrite(MyFile *file, void *str, int len)
{
// 1. 拷贝
memcpy(file->outbuffer+file->bufferlen, str, len);
file->bufferlen += len;
// 2. 尝试判断是否满足刷新条件!
if((file->flush_method & LINE_FLUSH) && file->outbuffer[file->bufferlen-1] == '\n')
{
MyFFlush(file);
}
return 0;
}
void MyFFlush(MyFile *file)
{
if(file->bufferlen <= 0) return;
// 把数据从用户拷贝到内核文件缓冲区中
int n = write(file->fileno, file->outbuffer, file->bufferlen);
(void)n;
fsync(file->fileno);
file->bufferlen = 0;
}
mystring.h:
cpp
#pragma once
int my_strlen(const char *s);
mystring.c:
cpp
#include "mystring.h"
int my_strlen(const char *s)
{
const char *start = s;
while(*s)
{
s++;
}
return s - start;
}
2.1 静态库生成
- ar 是 gnu 归档工具, rc 表示 (replace and create)。.a静态库,本质是一种归档文件,不需要使用者解包,而用gcc/g++直接进行链接即可!
bash
$ ar -tv libmystdio.a
rw-rw-r-- 1000/1000 2848 Oct 29 14:35 2024 my_stdio.o
rw-rw-r-- 1000/1000 1272 Oct 29 14:35 2024 my_string.o
- t:列出静态库中的文件
- v:verbose 详细信息
作为库的制作者,以下为简单静态库的制作示例:
bash
ztl@hcss-ecs-c91e:~/linux/26_0518$ ll
total 28
drwxrwxr-x 2 ztl ztl 4096 May 18 13:50 ./
drwxrwxr-x 22 ztl ztl 4096 May 18 13:39 ../
-rw-rw-r-- 1 ztl ztl 1700 May 18 13:49 mystdio.c
-rw-rw-r-- 1 ztl ztl 409 May 18 13:48 mystdio.h
-rw-rw-r-- 1 ztl ztl 144 May 18 13:50 mystring.c
-rw-rw-r-- 1 ztl ztl 44 May 18 13:49 mystring.h
ztl@hcss-ecs-c91e:~/linux/26_0518$ vim Makefile
bash
//vim Makefile:
libmyc.a:mystdio.o mystring.o
ar -rc $@ $^
mystdio.o:mystdio.c
gcc -c $<
mystring.o:mystring.c
gcc -c $<
.PHONY:output
output:
mkdir -p lib/include
mkdir -p lib/mylib
cp -f *.h lib/include
cp -f *.a lib/mylib
tar czf lib.tgz lib
.PHONY:clean
clean:
rm -rf *.o libmyc.a lib lib.tgz
bash
ztl@hcss-ecs-c91e:~/linux/26_0518$ make
gcc -c mystdio.c
gcc -c mystring.c
ar -rc libmyc.a mystdio.o mystring.o
ztl@hcss-ecs-c91e:~/linux/26_0518$ ll
total 44
drwxrwxr-x 2 ztl ztl 4096 May 18 15:31 ./
drwxrwxr-x 22 ztl ztl 4096 May 18 13:39 ../
-rw-rw-r-- 1 ztl ztl 4658 May 18 15:31 libmyc.a
-rw-rw-r-- 1 ztl ztl 292 May 18 15:31 Makefile
-rw-rw-r-- 1 ztl ztl 1700 May 18 13:49 mystdio.c
-rw-rw-r-- 1 ztl ztl 409 May 18 13:48 mystdio.h
-rw-rw-r-- 1 ztl ztl 3136 May 18 15:31 mystdio.o
-rw-rw-r-- 1 ztl ztl 144 May 18 13:50 mystring.c
-rw-rw-r-- 1 ztl ztl 44 May 18 13:49 mystring.h
-rw-rw-r-- 1 ztl ztl 1264 May 18 15:31 mystring.o
ztl@hcss-ecs-c91e:~/linux/26_0518$ make output
mkdir -p lib/include
mkdir -p lib/mylib
cp -f *.h lib/include
cp -f *.a lib/mylib
tar czf lib.tgz lib
ztl@hcss-ecs-c91e:~/linux/26_0518$ ll
total 52
drwxrwxr-x 3 ztl ztl 4096 May 18 15:31 ./
drwxrwxr-x 22 ztl ztl 4096 May 18 13:39 ../
drwxrwxr-x 4 ztl ztl 4096 May 18 15:31 lib/
-rw-rw-r-- 1 ztl ztl 4658 May 18 15:31 libmyc.a
-rw-rw-r-- 1 ztl ztl 1790 May 18 15:31 lib.tgz
-rw-rw-r-- 1 ztl ztl 292 May 18 15:31 Makefile
-rw-rw-r-- 1 ztl ztl 1700 May 18 13:49 mystdio.c
-rw-rw-r-- 1 ztl ztl 409 May 18 13:48 mystdio.h
-rw-rw-r-- 1 ztl ztl 3136 May 18 15:31 mystdio.o
-rw-rw-r-- 1 ztl ztl 144 May 18 13:50 mystring.c
-rw-rw-r-- 1 ztl ztl 44 May 18 13:49 mystring.h
-rw-rw-r-- 1 ztl ztl 1264 May 18 15:31 mystring.o
2.2 静态库使用
bash
// 任意⽬录下,新建
// usercode.c,引⼊库头⽂件
#include "mystdio.h"
#include "mystring.h"
#include <stdio.h>
int main()
{
const char *s = "abcdefg";
printf("%s: %d\n", s, my_strlen(s));
mFILE *fp = mfopen("./log.txt", "a");
if(fp == NULL) return 1;
mfwrite(s, my_strlen(s), fp);
mfwrite(s, my_strlen(s), fp);
mfwrite(s, my_strlen(s), fp);
mfclose(fp);
return 0;
}
// 场景1:头⽂件和库⽂件安装到系统路径下
$ gcc main.c -lmyc
// 场景2:头⽂件和库⽂件和我们⾃⼰的源⽂件在同⼀个路径下
$ gcc main.c -L. -lmyc
// 场景3:头⽂件和库⽂件有⾃⼰的独⽴路径
$ gcc main.c -I头⽂件路径 -L库⽂件路径 -lmyc
示例:
bash
ztl@hcss-ecs-c91e:~/linux$ ls
26_0111 26_0313 26_0410 26_0421 26_0507 26_0511 26_0515 lesson16 lesson18 lesson20 test.c zhangsan
26_0112 26_0316 26_0411 26_0504 26_0508 26_0514 26_0518 lesson17 lesson19 test1.c test.cc
ztl@hcss-ecs-c91e:~/linux$ cd zhangsan
ztl@hcss-ecs-c91e:~/linux/zhangsan$ cp ../26_0518/lib.tgz . //模拟zhangsan用户拿到静态库
ztl@hcss-ecs-c91e:~/linux/zhangsan$ ll //后的使用示范
total 12
drwxrwxr-x 2 ztl ztl 4096 May 18 16:02 ./
drwxrwxr-x 23 ztl ztl 4096 May 18 16:00 ../
-rw-rw-r-- 1 ztl ztl 1790 May 18 16:02 lib.tgz
ztl@hcss-ecs-c91e:~/linux/zhangsan$ vim usercode.c
ztl@hcss-ecs-c91e:~/linux/zhangsan$ ll
total 16
drwxrwxr-x 2 ztl ztl 4096 May 18 16:04 ./
drwxrwxr-x 23 ztl ztl 4096 May 18 16:00 ../
-rw-rw-r-- 1 ztl ztl 1790 May 18 16:02 lib.tgz
-rw-rw-r-- 1 ztl ztl 586 May 18 16:04 usercode.c
ztl@hcss-ecs-c91e:~/linux/zhangsan$ tar xzf lib.tgz //解压缩包
ztl@hcss-ecs-c91e:~/linux/zhangsan$ ll
total 20
drwxrwxr-x 3 ztl ztl 4096 May 18 16:05 ./
drwxrwxr-x 23 ztl ztl 4096 May 18 16:00 ../
drwxrwxr-x 4 ztl ztl 4096 May 18 15:31 lib/
-rw-rw-r-- 1 ztl ztl 1790 May 18 16:02 lib.tgz
-rw-rw-r-- 1 ztl ztl 586 May 18 16:04 usercode.c
ztl@hcss-ecs-c91e:~/linux/zhangsan$ gcc usercode.c -I ./lib/include/ -L ./lib/mylib/ -l myc
ztl@hcss-ecs-c91e:~/linux/zhangsan$ ll
total 40
drwxrwxr-x 3 ztl ztl 4096 May 18 16:06 ./
drwxrwxr-x 23 ztl ztl 4096 May 18 16:00 ../
-rwxrwxr-x 1 ztl ztl 16736 May 18 16:06 a.out*
drwxrwxr-x 4 ztl ztl 4096 May 18 15:31 lib/
-rw-rw-r-- 1 ztl ztl 1790 May 18 16:02 lib.tgz
-rw-rw-r-- 1 ztl ztl 586 May 18 16:04 usercode.c
ztl@hcss-ecs-c91e:~/linux/zhangsan$ ./a.out
buffer: hello myfile!!!
buffer: hello myfile!!!
buffer: hello myfile!!!
buffer: hello myfile!!!
buffer: hello myfile!!!
3. 动态库
- 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码
- ⼀个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的⼀个表,而不是外部函数所在目标文件的整个机器码
- 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中, 这个过程称为动态链接(dynamic linking)
- 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的⼀份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间
3.1 动态库生成
bash
//Makefile
libmyc.so:mystdio.o mystring.o
gcc -shared -o $@ $^
mystdio.o:mystdio.c
gcc -fPIC -c $<
mystring.o:mystring.c
gcc -fPIC -c $<
.PHONY:output
output:
mkdir -p lib/include
mkdir -p lib/mylib
cp -f *.h lib/include
cp -f *.so lib/mylib
tar czf lib.tgz lib
.PHONY:clean
clean:
rm -rf *.o libmyc.so lib lib.tgz
- shared: 表示生成共享库格式
- fPIC:产生位置无关码(position independent code)
- 库名规则:libxxx.so
示例:
bash
ztl@hcss-ecs-c91e:~/linux/26_0518$ ls
Makefile mystdio.c mystdio.h mystring.c mystring.h
ztl@hcss-ecs-c91e:~/linux/26_0518$ vim Makefile
ztl@hcss-ecs-c91e:~/linux/26_0518$ make
gcc -fPIC -c mystdio.c
gcc -fPIC -c mystring.c
gcc -shared -o libmyc.so mystdio.o mystring.o
ztl@hcss-ecs-c91e:~/linux/26_0518$ ls
libmyc.so Makefile mystdio.c mystdio.h mystdio.o mystring.c mystring.h mystring.o
ztl@hcss-ecs-c91e:~/linux/26_0518$ make output
mkdir -p lib/include
mkdir -p lib/mylib
cp -f *.h lib/include
cp -f *.so lib/mylib
tar czf lib.tgz lib
ztl@hcss-ecs-c91e:~/linux/26_0518$ ls
lib libmyc.so lib.tgz Makefile mystdio.c mystdio.h mystdio.o mystring.c mystring.h mystring.o
3.2 动态库使用
bash
// 场景1:头⽂件和库⽂件安装到系统路径下
$ gcc main.c -lmyc
// 场景2:头⽂件和库⽂件和我们⾃⼰的源⽂件在同⼀个路径下
$ gcc main.c -L. -lmyc // 从左到右搜索-L指定的⽬录
// 场景3:头⽂件和库⽂件有⾃⼰的独⽴路径
$ gcc main.c -I头⽂件路径 -L库⽂件路径 -lmyc
$ ldd libmystdio.so // 查看库或者可执⾏程序的依赖
linux-vdso.so.1 => (0x00007fffacbbf000)
libc.so.6 => /lib64/libc.so.6 (0x00007f8917335000)
/lib64/ld-linux-x86-64.so.2 (0x00007f8917905000)
示例:
bash
ztl@hcss-ecs-c91e:~/linux/zhangsan$ ll
total 12
drwxrwxr-x 2 ztl ztl 4096 May 18 16:48 ./
drwxrwxr-x 23 ztl ztl 4096 May 18 16:00 ../
-rw-rw-r-- 1 ztl ztl 586 May 18 16:04 usercode.c
ztl@hcss-ecs-c91e:~/linux/zhangsan$ cp ../26_0518/lib.tgz .
ztl@hcss-ecs-c91e:~/linux/zhangsan$ ls
lib.tgz usercode.c
ztl@hcss-ecs-c91e:~/linux/zhangsan$ gcc -o usercode usercode.c -I lib/include/ -L lib/mylib/ -lmyc
ztl@hcss-ecs-c91e:~/linux/zhangsan$ ls
lib lib.tgz usercode usercode.c
ztl@hcss-ecs-c91e:~/linux/zhangsan$ ./usercode
./usercode: error while loading shared libraries: libmyc.so: cannot open shared object file: No such file or directory
ztl@hcss-ecs-c91e:~/linux/zhangsan$ ldd usercode
linux-vdso.so.1 (0x00007fff07cf0000)
libmyc.so => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007b7d8b400000)
/lib64/ld-linux-x86-64.so.2 (0x00007b7d8b77c000)
3.3 库运行搜索路径
3.3.1 问题:
在上面运行usercode文件时会发现如下问题:
bash
ztl@hcss-ecs-c91e:~/linux/zhangsan$ ./usercode
./usercode: error while loading shared libraries: libmyc.so: cannot open shared object file: No such file or directory
ztl@hcss-ecs-c91e:~/linux/zhangsan$ ldd usercode
linux-vdso.so.1 (0x00007fff07cf0000)
libmyc.so => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007b7d8b400000)
/lib64/ld-linux-x86-64.so.2 (0x00007b7d8b77c000)
在上述的操作中,不是明明已经说明了要在哪个路径下找库吗,为什么还是会找不到libmyc.so。
原来在动态库的操作中,上述操作只是告诉了gcc去哪找库,而当我们的可执行程序准备运行的时候,该程序所依赖的库是需要被系统知道的。系统不等于gcc,所以会出错。
3.3.2 解决方案:
- 拷贝 .so 文件到系统共享库路径下, ⼀般指 /usr/lib、/usr/local/lib、/lib64 或者开篇指明的库路径等
- 向系统共享库路径下建立同名软链接
- 更改环境变量: LD_LIBRARY_PATH (OS在运行程序时,要查找动态库,也会在该环境变量下查找动态库,注意其经常为空)
- ldconfig方案:配置/ etc/ld.so.conf.d/ ,ldconfig更新
示例:
bash
ztl@hcss-ecs-c91e:~/linux/zhangsan$ pwd
/home/ztl/linux/zhangsan
ztl@hcss-ecs-c91e:~/linux/zhangsan$ export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/home/ztl/linux/zhangsan/lib/mylib
ztl@hcss-ecs-c91e:~/linux/zhangsan$ echo ${LD_LIBRARY_PATH}
:/home/ztl/linux/zhangsan/lib/mylib:/home/ztl/linux/zhangsan/lib/mylib
ztl@hcss-ecs-c91e:~/linux/zhangsan$ ldd usercode
linux-vdso.so.1 (0x00007ffc21195000)
libmyc.so => /home/ztl/linux/zhangsan/lib/mylib/libmyc.so (0x0000788eecc60000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x0000788eeca00000)
/lib64/ld-linux-x86-64.so.2 (0x0000788eecc6c000)
ztl@hcss-ecs-c91e:~/linux/zhangsan$ ./usercode
buffer: hello myfile!!!
buffer: hello myfile!!!
buffer: hello myfile!!!