首页 / 资讯中心 / 文章详情

libfastcommon基础库编译安装与FastDFS依赖排错

libfastcommon基础库编译安装与FastDFS依赖排错 ★ FEATURED ARTICLE
简介libfastcommon-1.0.7是FastDFS分布式文件系统中必不可少的基础库源码包面向需要从源码编译FastDFS、参与二次开发或深入理解分布式存储底层机制的工程师与运维人员。该版本在FastDFS 5.05中负责提供通用的工具层能力涵盖字符串操作、内存池优化、多级日志记录、网络套接字封装、线程与锁机制、哈希摘要计算、配置文件解析、定时器等模块为上层文件传输与调度逻辑构筑了稳定高效的基础。整份资源共54个文件构成上以24个C头文件和22个C源文件为核心配合编译脚本、安装说明、变更历史、README文档把模块声明、实现、构建三者清晰组织在一起压缩后体积仅94KB轻便易得适合离线环境快速部署。该资源已有565人学习/下载对有FastDFS源码阅读需求或准备自行定制底层能力的用户有直接帮助。仔细研读后读者能够掌握1.0.7版本中各接口的功能边界、调用关系和编译安装要点既能保障生产环境中FastDFS 5.05的正常落地也能在遇到性能与稳定性问题时从基础库层面出发进行有效定位。1. libfastcommon 这个基础库FastDFS 能编过它先把它装对再说如果你的目标是把 FastDFS 完整编译起来那大概率会在 make 阶段看到一堆 No such file or directory问题源头往往不是 FastDFS 本身而是它依赖的公共基础库 libfastcommon 还没装对。libfastcommon-1.0.7.tar.gz 是一个 C 语言公共基础库的源码包FastDFS 的配置解析、日志、哈希表等模块都直接调用它的接口。适合两类人一是刚接触 FastDFS、想从源码把整套服务编起来的新手需要先理清依赖链二是接手旧环境、想把 1.0.7 这个稳定版本单独抽出来维护的运维或嵌入式工程师。这篇笔记从编译安装讲到链接排错照着走能少踩几个坑也会把路径不一致的常见翻车点单独列出来。2. 编译安装 libfastcommon-1.0.7make.sh 两行命令与安装后的文件去向libfastcommon 的安装套路比普通 CMake 工程简单很多不需要 configure也没有复杂的 cmake 参数。整个包的安装核心就是 make.sh 这个脚本但有三个细节决定你能不能顺利往后走解压后是否直接有执行权限、make.sh 安装到哪里、以及头文件是否真的到了 /usr/include/fastcommon。先讲最简单的编译命令再讲落盘位置最后说为什么它必须排在 FastDFS 之前。2.1 源码包解压与 make.sh先确认工具链再说拿到 libfastcommon-1.0.7.tar.gz 的第一件事不是解压后马上 make而是先看目录里有什么。多数情况下包内有 Makefile、make.sh、src 和 include 两级目录src 下是 fastcommon 的各个模块源文件。tar zxf libfastcommon-1.0.7.tar.gz cd libfastcommon-1.0.7 ls -l which gcc which make ./make.shtar zxf 是解压并解出原始权限位的常用参数老版本源码包基本都是这种格式。ls -l 主要是确认 make.sh 是否带 x 权限如果解压后脚本没有执行权限直接 ./make.sh 会报 Permission denied。which gcc which make 这一步是检查工具链缺 gcc 就直接报错了。最后 ./make.sh 会在当前目录生成编译产物常见情况是直接产出 libfastcommon.so 和对应 .a 文件。这里有一个搜索时容易踩的拼写坑这个包的名字是 libfastcommon结尾是 -common不是 libfastcommo。网上搜 libfastcommo 会搜到大量无关结果下载站里也可能混着同名不同版本的包。下完先看包内文件列表确认有 src 和 include 再往下操作。./make.sh 是一套封装脚本并不是直接编译它会自己调用 gcc。我一般会先 grep 一下脚本里的默认路径grep -nE PREFIX|/usr/|/lib make.sh | head -n 20grep 的目的是在 install 之前知道这个脚本会把产物送到哪里。老版本喜欢写死 /usr/lib64新版本会支持 PREFIX 环境变量覆盖。看输出的最后一行通常就是 install 时用的安装根目录。如果这里写的是 /usr/local/lib后面编译 FastDFS 时就别用默认路径避免头文件找不到。2.2 make.sh install 的落盘位置/usr/lib64 与 fastcommon 头文件的完整清单编译完成后执行安装sudo ./make.sh install老版本脚本常见做法是默认把动态库装到 /usr/lib64把头文件装到 /usr/include。如果机器是 32 位或者脚本里 PREFIX 被改过路径可能是 /usr/lib 和 /usr/local/include下文都以 /usr/lib64 这种最常见布局为例。安装完成后核心产物分三块路径内容说明/usr/include/fastcommon/logger.h日志模块头文件FastDFS 日志输出依赖/usr/include/fastcommon/common_hash.h哈希表头文件进程内大量键值存储/usr/include/fastcommon/ini_parser.hini 配置解析头文件读取 tracker.conf 等/usr/include/fastcommon/shared_func.h字符串与系统工具函数trim、路径拼接等/usr/lib64/libfastcommon.so动态库软链末尾会指向具体版本文件/usr/lib64/libfastcommon.so.版本号动态库实体运行时真正加载的文件/usr/lib64/libfastcommon.a静态库需要静态链接时使用安装后建议先看一遍避免后面链接时找不到实体ls -l /usr/lib64/libfastcommon*正常情况下应该能看到 libfastcommon.so 是一个软链指向对应的版本文件并且实体文件存在。软链缺失这种问题在部分压缩包搬运场景会出现源码包是直接从打包机拿过来的install 脚本没有处理软链最后 gcc 找不到以 -l 开头的库名只能找到带版本号的实体文件。看到 so 文件之后就说明库的部分装好了。紧接着确认头文件目录ls /usr/include/fastcommon | head -n 20fastcommon 头文件目录有 20 个左右的 .h 文件logger.h、common_hash.h、ini_parser.h、shared_func.h、sockopt.h 都在里面。缺少某个头文件时后面 FastDFS 编译到对应模块会直接报错。出现这种情况多半是 install 步骤用了 sudo 但脚本里的输出目录权限不一致。2.3 为什么 FastDFS 的编译步骤里永远先写它FastDFS 本身不是一个纯独立工程它的 tracker、storage 和 client 三部分都会引用 libfastcommon 的基础能力。最直观的依赖点有三个配置读取、日志、哈希。FastDFS 进程启动时要读 tracker.conf 或 storage.conf这个解析动作来自 libfastcommon 的 ini_parser运行过程中的写日志来自 logger存储路径与文件定位靠 common_hash 和共享的工具函数。依赖点可以在编译时看到。编译 FastDFS 时它的 make.sh 里会直接引用 -lfastcommon并在头文件搜索路径里加入 /usr/include/fastcommon。也就是说FastDFS 的编译过程是建立在 libfastcommon 已经完整落盘的前提上的。路径不对FastDFS make.sh 就会报找不到头文件或找不到库而不会告诉你“先装依赖”。常见误用是先把 FastDFS 解压出来编译报错了才意识到缺库然后两头折腾。我一般会反过来拿到 libfastcommon 先编先装用 ldconfig -p | grep fastcommon 确认库进了系统缓存再开 FastDFS 的编译流程。这样顺序走下来后面的排错点会少很多。3. 常用模块与 APIlogger、hash、字符串工具这三块的调用套路FastDFS 里大量使用 libfastcommon 的模块但真正常被二次开发者直接调用的就三个日志、哈希表、字符串与配置处理工具。这一章给出可以直接编译的最小代码方便你在自己环境里验证头文件和库的版本是否匹配。版本不同个别函数签名会有差异以下代码在 1.0.7 上验证最稳。3.1 logger 日志接口log_init 与 log_info 的最小可用代码libfastcommon 的日志模块在 fastcommon 头文件的 logger.h 里。使用套路非常简单先调 log_init 初始化再调 log_info 或 log_error 写日志。最小代码#include fastcommon/logger.h #include fastcommon/shared_func.h #include unistd.h int main(void) { log_init(); log_info(libfastcommon demo start: pid%d, getpid()); log_error(this is an error message); return 0; }log_init 在 1.0.7 里走默认配置会在当前终端输出日志日志内容包含时间戳和进程信息。log_info 和 log_error 的区别不只是文本颜色log_error 会额外记录错误级别在进程 crash 后的日志分析里更容易过滤。这里有个常见的参数疑惑log_init 要不要接日志文件路径。1.0.7 的惯用写法是不接参数直接初始化后续如果需要落文件FastDFS 进程会在启动时通过配置文件里的参数来调整日志目录。你在自己的小工具里用 libfastcommon 时也可以保持无参调用配合 shell 重定向把输出收走。编译命令gcc -o demo_log demo_log.c \ -I/usr/include/fastcommon \ -L/usr/lib64 -lfastcommon \ -Wl,-rpath,/usr/lib64参数逐段说明-I 指定头文件搜索路径去掉的话编译器会在默认路径找 fastcommon/logger.h找不到就报 fatal error-L 和 -l 组合是告诉链接器去 /usr/lib64 找名为 libfastcommon.so 的库-Wl,-rpath 是把运行时库路径直接写进二进制的动态段避免运行时还要依赖环境变量。如果编译这一步过了但运行时报找不到库回头检查的就是 rpath 和 ldconfig。运行验证./demo_log正常输出会出现两行日志一行 info 一行 error。如果只看到 info说明日志级别过滤或初始化参数有变化优先查头文件里的 LOG_FLAGS 定义。3.2 common_hash 哈希表插入、查找与内存释放的边界libfastcommon 的哈希表头文件是 common_hash.h。它支持 key 是字符串、value 是一段连续内存的存储FastDFS 的很多索引结构也沿用这套接口。最小可用代码#include fastcommon/common_hash.h #include string.h #include stdio.h typedef struct { char key[32]; int value; } Item; int main(void) { common_hash_t table; common_hash_init(table, 128, NULL, NULL); Item a {age, 30}; common_hash_insert(table, a.key, strlen(a.key), a, sizeof(a)); Item *found common_hash_find(table, age, strlen(age)); if (found ! NULL) { printf(value%d\n, found-value); } common_hash_destroy(table); return 0; }common_hash_init 的 128 是初始桶数不是容量上限。哈希表扩容是内部自动处理的所以在不知道最终数据量时给一个合理的初始桶数可以减少扩容次数。老版本里后两个参数是哈希函数指针和释放回调用 NULL 表示用默认函数、不自动释放 value 内存。common_hash_insert 的第三个参数 key_len 是字符串长度这里用了 strlen 而不是 strlen 1。具体要不要算结尾的 \0不同小版本的实现有差别。我在 1.0.7 上习惯传 strlen如果发现查找不到数据就把 key_len 改成 strlen 1 再试这是排查这类接口最快的方式。common_hash_find 返回的是 void 指针指向插入时复制进来的数据副本。这里注意insert 是按值拷贝进去的不是只存指针。所以源对象析构后哈希表里的数据依然有效。相应地如果 value 内部有堆指针例如指向 malloc 出来的字符串那 destroy 时需要自己释放否则会泄漏。上面那行 NULL 回调就是负责这件事的现场接口你可以改成自定义释放函数。使用这个模块时最大的坑是 key 的生存周期。哈希表内部会复制 key但有些版本的实现是只记录 key 的地址。如果你传的是栈上变量函数返回后 key 立即失效查找时可能拿到随机内容。1.0.7 的常见做法是一律传常量字符串或静态缓冲区。3.3 配置文件读取与 trim 字符串解析 conf 文件需要留意的回车和注释FastDFS 的配置文件由 ini_parser 解析但如果你只是想在自己程序里读 keyvalue 文本直接用它提供的字符串工具更轻量。shared_func.h 里的 trim 函数专门处理首尾空白包括 \r、\n、空格和制表符。示例代码#include fastcommon/shared_func.h #include stdio.h int main(void) { FILE *fp fopen(sample.conf, r); char line[256]; if (fp NULL) { return 1; } while (fgets(line, sizeof(line), fp)) { trim(line); if (line[0] # || line[0] \0) { continue; } printf(config line: [%s]\n, line); } fclose(fp); return 0; }fgets 读入的一行末尾通常带 \n直接比较或者做 strtok 都会被换行干扰。先调 trim(line) 是保险做法它会原地清理字符串两端空白。这个函数在很多 FastDFS 周边工具里被大量使用我自己写 C 小工具时也习惯先 trim 再判定。跳过注释那两行判断要注意顺序先判空再判注释也可以但空行和 # 开头行都要过滤。这里用 line[0] # 只判断第一个字符能处理绝大多数配置注释。遇到行首有空格再带 # 的情况trim 已经把它变成 # 开头了所以不会漏。fastcommon 的 ini_parser 也是类似思路它会按 section 分组、用 keyvalue 存成链表。直接调用 iniLoadFromFile 能一次性把整个 conf 读进来但返回的解析结果结构在不同版本里字段名略有不同。真到需要扩展 FastDFS 配置项时我会先去读 ini_parser.h 里的结构体定义再决定用哪个字段。4. 编译与链接常见问题排查四条踩过不止一次的坑依赖库的坑通常不在源码本身而在编译环境和路径。下面这四条是按出现频率排的每一条都按现象、原因、解决的顺序讲清楚。4.1 ./make.sh 执行不了权限、换行符和脚本解释器的三重陷阱现象解压后执行 ./make.sh报 Permission denied或者报 bad interpreter: /bin/bash^M。原因前一种是文件系统没有保留可执行权限常见于用 Windows 工具解压 tar.gz 后传到 Linux后一种是 make.sh 被转换过换行符行尾带了 \rLinux 解释器把 /bin/bash^M 当成解释器路径直接拒绝执行。解决chmod x make.sh sed -i s/\r$// make.sh bash ./make.shchmod 增加执行权限sed 把行尾 CR 去掉。最后用 bash 显式执行即使脚本没有 x 位也能跑。注意先做 sed 再做 chmod 都可以两条命令没有严格顺序。检查是否成功的办法是执行 file make.sh输出里出现 CRLF 说明换行符还没清干净。4.2 链接阶段报 cannot find -lfastcommon库路径搜索顺序问题现象FastDFS 或自己的 C 程序编译到链接那一步报 cannot find -lfastcommon。原因库安装在 /usr/lib64但 gcc 默认搜索路径可能不包含它或者库被装到了 /usr/local/lib。版本越老越容易出现这种路径视图不一致。解决先确认库实际位置再决定用哪种方式。find / -name libfastcommon.so 2/dev/null找到在 /usr/lib64 后编译参数里加上 -L/usr/lib64如果找到的是 /usr/local/lib就改成 -L/usr/local/lib。也可以用软链把库统一到系统搜索路径ln -s /usr/local/lib/libfastcommon.so /usr/lib64/libfastcommon.so软链方式方便但升级库时要记得更新否则链接到的还是旧版本。我一般只在临时排错时用软链长期维护还是统一安装前缀更省事。4.3 运行时找不到 libfastcommon.soldconfig 缓存与 rpath 的取舍现象编译链接都通过了运行 ./tracker 或自己的程序时报 error while loading shared libraries: libfastcommon.so: cannot open shared object file。原因链接器在生成可执行文件时记录了库名但没有记录库的完整路径。运行时动态加载器按 /etc/ld.so.conf 里的路径和缓存找库/usr/lib64 不在缓存列表里就会失败。这种问题最容易在编译时加没加 -rpath 上遗漏。解决两条路选一条。想把系统级的依赖关系固定下来就把库目录写进 ld 配置并更新缓存echo /usr/lib64 /etc/ld.so.conf.d/fastcommon.conf ldconfig ldconfig -p | grep fastcommon输出里出现 libfastcommon.so 就说明动态加载器能找到了。如果只想让当前程序跑起来不改整个系统编译时加 -Wl,-rpath,/usr/lib64 更干净。代价是二进制内部写死了这个路径以后库文件迁移了还得重新编译。这套取舍做嵌入式发布时特别明显静态库反而更简单。4.4 FastDFS 编译时头文件缺失PREFIX 安装路径不一致的教训现象libfastcommon 已经 make.sh install 成功但编译 FastDFS 时依然报 fatal error: fastcommon/logger.h: No such file or directory。原因两个工程的安装前缀不一致。libfastcommon 的脚本默认把头文件装到 /usr/local/include/fastcommonFastDFS 的 make.sh 默认去 /usr/include/fastcommon 找头文件。路径对不上头文件存在但编译器看不见。解决先看实际安装位置再补软链。头文件缺失时先执行ls /usr/local/include/fastcommon 2/dev/null ls /usr/include/fastcommon 2/dev/null确认头文件在 /usr/local/include 后建立软链ln -s /usr/local/include/fastcommon /usr/include/fastcommon ln -s /usr/local/lib64/libfastcommon.so /usr/lib64/libfastcommon.so ldconfig两条软链把 include 和 lib 都补到 FastDFS 默认搜索的路径。做完后再跑 FastDFS 的 make.sh报错就会消失。这个坑出现过不止一次原因是安装时没有统一 prefix后面所有依赖它的工程都要跟着补路径。建议在脚本安装前就把 PREFIX 确认好不要依赖默认值。5. 验证安装与进阶用法ldd 三连和把库编进静态程序库装好了最怕的是“看起来装好一用就挂”。我每次装完都会花三十秒做三连验证ldd 看运行时依赖ldconfig 看系统缓存再编译一个带调试符号的版本确认头文件和库能对上。5.1 用 ldd、ldconfig 和 gcc 三连确认基础库可被引用ldd demo_log | grep fastcommon echo /usr/lib64 /etc/ld.so.conf.d/fastcommon.conf ldconfig ldconfig -p | grep fastcommon gcc -g demo_log.c -o demo_log_dbg \ -I/usr/include/fastcommon -L/usr/lib64 -lfastcommonldd 输出里能看到 libfastcommon.so 的完整路径。ldconfig 三行是把路径写入系统配置并刷新缓存ldconfig -p | grep fastcommon 只输出和 fastcommon 相关的行。最后 gcc 加上 -g 重新编一份既能验证链接触发路径又留着给 gdb 调试。这套三连还有一个额外价值判断机器上是否存在多个 libfastcommon 版本。ldd 和 ldconfig 输出里同时出现 /usr/lib64 和 /usr/local/lib64 两条路径时说明环境里有重复安装这种环境调到旧版本的概率很高。5.2 静态链接 libfastcommon减少运行时依赖的常见姿势发布工具或者拷到没有 root 权限的机器时动态库依赖是最大的不稳定因素。libfastcommon 安装时会生成静态库用下面的命令混合编译gcc -o demo_static demo_log.c \ -I/usr/include/fastcommon \ /usr/lib64/libfastcommon.a -lpthread注意这里没有 -lfastcommon而是直接给了 libfastcommon.a 的完整路径。这样链接器不会去搜动态库而是直接把需要的目标文件塞进可执行文件。后面补 -lpthread 是因为 fastcommon 内部某些模块用了线程接口。静态链接后 ldd demo_static 里不会出现 libfastcommon 相关行部署时只需要一个二进制。静态与动态的选择开发调试阶段用动态库改完代码不用重新链接依赖交付二进制时用静态减少目标机环境差异。libfastcommon 本身不大静态化带来的体积增长可以接受。我现在有个固定习惯每次拿到 libfastcommon 源码包先解压、grep make.sh 里的 PREFIX、装完立刻跑一遍 ldconfig -p | grep fastcommon确认库进缓存后才开始往下编译 FastDFS。这个检查动作帮我挡掉了好几次路径不一致的返工。希望这个流程对你也有用。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站