首页 > 系统相关 >Linux C/C++编程之动态库

Linux C/C++编程之动态库

时间:2024-12-02 10:55:10浏览次数:7  
标签:动态 lib 编程 C++ so conf Linux main libtest

【图书推荐】《Linux C与C++一线开发实践(第2版)》_linux c与c++一线开发实践pdf-CSDN博客

《Linux C与C++一线开发实践(第2版)(Linux技术丛书)》(朱文伟,李建英)【摘要 书评 试读】- 京东图书 (jd.com)

10.4.1  动态库的基本概念

动态库又称为共享库。这种类型的库的命名规则一般是libxxx.M.N.so,其中,xxx为库的名字,M是库的主版本号,N是库的副版本号。当然也可以不要版本号,但名字必须有,即libxxx.so。相对于静态函数库,动态函数库在编译的时候并没有被编译进目标代码中,我们的程序执行到相关函数时才调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。由于函数库没有被整合进程序,而是程序运行时动态地申请并调用,因此程序的运行环境中必须提供相应的库。动态函数库的改变并不影响程序,所以动态函数库的升级比较方便。Linux系统有几个重要的目录用于存放相应的函数库,如/lib/usr/lib。

当要使用静态的程序库时,连接器会找出程序所需的函数,然后将它们复制到执行文件,由于这种复制是完整的,因此一旦连接成功,静态程序库也就不再需要了。然而,对动态库而言,就不是这样的。动态库会在执行程序内留下一个标记,指明当程序执行时,首先必须载入这个库。由于动态库节省空间,因此Linux下进行连接的默认操作是首先连接动态库,也就是说,如果同时存在静态和动态库,不特别指定的话,将与动态库连接。

10.4.2  动态库的创建和使用

动态库文件的后缀为.so,可以直接使用gcc或g++生成。下面来看一个例子。

【例10.3】创建和使用动态库

(1)打开Visual Studio Code,新建一个源文件test.cpp,内容如下:

#include  <stdio.h>

#include <iostream>

using namespace std;

void f(int age)

{

    cout << "your age is " << age << endl;

    printf("age:%d\n",age);

}

代码很简单。这个源文件主要作为动态库。把源test.c文件上传到Linux,在命令行输入:

# g++ test.cpp -fPIC -shared -o libtest.so

此时会在同目录下生成动态库文件libtest.so。上面命令行中的-shared表明生成共享库,而-fPIC则表明使用地址无关代码。PIC的全称是Position Independent Code。在Linux下编译共享库时,必须加上-fPIC参数,否则在链接时会有错误提示。那么fPIC的目的是什么?共享库文件可能会被不同的进程加载到不同的位置上,如果共享对象中的指令使用了绝对地址、外部模块地址,那么在共享对象被加载时就必须根据相关模块的加载位置对这个地址做调整,也就是修改这些地址,让它在对应进程中能正确访问,这样就不能实现多进程共享一份物理内存,共享库在每个进程中都必须有一份物理内存的复制。fPIC指令就是为了让使用同一个共享对象的多个进程能尽可能多地共享物理内存,它背后把那些涉及绝对地址、外部模块地址访问的地方都抽离出来,保证代码段的内容可以多进程相同,实现共享。这些内容了解即可。总之,-fPIC(或-fpic)表示编译为位置独立的代码。位置独立的代码即位置无关代码,在可执行程序加载的时候,可以存放在内存中的任何位置。若不使用该选项,则编译后的代码是位置相关的代码,在可执行程序加载时,通过代码复制的方式来满足不同进程的需要,没有实现真正意义上的位置共享。

(2)动态库产生后,我们就可以使用动态库了,下面先编写一个主函数。打开Visual Studio Code,新建一个文件main.cpp,并输入如下代码:

extern void f(int age);         // 声明要使用的函数

#include <iostream>

using namespace std;

 

int main(int argc, char *argv[])

{

    f(66);

    cout << "HI" << endl;

   

    return 0;

}

代码很简单。首先声明一下f,然后就可以在main函数中使用了。保存代码后将其上传到Linux,注意要和libtest.a放在同一个目录,然后在命令行进行编译并运行:

# g++ main.c -o main -L ./ -ltest

其中,-L用来告诉g++去哪里找库文件,它后面加了空格和./表示在当前目录下寻找库,或者直接写-L.即可;-l用来指定具体的库,其中的lib和.so不用显式写出,g++会自动去寻找libtest.so。默认情况下,g++或gcc首先搜索动态库(.so)文件,找不到后再去寻找静态库(.a)文件。当前目录下以test命名的库文件有动态库文件(libtest.so),因此g++可以找到。

编译链接后,会在当前目录下生成可执行文件main,如果此时运行,会发现运行不了:

# ./main

./main: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory

这是为什么呢?看提示似乎是main程序找不到libtest.so,但是main文件和libtest.so都在同一目录下。虽然我们知道它们在同一目录下,但程序main并不知道。那怎么办呢?把动态库放到默认的搜索路径上,或者告诉系统动态库的路径即可,有以下3种方法。

第一种,将库复制到/usr/lib和/lib(不包含子目录)下。

这两个路径是默认搜索的地方,但要注意的是,把动态库放到这两个目录之一后,要执行命令ldconfig,否则还是会提示找不到。现在我们把libtest.so剪切到/usr/lib:

# mv libtest.so /usr/local/lib

移动后,当前目录下就没有libtest.so了,然后执行ldconfig后再运行main:

# ldconfig

# ./main

age:66

HI

很多开源软件通过源码包进行安装时,如果不指定--prefix,就会将库安装在/usr/local/lib目录下,当运行程序需要链接动态库时,提示找不到相关的.so库,进而报错。也就是说,/usr/local/lib目录不在系统默认的库搜索目录中。

第二种,在命令前加环境变量。

如果第一种方法做了,则首先把/usr/lib或/lib下的libtest.so删除:

[root@localhost lib]# cd /usr/lib

[root@localhost lib]# rm -f libtest.so

再回到/zww/test下重新生成一个libtest.so,然后加环境变量后运行main:

# g++ test.cpp -fPIC -shared -o libtest.so

# LD_LIBRARY_PATH=/zww/test ./main

age:66

HI

可以看到,我们把动态库libtest.so的路径/zww/test赋值给了环境变量LD_LIBRARY_PATH,然后运行main就成功了。这种方法虽然简单,但该环境变量只对当前命令有效,当该命令执行完成后,该环境变量就无效了,除非每次执行main都这样加环境变量。要想采用永久法,可以参考第三种方法。

第三种,修改/etc/ld.so.conf文件。

我们可以把自己的动态库文件的路径加到/etc/ld.so.conf中,这个文件叫动态库配置文件,接着执行ldconfig,然后系统可以把我们添加的路径作为其默认的搜索路径,一劳永逸。

# vi  /etc/ld.so.conf

然后在该文件末尾新起一行加入我们的库路径/zww/test/,保存并关闭。此时查看/etc/ld.so.conf的内容为:

# cat  /etc/ld.so.conf

include ld.so.conf.d/*.conf

/zww/test/

其中,第一行原来就有。

现在开始执行main:

# ./main

age:66

HI

可以发现执行成功了。我们也可以把libtest.so放到任意目录,然后添加任意目录的路径到/etc/ld.so.conf,发现再也不用担心main找不到ibtest.so了。比如现在把libtest.so放到/root/下,并执行main:

# mv libtest.so /root

# ./main

./main: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory

预料之中,main找不到libtest.so,因为/zww/test下没有了。我们来修改/etc/ld.so.conf,把/root添加进去,添加后的内容如下:

# cat /etc/ld.so.conf

include ld.so.conf.d/*.conf

/root

再执行ldconfig,然后执行main:

# ldconfig

# ./main

age:66

HI

执行成功了。值得注意的是,每次修改/etc/ld.so.conf后,都要执行ldconfig。ldconfig命令的用途主要是在默认搜寻目录(/lib和/usr/lib)以及动态库配置文件/etc/ld.so.conf所列的目录下,搜索出可共享的动态链接库(格式如前面介绍的lib*.so*),进而创建出动态装入程序(ld.so程序)所需的连接和缓存文件。缓存文件默认为/etc/ld.so.cache,此文件保存已排好序的动态链接库的名字列表。

【例10.4】多个文件生成动态库

(1)打开Visual Studio Code,新建一个源文件test1.cpp,内容如下:

#include <iostream>

using namespace std;

void f1(int age)

{

    cout << "this is libtest1.so: " << age << endl;

}

(2)保存后,再新建一个源文件test2.cpp,内容如下:

#include <iostream>

using namespace std;

void f2(int age)

{

    cout << "this is libtest2.so: " << age << endl;

}

代码很简单。这两个源文件主要作为动态库。

(3)把两个源文件上传到Linux,在命令行下输入以下命令:

# g++ test1.cpp test2.cpp -fPIC -shared -o libtest.so

此时会在同目录下生成动态库文件libtest.so,然后编译main:

# g++ main.cpp -L. -ltest -o main

此时把/zww/test路径加入动态库配置文件/etc/ld.so.conf中,加入后内容如下:

# cat /etc/ld.so.conf

include ld.so.conf.d/*.conf

/zww/test

执行ldconfig后再执行main:

# ldconfig

# ./main

this is libtest1.so: 65

this is libtest2.so: 66

bye

运行成功了。其实多个文件组成库的过程和一个文件类似,只是编译库的时候多加一个源文件而已。

 

 

标签:动态,lib,编程,C++,so,conf,Linux,main,libtest
From: https://www.cnblogs.com/brucexia/p/18581252

相关文章

  • Linux的一些常用命令
    1.查看系统信息系统版本uname-acat/etc/redhat-release查看CPUlscpu内存free-h硬盘df-Th 2.firewall防火墙启动:systemctlstartfirewalld查状态:systemctlstatusfirewalld停止:systemctldisablefirewalld禁用:systemctlstopfirewalld在开机时启用一......
  • linux ln命令详解
    介绍ln是linux的一个重要命令,它的功能是为某一个文件在另外一个位置建立一个同步的链接。当我们需要在不同的目录,用到相同的文件时,我们不需要在每一个需要的目录下都放一个必须相同的文件,我们只要在某个固定的目录,放上该文件,然后在其它的目录下用ln命令链接(link)它就可以,不必重复......
  • Linux常用的文件操作命令
    目录在Linux系统中,文件操作是日常任务的重要组成部分。下面是一些常用的命令行工具,用于创建、查看、编辑和管理文件。touch-创建空文件或更新文件的时间戳touch文件名cp-复制文件或目录cp[选项]源文件目标文件less/more-分页查看文件内容less文件名......
  • Java基础全解:构建扎实编程技能
    文章目录1.HelloWorld程序深入解析:2.数据类型深入解析:3.条件判断深入解析:4.循环结构深入解析:5.数组深入解析:6.方法定义与调用深入解析:1.HelloWorld程序深入解析:类声明:publicclassHelloWorld定义了一个公共类。public关键字意味着这个类可以......
  • Kali Linux怎么开python虚拟环境
    相信很多朋友再学习的过程中都会遇到一些pip失效,或者报错的时候,他们要求我们要使用虚拟环境,但是不知道怎么搭建,下面这篇文章就来告诉你如何搭建虚拟环境,这个方法在所有Linux的服务器都通用,就两行命令安装venv模块(Python3.3及以上版本自带,无需安装)python3-mvenvmyenv......
  • 百度的面试官,你什么勾八水平,也好意思上车锁门 面试官:说一下C++强制类型转换,C++内存管
    ......
  • linux进程管理
    一、进程数据结构和组织二、进程切换三、进程创建四、进程调度进程是一个程序运行的实例,操作系统通过并行和并发的运行多个进程实现多个任务的并行处理;从系统资源的角度看,多个进程同时运行时,操作系统以进程为单位来分配系统资源(比如CPU时间、内存等);     进程作为系......
  • linux好玩又没用的命令
    原文地址:linux好玩又没用的命令–无敌牛欢迎参观我的个人博客:无敌牛–技术/著作/典籍/分享等介绍linux环境下几款好玩但又没用的命令,使用linux系统的时候增加一些乐趣。1、cmatrix黑客帝国电影中,字符动画的效果安装:aptinstallcmatrix测试直接执行cmatrix开......
  • C++ : 四、面向对象编程(1)
    (一)面向对象的概念面向对象编程是一种编程范式,它以对象为核心来组织程序结构。对象就是在现实世界中,某个具体的实体在计算机世界中的映射和体现。在C++中,对象是类的实例,类是一种用户自定义的数据类型,它封装了数据(成员变量)和操作这些数据的函数(成员函数)。比如:银行中实体的柜......
  • C++ : 五、面向对象编程(2)
    (一)对象的生存期1.对于局部定义的对象,当程序控制流到达该对象定义处时,调用构造函数,程序控制走出该局部域时,调用析构函数。2.对于静态局部定义的对象,当程序控制流到达该对象定义处时,调用构造函数,整个程序结束时时,调用析构函数。voidfun(){ Complexc1(11,12); staticCom......