开发规范之动态库(使用与容易遗漏的部分)

一、为什么拷贝到/usr/lib必须加sudo

1. 核心原因:/usr/lib是系统保护目录,普通用户没有写入权限

Linux 权限机制:

  • /usr/下面属于系统核心目录,存放系统自带软件、基础库、系统程序;
  • 默认只有root 超级用户才能新建、删除、修改里面的文件;
  • 你当前登录的普通用户,执行cp libxxx.so /usr/lib会直接报错:Permission denied(权限拒绝)

sudo=superuser do,临时借用 root 管理员权限执行这一条拷贝命令,改完自动收回权限,防止普通用户乱删系统文件破坏系统。

2. 光sudo cp还不够,必须再加一步

sudo cp libadd.so /usr/lib sudo ldconfig

Linux 不会每次运行程序遍历所有文件夹找库,而是靠缓存文件/etc/ld.so.cache快速查找;ldconfig就是刷新这个系统动态库缓存,把新放进/usr/lib的 so 录入系统索引,不然程序依旧提示找不到库。

二、那为什么非要丢进/usr/lib?根源是动态库运行时查找规则

静态库编译链接时直接打包进程序,运行不需要原.a文件;动态库只在编译时记录库名,程序运行时才去硬盘搜索.so文件,搜索优先级顺序:

  1. 编译时-Wl,-rpath硬编码路径
  2. 环境变量LD_LIBRARY_PATH
  3. /etc/ld.so.cache系统缓存(包含/usr/lib/lib
  4. 兜底默认路径:/lib/usr/lib

把 so 放到/usr/lib,就是放进系统默认兜底搜索目录,任何终端、任何用户运行程序,系统都能自动找到库,不用额外配环境变量。

三、完全可以不用 sudo、不用往系统目录拷贝(三种更常用方案)

方案 1:临时环境变量(开发最常用,不用 sudo)

so 还在当前文件夹,执行:

export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH ./a.out

仅当前终端窗口生效,关闭终端就失效,适合调试。

方案 2:编译时指定 rpath,永久绑定库路径

编译 main 时直接写死 so 所在文件夹:

gcc main.c -L. -ladd -Wl,-rpath=.

生成的可执行文件自带查找路径,以后直接./a.out就能运行,全程不需要 sudo、不用移动 so 文件

方案 3:添加自定义路径到系统缓存(推荐正规安装)

  1. 新建文件夹放库:sudo mkdir /usr/local/mylib
  2. 复制 so 进去:cp libadd.so /usr/local/mylib(不用 sudo 也能写自定义目录)
  3. 把路径写入系统配置:
echo "/usr/local/mylib" | sudo tee /etc/ld.so.conf.d/mylib.conf sudo ldconfig

后续全局可用,也不会污染原生/usr/lib系统目录。

四、动静库在头文件、声明那套规则完全一致

  1. .so只包含机器码 + 符号,绝不打包.h 头文件
  2. 库源码add.c#include "add.h"只是作者自检,防止声明实现不一致;
  3. 使用者可以丢弃原头文件,自己新建任意命名.h,只要函数签名匹配就能编译调用;
  4. 结构体、宏、typedef 必须依赖作者提供的.h,否则无法使用。

极简总结

  1. sudo只是为了突破系统目录写入权限限制,不是动态库本身强制要求;
  2. 放进/usr/lib只是为了让系统默认路径能搜到 so,属于部署方式,不是唯一用法;
  3. 日常写代码优先用LD_LIBRARY_PATH或者-rpath,尽量少往/usr/lib塞自定义库,避免系统文件杂乱。