Linux 动静态库完全指南:制作、使用、原理与实战

Linux 动静态库完全指南:制作、使用、原理与实战
在这里插入图片描述

🔥草莓熊Lotso:个人主页
❄️个人专栏: 《C++知识分享》《Linux 入门到实践:零基础也能懂》
✨生活是默默的坚持,毅力是永久的享受!


🎬 博主简介:

在这里插入图片描述

文章目录


前言:

在 Linux 开发中,库是代码复用的核心载体 —— 无论是 C 标准库的printf,还是自定义的工具函数,都可以封装为库供多个项目调用。库分为静态库(.a)和动态库(.so),二者在编译链接方式、内存占用、更新维护等方面差异显著,掌握其制作、使用与底层原理,是 Linux 开发的必备技能。本文从库的基础概念入手,详解动静态库的制作流程、使用场景,结合 ELF 文件格式解析底层原理,最后附上外部库(ncurses)的实战案例,帮你彻底打通 Linux 库开发的全链路。

一. 库的基础认知:是什么?有哪些?

1.1 库的本质

库是编译后的二进制文件,包含可复用的代码和数据,本质是 “提前写好、经过验证的成熟代码”。其核心价值在于:

  • 避免重复开发:无需从零实现基础功能(如字符串处理、文件 IO);
  • 简化项目管理:将复杂功能拆分到库中,降低主项目复杂度;
  • 隐藏实现细节:只暴露接口,保护核心逻辑。
在这里插入图片描述

1.2 库的分类与系统位置

Linux 下库分为两类,命名和存储路径有明确规范:

类型后缀 (Linux)后缀 (Windows)系统默认路径核心特征
静态库.a.lib/lib, /usr/lib, /usr/local/lib编译时链接,可执行程序独立运行
动态库.so.dll/lib64, /usr/lib64, /usr/local/lib64运行时链接,多程序共享

系统库示例(Ubuntu/CentOS)

# Ubuntu查看C标准库ls-l /lib/x86_64-linux-gnu/libc-2.31.so # 动态库ls-l /lib/x86_64-linux-gnu/libc.a # 静态库# Ubuntu查看C++标准库ls /usr/lib/gcc/x86_64-linux-gnu/9/libstdc++.so -l# 动态库ls /usr/lib/gcc/x86_64-linux-gnu/9/libstdc++.a # 静态库# CentOS查看C标准库ls /lib64/libc-2.17.so -l# 动态库ls /lib64/libc.a -l# 静态库# CentOS查看C++标准库ls-l /lib64/libstdc++.so.6 # 动态库(软链接到实际版本)ls-l /usr/lib/gcc/x86_64-redhat-linux/4.8.2/libstdc++.a # 静态库

1.3 预备工作:自定义库源码

后续动静态库制作将基于以下自定义源码(模拟文件 IO 和字符串工具库):
(1)文件 IO 库(my_stdio.h/my_stdio.c)

// my_stdio.h#pragmaonce #defineSIZE1024// 定义为 1,2,4方便使用 & 操作, 都只有一个比特位为 1#defineFLUSH_NONE1#defineFLUSH_LINE2#defineFLUSH_FULL4typedefstruct{int fileno;// 文件描述符int flags;int fstrategy;// 刷新策略char outbuffer[SIZE];int cap;// 容量int size;//char inbuffer[1024];}My_FILE; My_FILE*Myfopen(constchar* pathname,constchar* mode);// r w aintMyfwrite(constchar* message,int size,int num, My_FILE* fp);voidMyfflush(My_FILE* fp);voidMyfclose(My_FILE* fp);
// my_stdio.c#include"my_stdio.h"#include<stdio.h>#include<string.h>#include<stdlib.h>#include<sys/types.h>#include<sys/stat.h>#include<unistd.h>#include<fcntl.h>staticmode_t fmode =0666;// 文件权限 My_FILE*Myfopen(constchar* pathname,constchar* mode)// r w a{if(pathname ==NULL|| mode ==NULL)returnNULL;umask(0);int fd =0;int flags =0;if(strcmp(mode,"w")==0){ flags = O_CREAT | O_WRONLY | O_TRUNC; fd =open(pathname, flags, fmode);(void) fd;}if(strcmp(mode,"r")==0){ flags = O_RDONLY; fd =open(pathname, flags);(void) fd;}if(strcmp(mode,"a")==0){ flags = O_CREAT | O_WRONLY | O_APPEND; fd =open(pathname, flags, fmode);(void) fd;}else{}if(fd <0)returnNULL;// 创建 My_FILE对象 My_FILE* fp =(My_FILE*)malloc(sizeof(My_FILE));if(!fp)returnNULL; fp->fileno = fd; fp->flags = flags; fp->fstrategy = FLUSH_LINE; fp->outbuffer[0]='\0';// memset(fp->outbuffer, 0, sizeof(fp->outbuffer)); fp->cap = SIZE; fp->size =0;return fp;}voidMyfflush(My_FILE* fp){if(!fp)return;if(fp->size >0){// 写到内核文件的文件缓冲区中write(fp->fileno, fp->outbuffer, fp->size);// 刷新到外设fsync(fp->fileno); fp->size =0;}}intMyfwrite(constchar* message,int size,int num, My_FILE* fp){if(message ==NULL|| fp ==NULL)return0;// C语言向文件写入实际上是向缓冲区写入int sizeNum = size * num;if(fp->size + sizeNum < fp->cap -1)// 预留\0的位置{memcpy(fp->outbuffer + fp->size, message, sizeNum); fp->size += sizeNum; fp->outbuffer[fp->size]=0;}// 刷新缓冲区条件: 不是每次都刷新的,这样也可以加快响应速度if(fp->size >0&& fp->outbuffer[fp->size -1]=='\n'&&(fp->fstrategy & FLUSH_LINE)){Myfflush(fp);}return sizeNum;}voidMyfclose(My_FILE* fp){if(fp->size >0){Myfflush(fp);}close(fp->fileno);}

(2)字符串库(my_string.h/my_string.c)

// my_string.h#pragmaonceintmy_strlen(constchar* s);
// my_string.c#include"my_string.h"intmy_strlen(constchar* s){constchar* end = s;while(*end !='\0') end++;return end - s;}

总结与引入

在这里插入图片描述

二. 静态库:编译时链接,独立运行

静态库(.a)的核心特征是 “编译链接时,将库代码完整拷贝到可执行程序中”,生成的可执行程序不依赖外部库,可独立运行。

2.1 整体图示:理清思路

我们可以先看看这个图示的流程再来往下详细学习

在这里插入图片描述

2.2 静态库制作流程(Makefile 自动化,更简便)

静态库通过ar(GNU 归档工具)制作,核心步骤:编译源码生成.o 文件 → 归档.o 文件为.a 静态库。

  • 编写 Makefile:
target=libmyc.a src=$(wildcard *.c)obj=$(src:.c=.o)cc=gcc -car=ar -rc$(target):$(obj)$(ar)$@ $^ # 编译.o文件(只编译不链接) %.o:%.c $(cc) $<# 输出库文件(模拟安装到系统目录结构) .PHONY:output output: @mkdir -p myc/lib @mkdir -p myc/include @cp *.h myc/include @cp *.a myc/lib @tar czf myc.tgz myc .PHONY:clean clean: rm-rf *.o $(target) myc myc.tgz debug: @echo $(target) @echo $(src) @echo $(obj)
  • 后续操作如下图所示:
在这里插入图片描述

2.3 静态库使用场景与命令

静态库使用需指定 “头文件路径、库文件路径、库名”,核心命令格式(上面的使用过程中也体现了):

gcc 源文件.c -I头文件路径 -L库文件路径 -l库名 [-static]
  • -I:指定头文件搜索路径(默认搜索 /usr/include 等系统目录);
  • -L:指定库文件搜索路径(默认搜索 /lib 等系统目录);
  • -l:指定库名(需去掉前缀lib和后缀.a,如libmyc.a → -l myc);
  • -static:强制链接静态库(优先使用静态库,无静态库则报错)。

场景 1:头文件 / 库文件与源文件同目录

# 编译(同目录下可省略-I) gcc main.c -lmystdio-L.-static

场景 2:头文件 / 库文件在独立路径

# 假设库文件在 ./stdc/lib,头文件在 ./stdc/include gcc main.c -I./stdc/include -L./stdc/lib -lmystdio-static

场景 3:安装到系统目录(全局可用)

# 拷贝头文件到系统目录sudocp *.h /usr/include/ # 拷贝静态库到系统目录sudocp libmystdio.a /usr/lib/ # 直接编译(无需指定-I和-L,但是 -l 一定还是必须的) gcc main.c -lmystdio-static

2.4 静态库核心特点

  • 优点:可执行程序独立运行,不依赖外部库;运行时无需加载库,启动速度快;
  • 缺点:可执行程序体积大(包含库代码);库更新后需重新编译链接;多个程序使用会重复占用磁盘和内存。

三. 动态库:运行时链接,共享复用

动态库(.so)的核心特征是 “编译时仅记录库依赖,运行时才加载库代码”,多个程序可共享同一库文件,节省磁盘和内存空间。

3.1 动态库制作流程(Makefile 自动化)

动态库制作需生成 “位置无关码(PIC)”,核心步骤:编译 PIC 目标文件 → 链接为共享库。

  • 编写 Makefile:
target=libmyc.so src=$(wildcard *.c)obj=$(src:.c=.o)cc=gcc $(target):$(obj)$(cc)-shared-o$@ $^ # 编译PIC目标文件(位置无关码,支持任意地址加载) %.o:%.c $(cc)-fPIC-c $< .PHONY:output output: @mkdir -p myc/lib @mkdir -p myc/include @cp *.h myc/include @cp *.so myc/lib @tar czf myc.tgz myc .PHONY:clean clean: rm-rf *.o $(target) myc myc.tgz debug: @echo $(target) @echo $(src) @echo $(obj)

后续操作如下图所示:

在这里插入图片描述

3.2 动态库使用:编译与运行时依赖

动态库编译命令与静态库类似,但运行时需确保系统能找到动态库(否则报错 “libmystdio.so not found”)。

  • 步骤 1:编译(同静态库命令,无需 - static)
# 同目录编译 gcc main.c -L. -lmystdio# 独立路径编译 gcc main.c -I./stdc/include -L./stdc/lib -lmystdio
  • 步骤 2:解决运行时库搜索路径
    动态库运行时搜索路径优先级:
    • 编译时指定的-rpath(嵌入可执行程序);
    • 环境变量LD_LIBRARY_PATH
    • 系统配置文件/etc/ld.so.conf.d/(需执行ldconfig生效);
    • 系统默认路径(/lib64、/usr/lib64)。

解决方案(任选其一,看图示吧)

在这里插入图片描述


在这里插入图片描述

3.3 动态库核心特点

  • 优点:可执行程序体积小;库更新后无需重新编译(替换.so 文件即可);多个程序共享库代码,节省资源;
  • 缺点:运行时依赖动态库,缺失会导致程序无法启动;启动时需加载库,速度略慢于静态库。

四. 动静态库对比与选型建议

对比维度静态库 (.a)动态库 (.so)
链接时机编译、链接阶段程序运行阶段
可执行程序体积大(库代码被复制进去)小(仅记录依赖信息)
运行依赖无需外部文件,独立运行必须依赖对应的.so文件
库更新需重新编译、链接整个程序直接替换.so文件即可生效
内存占用多个程序重复占用内存多个程序共享内存中的同一份代码
编译速度快(链接后无需额外处理)略慢(需处理动态链接信息)
适用场景小程序、嵌入式(追求无依赖)大型项目、多程序共享(节省资源)

选型建议:

  • 若程序需独立部署(如嵌入式设备),选静态库;
  • 若多个程序共用同一功能(如公司内部工具库),选动态库;
  • 若库更新频繁(如业务逻辑迭代快),选动态库;
  • 若追求启动速度和稳定性,选静态库。

相关问题:

在这里插入图片描述

五. 实战:使用外部库(ncurses 图形库)

除了自定义库,Linux 系统提供大量现成外部库,以 ncurses(终端图形库)为例,演示外部库的安装与使用。

5.1 安装 ncurses 库

# CentOSsudo yum install-y ncurses-devel # Ubuntusudoaptinstall-y libncurses-dev 

5.2. 编写测试代码(大家可以自己试试别的)

#include <ncurses.h>#include <stdlib.h>#include <time.h>#include <unistd.h>#define MAX_DATA 30#define HEIGHT 20#define WIDTH 60 int main(){ int data[MAX_DATA]={0}; int index =0; // 初始化ncurses initscr(); cbreak(); noecho(); curs_set(0); nodelay(stdscr, TRUE); // 非阻塞输入 keypad(stdscr, TRUE); // 初始化颜色(如果终端支持) if(has_colors()){ start_color(); init_pair(1, COLOR_GREEN, COLOR_BLACK); init_pair(2, COLOR_YELLOW, COLOR_BLACK); init_pair(3, COLOR_RED, COLOR_BLACK); init_pair(4, COLOR_CYAN, COLOR_BLACK);} // 生成初始随机数据 srand(time(NULL));for(int i =0; i < MAX_DATA; i++){ data[i]= rand() % (HEIGHT - 2) + 1;} // 主循环 int ch;while((ch = getch())!='q'){ clear(); // 绘制边框和标题 if(has_colors()) attron(COLOR_PAIR(4)); box(stdscr, 0, 0); mvprintw(0, 2, " CPU Usage Monitor (Press 'q' to quit) ");if(has_colors()) attroff(COLOR_PAIR(4)); // 绘制坐标轴 mvaddch(HEIGHT, 1, ACS_LTEE);for(int i =0; i < WIDTH - 2; i++){ mvaddch(HEIGHT, i + 2, ACS_HLINE);} mvaddch(HEIGHT, WIDTH - 1, ACS_RTEE); // 绘制Y轴刻度 mvprintw(1, 1, "100%%"); mvprintw(HEIGHT/2, 1, " 50%%"); mvprintw(HEIGHT-1, 1, " 0%%"); // 更新数据(模拟实时变化) data[index]= rand() % (HEIGHT - 2) + 1; index =(index + 1) % MAX_DATA; // 绘制数据点并连线 for(int i =0; i < MAX_DATA; i++){ int x =(i * (WIDTH - 4)) / MAX_DATA + 5; int y = HEIGHT - data[(index + i) % MAX_DATA]; // 根据数值选择颜色 if(has_colors()){if(data[(index + i) % MAX_DATA]>(HEIGHT * 2) / 3) attron(COLOR_PAIR(3));elseif(data[(index + i) % MAX_DATA]> HEIGHT / 3) attron(COLOR_PAIR(2));else attron(COLOR_PAIR(1));} // 绘制点 mvaddch(y, x, '*'); // 绘制连线(如果下一个点存在) if(i < MAX_DATA - 1){ int next_x =((i +1)*(WIDTH -4)) / MAX_DATA + 5; int next_y = HEIGHT - data[(index + i + 1) % MAX_DATA]; // 简单的连线算法 int dx = next_x - x; int dy = next_y - y; int steps = abs(dx)> abs(dy) ? abs(dx): abs(dy);for(int s =1; s < steps; s++){ int inter_x = x + (dx * s) / steps; int inter_y = y + (dy * s) / steps;if(inter_y >=1&& inter_y < HEIGHT){ mvaddch(inter_y, inter_x, '.');}}}if(has_colors()) attroff(COLOR_PAIR(1)| COLOR_PAIR(2)| COLOR_PAIR(3));} // 显示当前数值 mvprintw(HEIGHT + 1, 2, "Current: %3d%% Average: %3d%%", (data[index] * 100) / HEIGHT, ((HEIGHT - (HEIGHT - data[index]) / 2) * 100) / HEIGHT); refresh(); usleep(300000); // 每0.3秒更新一次 } endwin();return0;}
# 编译(-lncurses指定链接ncurses库) gcc test.c -otest-lncurses# 运行(终端中显示动态进度条) ./test 
在这里插入图片描述

结尾:

🍓 我是草莓熊 Lotso!若这篇技术干货帮你打通了学习中的卡点: 👀 【关注】跟我一起深耕技术领域,从基础到进阶,见证每一次成长 ❤️ 【点赞】让优质内容被更多人看见,让知识传递更有力量 ⭐ 【收藏】把核心知识点、实战技巧存好,需要时直接查、随时用 💬 【评论】分享你的经验或疑问(比如曾踩过的技术坑?),一起交流避坑 🗳️ 【投票】用你的选择助力社区内容方向,告诉大家哪个技术点最该重点拆解 技术之路难免有困惑,但同行的人会让前进更有方向~愿我们都能在自己专注的领域里,一步步靠近心中的技术目标! 

结语:动静态库是 Linux 开发中代码复用的核心手段,掌握其制作、使用与选型,能显著提升开发效率和项目质量。静态库适合独立部署场景,动态库适合资源共享场景,实际开发中需根据需求灵活选择。本文从基础概念到实战案例,覆盖了库开发的核心流程,后续可进一步学习库的版本管理、符号隐藏、动态加载(dlopen/dlsym)等高级特性

✨把这些内容吃透超牛的!放松下吧✨ʕ˘ᴥ˘ʔづきらど

Read more

华为OD机试双机位C卷-部门人力分配 (Py/Java/C/C++/Js/Go)

华为OD机试双机位C卷-部门人力分配 (Py/Java/C/C++/Js/Go)

部门人力分配 华为OD机试双机位C卷 - 华为OD上机考试双机位C卷 100分题型 华为OD机试双机位C卷真题目录点击查看: 华为OD机试双机位C卷真题题库目录|机考题库 + 算法考点详解 题目描述 部门在进行需求开发时需要进行人力安排。 当前部门需要完成 N 个需求,需求用 requirements 表述,requirements[i] 表示第 i 个需求的工作量大小,单位:人月。 这部分需求需要在 M 个月内完成开发,进行人力安排后每个月人力时固定的。 目前要求每个月最多有2个需求开发,并且每个月需要完成的需求不能超过部门人力。 请帮助部门评估在满足需求开发进度的情况下,每个月需要的最小人力是多少? 输入描述 输入为 M 和 requirements,M 表示需求开发时间要求,requirements 表示每个需求工作量大小,N 为 requirements长度, * 1 ≤ N/2 ≤ M ≤ N ≤ 10000

By Ne0inhk
AI 的智能体专栏:手把手教你用豆包打造专属 Python 智能管家,轻松解决编程难题

AI 的智能体专栏:手把手教你用豆包打造专属 Python 智能管家,轻松解决编程难题

AI 的智能体专栏:手把手教你用豆包打造专属 Python 智能管家,轻松解决编程难题 AI 的智能体专栏:手把手教你用豆包打造专属 Python 智能管家,轻松解决编程难题,本文介绍了如何利用豆包平台打造专属Python智能管家。首先简述豆包平台的核心优势,接着说明创建前的准备工作,包括注册账号、明确定位和收集训练资料。随后详细讲解创建流程,从新建智能体、基础设置、能力配置到测试优化,还提及集成代码执行环境等高级功能扩展,以及使用技巧与实际应用案例。该智能官能解决多种Python编程问题,可提升学习效率和问题解决速度,是实用的个性化编程助手。 前言     人工智能学习合集专栏是 AI 学习者的实用工具。它像一个全面的 AI 知识库,把提示词设计、AI 创作、智能绘图等多个细分领域的知识整合起来。无论你是刚接触 AI 的新手,还是有一定基础想提升的人,都能在这里找到合适的内容。从最基础的工具操作方法,到背后深层的技术原理,专栏都有讲解,还搭配了实例教程和实战案例。这些内容能帮助学习者一步步搭建完整的 AI 知识体系,让大家快速从入门进步到精通,

By Ne0inhk
Python反爬虫硬核技术:绕过风控、签名加密、分布式爬取实战(企业级落地版)

Python反爬虫硬核技术:绕过风控、签名加密、分布式爬取实战(企业级落地版)

过去7年主导电商、金融、短视频领域的企业级爬虫项目,踩过的风控坑涵盖从基础IP封禁到高级设备指纹识别,破解过的签名加密算法包括MD5/HMAC/SM4/protobuf,搭建的分布式爬虫集群支撑日均亿级数据采集。本文聚焦反爬虫硬核技术落地:从风控绕过的底层逻辑,到签名加密的逆向实战,再到分布式爬取的工程化实现,所有代码均经过生产环境验证,可直接复用。 一、核心认知:风控系统的底层检测逻辑 要绕过风控,先搞懂风控系统“看什么”——企业级风控不是单一检测,而是多维度特征建模,核心检测维度如下: 检测维度风控特征绕过核心思路网络层IP频率、IP地域、代理特征、请求来源高匿代理池+IP画像模拟+请求频率控制协议层User-Agent、Cookie、请求头完整性、签名参数真实请求头池+Cookie池+签名加密还原行为层点击间隔、滑动轨迹、页面停留时间、操作序列人类行为模拟+随机化操作+轨迹噪声设备层浏览器指纹、设备ID、JS环境特征指纹伪造+环境模拟+

By Ne0inhk
Python从0到100(九十八):融合选择性卷积与残差结构的SKResNet架构详解

Python从0到100(九十八):融合选择性卷积与残差结构的SKResNet架构详解

前言:零基础学Python:Python从0到100最新最全教程。 想做这件事情很久了,这次我更新了自己所写过的所有博客,汇集成了Python从0到100,共一百节课,帮助大家一个月时间里从零基础到学习Python基础语法、Python爬虫、Web开发、 计算机视觉、机器学习、神经网络以及人工智能相关知识,成为学业升学和工作就业的先行者! 【优惠信息】 • 新专栏订阅前500名享9.9元优惠 • 订阅量破500后价格上涨至19.9元 • 订阅本专栏可免费加入粉丝福利群,享受: - 所有问题解答 -专属福利领取 欢迎大家订阅专栏:零基础学Python:Python从0到100最新最全教程! 本文目录: * 一、SKResNet的理论基础与创新点 * 1. 传统卷积神经网络的局限性 * 2. SKResNet的核心创新 * 3. 技术优势分析 * 二、SKResNet架构设计详解 * 1. 整体架构概览 * 2. SKBlock:选择核模块详解 * 2.1 多尺度卷积核设计 * 2.2 注意力机制实现 * 2.

By Ne0inhk