首页 > 系统相关 >Linux C语言Shared Library共享库细节探究

Linux C语言Shared Library共享库细节探究

时间:2023-10-12 16:22:07浏览次数:57  
标签:类库 val int library C语言 编译 static Library Linux

开发中遇到一个问题,比如有一个类库A,被类库B引用,类库B和类库A都被程序C引用。类库A中有一个全局变量G,要求同一个进程中使用的是同一个全局变量G。

虽然看起来很简单,但是实际探究下来还有不少坑。

如果不是类库

如果A B都不是类库,而是直接引入源码编译,理论上比较方便解决。

示例一

pre.h

定义了全局变量a

#ifndef UNTITLED11_PRE_H
#define UNTITLED11_PRE_H
int a;
#endif //UNTITLED11_PRE_H

test.h

#ifndef UNTITLED11_TEST_H
#define UNTITLED11_TEST_H
#include <stdlib.h>

void ftest();
#endif //UNTITLED11_TEST_H

test.c

相当于另一个模块引用了全局变量

#include <stdio.h>
#include "pre.h"
#include "test.h"
#include <stdlib.h>
void ftest()
{
    a = 333;
}

main.c

#include <stdio.h>
#include <stdint.h>
#include "test.h"
#include "pre.h"
int main()
{
    a = 222;
    printf("main[%d]\n", a);
    ftest();
    printf("main[%d]\n", a);
    return 0;
}

输出结果

main[222]
main[333]

可以看出不同模块虽然都引入了pre.h,都有int a,但是使用的是同一个。

示例二

如果把上面pre.h中的int a;改成int a = 0;,编译就会报错

multiple definition of `a';

这是为什么呢?实际上示例一只是用了一个投巧的办法。因为int a;不能确定是声明还是定义,当遇到第一个赋值是,比如a = 222;,这时候才知道int a;是声明,只是告诉大家有这么一个变量,但是还没有为其分配空间,a = 222;才真正创建了这个变量,后面的a = 333;是赋值。所以使用的是同一个。

示例一的用法与使用extern是一致的,如下

示例三

把pre.h中的语句改成extern int a;
在main.c的main函数前添加int a = 0;

这个结果与示例一是一致的。就是大家都引用pre.h,但是extern int a;表示只是声明了这个变量,并没有实例化,a的实例化到其他文件找,我们在main.c中调用int a = 0;进行了实例化,所以当test.c中使用a的时候也会到其他文件找,也找到的是main.c中的a,所以是同一个。

使用类库

如果A B是类库,就比较麻烦了,因为类库与一个单独的程序没有区别,是独立的,所以就会遇到下面的问题。

示例一

在A的头文件定义全局变量,不管是int g_val;还是int g_val = 0;编译链接是都会遇到multiple definition问题,为什么呢?

那是因为,当编译A时,不管是如何写,A中肯定用到了g_val,当第一次用g_val时,g_val就已经定义了,同样B中也是一样。所以当B再次链接A或者C链接A和B时,就会有两个定义的g_val,所以就出现了multiple definition

示例二

在A的头文件定义静态变量。static int g_val;
这样虽然不会报错了,但是有另一个问题,就是大家的都相互独立,也就是说使用的是多份g_val。

我们直到static除了设置静态变量,让数据可以一直存在,还有一个作用,就是限制作用范围到当前文件。所以当编译完A后,A中有一个g_val,编译完B,B中有一个g_val,同理C中也有一个g_val,并且是独立的。这样虽然编译成功,但是没有达到我们的目的,我们要求是使用同一个g_val。

示例三

在A的头文件声明extern int g_val;
在A的源码定义int g_val = 0;
A编译成共享库,其他类库也是共享库。
这样就可以解决我们的问题。

因为共享库只会在第一次调用的时候加载一次,同一个进程,也只会在第一次加载的时候映射一次静态变量和全局变量。也就是说,虽然A B C都有全局变量G,但是只会在第一次调用的时候创建一个G,后续都使用的是同一个,这个与一开始直接把所有文件一起编译一样,只不过不是在main.c中定义了int a = 0;而是为pre.h创建一个pre.c,在pre.c中定义了int a = 0;

shared library共享库与static library静态库区别

提到shared library,我们都会对比static library。

主要的区别实际上可以从其初衷来看:

  • static library最早被发明出来,因为很多模块并不经常修改,每次都编译很麻烦,可以先把这些模块编译成static library,再编译其他模块时,如果需要,就把static library相关的代码加入到其他模块中。
  • shared library是为了解决,有写模块,经常被使用,或者要给其他人用,可以编译成shared library,当程序运行时,如果执行到shared library的代码,就把shared library加载进来,调用对应的二进制模块。节省了硬盘和内存空间。
  • static library相当于一个文件包,把其他模块编译生成的.o文件打包成一个.a(static library),当使用是,就相当于编译过程中把.o编译成一个二进制一样。
  • 由于static library是编译到其他模块中的,如果接口没变,但是实现方式做了修改(比如修复bug),那么所有使用到这个static library的模块都需要重新编译。
  • shared library只会把一些查找对应接口的信息放到程序中,并不会把整个库放入到链接库/程序中。当系统中第一个程序使用某个share library时,系统才把这个shared library从硬盘加载到内存。并且当有第二个程序使用这个shared library时,会使用内存中的同一份数据,节省了内存。
  • 共享库都是编译一份,放到指定路径,不会存在链接程序中,节省了硬盘。
  • 虽然多个进程使用同一份内存中的共享库,但是其静态数据和全局数据是相互独立的,会重新映射。这也是合理的,不然大家都可以相互修改数据,简直乱套了。
  • 由于链接程序只保存了共享库的接口信息,并没有载入其全部实现的二机制数据,所以如果接口信息没有变更,可以随时替换共享库,而链接程序不需要重新编译。
    The Linux Programming Interface

标签:类库,val,int,library,C语言,编译,static,Library,Linux
From: https://www.cnblogs.com/studywithallofyou/p/17759635.html

相关文章

  • Linux之iostat
    Linux之iostat前言iostat主要用于监控系统设备的IO负载情况。iostat首次运行时显示自系统启动开始的各项统计信息,之后运行iostat将显示自上次运行该命令以后的统计信息。用户可以通过指定统计的次数和时间来获得所需的统计信息。1.命令功能:通过iostat方便查看CPU、网卡、tty......
  • Linux 日志按时间、按行截取方式
    Linux日志按时间、按行截取方式代码脚本之家 2023-10-0812:53 发表于上海收录于合集#linux2个#日志1个1.截取catalina.out某段时间内的日志信息:sed-n'/2023-09-23 14:00:/,/2023-09-23 15:00:/p'catalina.out>seg.log2.按行截取日志先按照关键字找到相应......
  • linux TCP 通信流程 套接字函数
    TCP和UDP  -> 传输层的协议UDP:用户数据报协议,面向无连接,可以单播,多播,广播,面向数据报(类似战争中无线电的广播),不可靠。TCP:传输控制协议,面向连接的,可靠的,基于字节流,仅支持单播传输(点对点)。UDP TCP......
  • Linux快捷键及History用法
    Linux快捷键及History用法1.bash的快捷键Ctrl+l清屏,相当于clear命令Ctrl+o执行当前命令,并重新显示本命令Ctrl+s阻止屏幕输出,锁定Ctrl+q允许屏幕输出,解锁Ctrl+c终止命令Ctrl+z挂起命令Ctrl+a光标移到命令行首,相当于HomeCtrl+e光标移到命令......
  • Linux 中awk命令根据索引文件批量提取列和行
     001、批量提取列,根据索引index.txt文件批量提取2、4、8、9列[root@pc1test2]#lsa.txtindex.txt[root@pc1test2]#cata.txt##测试文件00100200300400500600700800901001101201301401501601701801902002102202302402502602......
  • 面试被问 Linux 命令 su 和 sudo 的区别
    之前一直对 su 和 sudo 这两个命令犯迷糊,最近专门搜了这方面的资料,总算是把两者的关系以及用法搞清楚了,这篇文章来系统总结一下。1、准备工作因为本篇博客中涉及到用户切换,所以我需要提前准备好几个测试用户,方便后续切换。Linux中新建用户的命令是 useradd ,一般系统中这个命......
  • kali linux 设置固定IP
    1.修改文件/etc/network/interfaces┌──(kali㉿kali)-[~]└─$sudovi/etc/network/interfaces#Thisfiledescribesthenetworkinterfacesavailableonyoursystem#andhowtoactivatethem.Formoreinformation,seeinterfaces(5).source/etc/network/in......
  • linux系统和windows系统检测磁盘大小报警
    1.应用场景需要往磁盘中存储图片和或者数据,需要检测磁盘大小,当小于5GB的时候提示用户,并停止存储图片和数据,避免磁盘塞满,无法启动系统或者操作电脑;2.实现方法用一个定时器去定时查询磁盘空间大小,linux系统,采用QProess执行命令,然后解析命令返回值,获取剩余的磁盘空间voidFaceM......
  • 关于c语言操作libwebsockets示例
    第一步,安装libwebsockets库,c语言编写的,默认安装引用库,配置相应的库及路径第二步:上代码main.h ////CreatedbyAdministratoron2020/5/1.// #ifndefMEDIA_MAIN_H#defineMEDIA_MAIN_H #define boolchar volatileintexit_sig=0; #defineMAX_PAYLOAD_SIZE 10......
  • 小干货~ NFS在Linux系统中的应用
    Linux系统中的经典的语句”一切皆文件”,意思就是说在Linux系统中,我们要达成任何目标,都是通过配置文件去实现的。既然这样,那我们就少不了跟文件打交道,今天就跟大家分享下如何在Linux系统中配置网络文件系统(NetworkFileSystem,NFS)服务来达到Linux系统之间的文件共享的目的。......