引言 - 也许是修行
很久以前写过关于动态库科普文章, 废话反正是说了好多. 核心就是在 linux 上面玩了一下 dlopen : )
linux动态库编译和使用详细剖析 - https://www.cnblogs.com/life2refuel/p/5332358.html
本文是上面文章的补充部分. 因为单纯的 linux 玩还是不太通用 ~
动态库最简单理解是为了解决操作系统级别的代码复用出现的技术. 现在服务器开发技术中,
几乎不再出现. 首先不好用, 其次多环境中常容易出错. 繁荣期应该在上古时代(2000-2005), 动态库技术
是一个很考验程序员的修养的基本功. (当前服务器主流是静态库, 客户端应该还是被动态库统治) 这里不妨扯一些
Windows 和 Unix 下动态链接库的区别 https://blog.codingnow.com/2006/11/windows_unix_dynamic_library.html
(没想到, 当年云风, 也会被上古的 winds 老前辈们 摩擦摩擦 ~.~ )
想深入了解动态库原理, 可以多看几遍 <<程序员自我修养>> and <<高级C/C++ 编译技术>> : )
虽然看完没什么暖用. 但也可以解闷(特别是后面那本小册子)不是吗 ?
那开始代码之旅, 不来虚的 ~
前言 - 准备测试环境
动态库当你面试时候碰到的话, 实在答不上上来. 就别继续说了概念了. 就简单说我'不会', 但我会写会用的很溜.
也许能到 61 分吧. 把这篇文章代码手打出来 : ) 咱们就玩实心的.
先看编译模块 Makefile
main.exe: gcc -fPIC -O2 -Wall -shared -o foo.dll foo.c gcc -g -Wall -O2 -o main.exe main.c dllso.c -ldl clean: -rm -rf *.o *.so *.exe *.out *.dll
待编译成动态库的文件 foo.h foo.c
#ifndef _F_FOO #define _F_FOO #include <stdio.h> #ifndef extern # if defined(_MSC_VER) # define extern extern __declspec(dllexport) # else # define extern extern # endif #endif//extern extern void * foo(int hoge); #undef extern #endif//_F_FOO
这里构建的 extern 宏, 是不是很飘. 用于解决 cl 和 gcc 对于动态库导出约束不一样.
cl 默认没有 __declspec(dllexport) 就不导出. gcc 默认全部导出, __attribute__((visibility("hidden"))) 可以设置不可见.
#include "foo.h" void * foo(int hoge) { static int _id; ++_id; // 简单自增长 printf("foo(%d) = %d\n", hoge, _id); return &_id; }
实现没有什么好说的.
其中动态库协助接口设计 dllso.h
#ifndef _H_DLLSO #define _H_DLLSO // // ds_create - 构造加载动态库文件 // path : 动态库文件路径 // return : 失败返回 NULL // extern void * ds_create(const char * path); // // ds_parse - 解析动态库文件, 返回执行函数 // so : 动态库对象 // name : 待解析的函数名称 // return : 返回解析的函数地址, NULL 是失败 // extern void * ds_parse(void * so, const char * name); // // ds_delete - 释放卸载动态库文件 // so : 动态库对象 // return : void // extern void ds_delete(void * so); #endif//_H_DLLSO
最终测试文件 main.c
#include "dllso.h" #include <stdio.h> #include <stdlib.h> #define _STR_FOO "./foo.dll" // 简单动态库测试 int main(int argc, char * argv[]) { void * so = ds_create(_STR_FOO); if (NULL == so) { fprintf(stderr, "ds_create %s err", _STR_FOO); exit(EXIT_FAILURE); } void * (* foo)(int) = ds_parse(so, "foo"); if (NULL == foo) { fprintf(stderr, "ds_parse err so = %p\n", so); exit(EXIT_FAILURE); } printf("foo() = %p\n", foo(0)); ds_delete(so); return EXIT_SUCCESS; }
到这里希望读者理解作者的思路. 动态库处理划分为三部分, 装载, 解析, 卸载.
这里扯淡一点, 对于动态库设计处理. winds 离不开 LoadLibrary 函数簇. linux 离不开 dlopen 函数簇.(自行科普)
当前测试核心思路就在 dllso.c
#include "dllso.h" #if defined(_MSC_VER) # include <windows.h> #define RTLD_LAZY LOAD_WITH_ALTERED_SEARCH_PATH #define dlopen(filename, flags) LoadLibraryEx(filename, NULL, flags) #define dlsym(handle, symbol) GetProcAddress(handle, symbol) #define dlclose(handle) FreeLibrary(handle) #else # include <dlfcn.h> #endif // // ds_create - 构造加载动态库文件 // path : 动态库文件路径 // return : 失败返回 NULL // inline void * ds_create(const char * path) { return dlopen(path, RTLD_LAZY); }