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

基于腾讯云HAI + DeepSeek快速设计自己的个人网页

基于腾讯云HAI + DeepSeek快速设计自己的个人网页

前言:通过结合腾讯云HAI 强大的云端运算能力与DeepSeek先进的 AI技术,本文介绍高效、便捷且低成本的设计一个自己的个人网页。你将了解到如何轻松绕过常见的技术阻碍,在腾讯云HAI平台上快速部署DeepSeek模型,仅需简单几步,就能获取一个包含个人简介、技能特长、项目经历及联系方式等核心板块的响应式网页。 目录 一、DeepSeek模型部署在腾讯云HAI 二、设计个人网页 一、DeepSeek模型部署在腾讯云HAI 把 DeepSeek 模型部署于腾讯云 HAI,用户便能避开官网访问限制,直接依托腾讯云 HAI 的超强算力运行 DeepSeek-R1 等模型。这一举措不仅降低了技术门槛,还缩短了部署时间,削减了成本。尤为关键的是,凭借 HAI 平台灵活且可扩展的特性,用户能够依据自身特定需求定制专属解决方案,进而更出色地适配特定业务场景,满足各类技术要求 。 点击访问腾讯云HAI控制台地址: 算力管理 - 高性能应用服务 - 控制台 腾讯云高性能应用服务HAI已支持DeepSeek-R1模型预装环境和CPU算力,只需简单的几步就能调用DeepSeek - R1

By Ne0inhk
AI革命先锋:DeepSeek与蓝耘通义万相2.1的无缝融合引领行业智能化变革

AI革命先锋:DeepSeek与蓝耘通义万相2.1的无缝融合引领行业智能化变革

云边有个稻草人-ZEEKLOG博客 目录 引言 一、什么是DeepSeek? 1.1 DeepSeek平台概述 1.2 DeepSeek的核心功能与技术 二、蓝耘通义万相2.1概述 2.1 蓝耘科技简介 2.2 蓝耘通义万相2.1的功能与优势 1. 全链条智能化解决方案 2. 强大的数据处理能力 3. 高效的模型训练与优化 4. 自动化推理与部署 5. 行业专用解决方案 三、蓝耘通义万相2.1与DeepSeek的对比分析 3.1 核心区别 3.2 结合使用的优势 四、蓝耘注册流程 五、DeepSeek与蓝耘通义万相2.1的集成应用 5.1 集成应用场景 1. 智能医疗诊断

By Ne0inhk
如何通过 3 个简单步骤在 Windows 上本地运行 DeepSeek

如何通过 3 个简单步骤在 Windows 上本地运行 DeepSeek

它是免费的——社区驱动的人工智能💪。         当 OpenAI 第一次推出定制 GPT 时,我就明白会有越来越多的人为人工智能做出贡献,并且迟早它会完全由社区驱动。         但从来没有想过它会如此接近😂让我们看看如何在 Windows 机器上完全免费使用第一个开源推理模型!  步骤 0:安装 Docker 桌面         我确信很多人已经安装了它,所以可以跳过,但如果没有 — — 这很简单,只需访问Docker 的官方网站,下载并运行安装 👍         如果您需要一些特定的设置,例如使用 WSL,那么有很多指导视频,请查看!我将继续下一步。 步骤 1:安装 CUDA 以获得 GPU 支持         如果您想使用 Nvidia 显卡运行 LLM,则必须安装 CUDA 驱动程序。(嗯……是的,它们需要大量的计算能力)         打开CUDA 下载页面,

By Ne0inhk
在 VSCode 中本地运行 DeepSeek,打造强大的私人 AI

在 VSCode 中本地运行 DeepSeek,打造强大的私人 AI

本文将分步向您展示如何在本地安装和运行 DeepSeek、使用 CodeGPT 对其进行配置以及开始利用 AI 来增强您的软件开发工作流程,所有这些都无需依赖基于云的服务。  步骤 1:在 VSCode 中安装 Ollama 和 CodeGPT         要在本地运行 DeepSeek,我们首先需要安装Ollama,它允许我们在我们的机器上运行 LLM,以及CodeGPT,它是集成这些模型以提供编码辅助的 VSCode 扩展。 安装 Ollama Ollama 是一个轻量级平台,可以轻松运行本地 LLM。 下载Ollama 访问官方网站:https://ollama.com * 下载适合您的操作系统(Windows、macOS 或 Linux)的安装程序。 * 验证安装 安装后,打开终端并运行: ollama --version  如果 Ollama 安装正确,

By Ne0inhk