在多线程编程中,读者 - 写者问题是经典的同步互斥场景。本文将演示如何将读者 - 写者模型封装为可复用的静态链接库(.a)和动态链接库(.so),并通过工具命令分析两种库的差异,帮助理解链接机制的底层逻辑。

一、环境与代码准备

1. 环境

  • 系统:Linux(本文基于 openEuler)
  • 工具:gcc(编译器)、ar(静态库打包)、readelf(ELF 分析)、ldd(动态依赖查看)、size(段大小分析)

2. 代码拆分

为实现库封装,将代码拆分为 3 个文件(分离接口与实现):

  • rw_lib.h:库接口声明(结构体、函数原型)

    #ifndef RW_LIB_H
    #define RW_LIB_H
    #include <pthread.h>
    #include <semaphore.h>
    
    #define MAX_THREADS 5
    #define MAX_OPERATIONS 3
    
    // 共享资源控制块
    typedef struct {
        sem_t rw_mutex;    // 读写互斥锁
        sem_t mutex;       // 读者计数保护锁
        int read_count;    // 当前读者数量
    } RWControl;
    
    // 线程参数
    typedef struct {
        int id;
        RWControl* rw_ctrl;
    } ThreadArgs;
    
    // 接口函数
    void rw_init(RWControl* rw_ctrl);
    void rw_destroy(RWControl* rw_ctrl);
    void* reader_thread(void* arg);
    void* writer_thread(void* arg);
    #endif
    
  • rw_lib.c:库功能实现(读者 / 写者逻辑)

    #include "rw_lib.h"
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    void rw_init(RWControl* rw_ctrl) {
        sem_init(&rw_ctrl->rw_mutex, 0, 1);
        sem_init(&rw_ctrl->mutex, 0, 1);
        rw_ctrl->read_count = 0;
    }
    
    void rw_destroy(RWControl* rw_ctrl) {
        sem_destroy(&rw_ctrl->rw_mutex);
        sem_destroy(&rw_ctrl->mutex);
    }
    
    // 读者线程实现(略,见完整逻辑)
    void* reader_thread(void* arg) { /* ... */ }
    
    // 写者线程实现(略,见完整逻辑)
    void* writer_thread(void* arg) { /* ... */ }
    
  • main.c:测试程序(调用库接口)

    #include "rw_lib.h"
    #include <stdio.h>
    #include <pthread.h>
    #include <time.h>
    
    int main() {
        srand(time(NULL));
        RWControl rw_ctrl;
        rw_init(&rw_ctrl);
    
        pthread_t readers[MAX_THREADS], writers[MAX_THREADS];
        ThreadArgs r_args[MAX_THREADS], w_args[MAX_THREADS];
    
        // 创建读者/写者线程(略)
        // 等待线程结束并销毁资源(略)
        return 0;
    }
    

二、生成静态链接库(.a)及分析

静态库是目标文件的归档包,编译时会被完整嵌入可执行文件,运行时不依赖外部文件。

1. 生成静态库步骤

# 1. 编译库实现为目标文件
gcc -c rw_lib.c -o rw_lib.o

# 2. 用ar打包为静态库(命名规则:libxxx.a)
ar rcs librw.a rw_lib.o

# 3. 链接静态库生成可执行程序
gcc main.c -o rw_static -L. -lrw -lpthread
  • -L.:指定当前目录为库搜索路径;-lrw:链接librw.a-lpthread:链接系统线程库。

2. 运行与验证

# 运行静态链接程序
./rw_static

输出示例:

[读者] 1号开始读取
[写者] 1号开始书写
...(读写过程)
所有线程结束

3. 用size查看段大小

size rw_static

输出示例:

   text    data     bss     dec     hex filename
  16466     640     288   17394    4432 rw_static
  • text(代码段):包含库和主程序的所有指令(最大,因静态库代码已嵌入)。
  • data/bss:数据段(初始化 / 未初始化的全局变量)。

三、生成动态链接库(.so)及分析

动态库(共享库)编译时仅记录引用,运行时才加载,可被多个程序共享,节省内存。

1. 生成动态库

# 1. 编译位置无关目标文件(-fPIC确保地址无关)
gcc -c -fPIC rw_lib.c -o rw_lib_so.o

# 2. 生成动态库(命名规则:libxxx.so)
gcc -shared rw_lib_so.o -o librw.so

# 3. 链接动态库生成可执行程序
gcc main.c -o rw_dynamic -L. -lrw -lpthread

2. 运行与验证

动态库需指定搜索路径(临时设置环境变量):

# 设置动态库路径
export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH

# 运行动态链接程序
./rw_dynamic

输出与静态程序一致(逻辑相同,仅链接方式不同)。

3. 用ldd查看动态依赖

ldd rw_dynamic

输出示例:

linux-vdso.so.1 
librw.so => ./librw.so  # 依赖自定义动态库
libpthread.so.0 => /lib64/libpthread.so.0  # 系统线程库
libc.so.6 => /lib64/libc.so.6  # 系统C库

明确显示程序依赖librw.so及系统库。

四、静态库与动态库的 ELF 格式差异(readelf分析)

ELF(Executable and Linkable Format)是 Linux 可执行文件 / 库的标准格式,通过readelf可观察两种链接方式的本质区别。

1. 静态链接程序(rw_static

# 查看动态段(无动态依赖)
readelf -d rw_static
# 输出:There is no dynamic section in this file.

# 查看符号表(库函数已嵌入)
readelf -s rw_static | grep "reader_thread"
# 输出:123: 00000000004011a6    99 FUNC    GLOBAL DEFAULT   14 reader_thread
  • 无动态段,库函数已嵌入代码段(DEFAULT)。

2. 动态链接程序(rw_dynamic

# 查看动态段(含依赖信息)
readelf -d rw_dynamic
# 输出关键项:
# 0x00000001 (NEEDED) Shared library: [librw.so]
# 0x0000000f (RPATH) Library rpath: [.]

# 查看符号表(库函数为外部引用)
readelf -s rw_dynamic | grep "reader_thread"
# 输出:102: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND reader_thread
  • 有动态段,明确依赖librw.so;库函数标记为UND(未定义),运行时动态加载。

五、总结

特性 静态库(.a 动态库(.so
链接方式 代码嵌入可执行文件 仅记录引用,运行时加载
可执行文件体积 较大(包含库代码) 较小(仅含主程序和引用)
运行依赖 无(独立运行) 依赖动态库文件存在
动态段(readelf 有,含NEEDED依赖信息
符号表(函数) 已嵌入(DEFAULT 外部引用(UND
适用场景 小库、需独立分发的程序 大库、多程序共享的场景

通过本文实践,可深入理解静态链接与动态链接的底层机制,为多线程程序的模块化设计和库管理提供参考。

Logo

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。

更多推荐