hello,再次见面,结果上一次我们的努力,张三与学校的其他校友成功完成了老师的作业,但是此时我们也想到了另一种技术--制作动态库!!
1.动态库的定义
动态库(.so):程序在运⾏的时候才去链接动态库的代码,多个程序共享使⽤库的代码
⼀个与动态库链接的可执⾏⽂件仅仅包含它⽤到的函数⼊⼝地址的⼀个表,⽽不是外部函数所在⽬
标⽂件的整个机器码在可执⾏⽂件开始运⾏以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中, 这个过程称为动态链接(dynamic linking)
动态库可以在多个程序间共享,所以动态链接使得可执⾏⽂件更⼩,节省了磁盘空间。操作系统采
⽤虚拟内存机制允许物理内存中的⼀份动态库被要⽤到该库的所有进程共⽤,节省了内存和磁盘空
间
2.制作一个动态库
我们借用一下前面的静态库代码

lib
Makefile
bash
libmyc.so : mystdio.o mystring.o
gcc -shared -o $@ $^
mystdio.o : mystdio.c
gcc -fPIC -c $^
mystring.o : mystring.c
gcc -fPIC -c $^
.PHONY : clean
clean :
rm -rf *.o *.so *.tgz lib
.PHONY : output
output :
mkdir ./lib
mkdir ./lib/include
mkdir ./lib/mylib
cp *.h ./lib/include
cp *.so ./lib/mylib
tar czf lib.tgz lib
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, "r") == 0)
{
flag = O_RDONLY;
fd = open(path, flag);
}
else if (strcmp(mode, "a") == 0)
{
flag = O_CREAT | O_WRONLY | O_APPEND;
fd = open(path, flag, 0666);
}
else
{
//TODO
}
if (fd < 0)
{
return NULL;
}
return BuyFile(fd, flag);
}
void MyFclose(MyFile* file)
{
MyFFlush(file);
}
int MyFwrite(MyFile* file, void* str, int len)
{
//拷贝
memcpy(file->outbuffer + file->bufferlen, str, len);
file->bufferlen += len;
//行刷新
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;
file->bufferlen = 0;
}
mystring.h
cpp
#pragma once
int mystrlen(const char* str);
mystring.c
cpp
#include "mystring.h"
int mystrlen(const char* str)
{
const char* start = str;
while (*str)
{
str++;
}
return str - start;
}
zhangsan
code.c
cpp
#include "mystdio.h"
#include "mystring.h"
#include <string.h>
#include <unistd.h>
int main()
{
MyFile* filep = MyFopen("./log.txt", "a");
if (!filep)
{
perror("filep open\n");
return 1;
}
//char* message = (char*)"hello my_libc\n"; //带/n行刷新
char* message = (char*)"hello my_libc!!!"; //不带就是全刷新
//MyFwrite(filep, message, strlen(message));
int cnt = 10;
while (cnt--)
{
MyFwrite(filep, message, strlen(message));
printf("filep->outbuffer : %s\n", filep->outbuffer);
sleep(1);
}
MyFclose(filep);
const char* str = "hello world!\n";
printf("strlen : %d\n", mystrlen(str));
return 0;
}
代码分析
和静态库类似,我们只需要.o文件即可(这里的.o不是一般的.o),但是静态库打包需要用到ar -rc指令,而动态库打包只需要gcc即可
具体指令
gcc -shared -o libmyc.so *.o
但是需要注意的是,这里的.o文件是需要加上fPIC进行编译的
gcc -fPIC -c *.c
命令行参数含义
shared: 表示⽣成共享库格式
fPIC:产⽣位置⽆关码(position independent code)(现在不讲,之后博客再将这个概念)
库名规则:libxxx.so
3.zhangsan运行动态库
此时张三接收到了来自我们的动态库,并且解包

我们用静态库的方法来尝试进行运行

明明我们都已经生成了code可执行文件,为什么在运行code的时候还是会显示没找到.so文件呢??我们输入命令ldd code来看看

确实没有找到!
这是因为尽管我们生成了code可执行文件,但是因为是动态链接的,所以执行的时候系统需要知道我们的libmyc.so在哪里才行,为什么静态库不需要呢,因为静态库在生成code的时候,是直接将所有库信息拷贝了一份给code,所以不需要让系统去找,那我们怎么让系统知道呢??
1.拷⻉ .so ⽂件到系统共享库路径下, ⼀般指 /usr/lib、/usr/local/lib、/lib64的库路径等

2.向系统共享库路径下建⽴同名软连接
3.更改环境变量: LD_LIBRARY_PATH
