Linux:理解动静态库

打印 上一主题 下一主题

主题 1010|帖子 1010|积分 3030



一、前言

   假如我们写了一些方法想给别人用??有什么办法呢??
  ——>(1)我直接把头文件和源文件给他(.c+.h)    ——>如许会让别人轻易看到你的实现
         (2)把源文件打包成库,再和头文件一起给他(库+.h)——>如许别人看不到你的实现
  ——>所以平常为了可以或许不让别人轻易窃取我们的劳动结果,我们一般采用的都是第二种方法,所以这就涉及到了如何把源文件打包成库的题目——>库又分静态库和动态库
  注:头文件是必须公开的!!相当于给别人的一份方法使用阐明书 
    所以为了学习如何创建静态库和动态库以及理解静态链接和动态链接的本质。我们得从以下两个角度来理解:
(1)站在库的制作者角度——>实验本身写一个简朴的库
(2)站在库的使用者角度——>学会如何使用第三方库 
二、静态链接

静态库 ——libXXX.a  
2.1 静态库的原理和命令

静态库的原理是什么呢??

     反正我假如给你源代码,你也是要先把所有的.c文件以及本身的main.c文件都酿成.o才能形成可执行程序,那么我干脆先把这些库文件都酿成.o文件,然后顺便帮你打个包,如许你的程序一样可以运行,并且你也看不到我的源代码。
——>静态库的原理:把方法有关的所有源文件都酿成.o文件,然后打包成libXXX.a ,然后当main.c酿成main.o的时间,就主动跟他一起毗连形成一个可执行程序了!!


1、ar是一个天生静态库的命令,第一个是计划天生的.a文件 后面跟着的是所有的.o文件 
2、选项-rc(replace and create)的意思是假如目标静态库文件存在就替换,不存在就创建
2.2 静态库制作和公开


 如许就可以完成静态库的制作,然后可以公开出去,酿成一个lib文件夹给用户


2.3 库使用及路径题目


      可是我们在头文件将路径都表示出来,显然不符合我们的使用习惯,假如我们去掉路径只保留mymath.h呢?? 

题目1 :为什么会找不到这个文件呢??
——> (1)没有路径,默认只会在这个路径找

          (2)也会在当前目次找一找 
 ——>解决方案:
(1)直接在头文件里带绝对路径(但是不符合我们的使用习惯)
(2) gcc有一个-I选项,就是告诉gcc,假如你在默认路径和当前路径找不到,你就到我指定的这个目次去找!!

 ——>更倾向于用第二种,因为第二种使用gcc的选项可以对gcc更为相识,不能总是系统怎样你就怎样,要真正学好动静态库,你就要学会去摒弃系统的默认动作,因为只有如许你才能知道编译器有一个查找头文件的动作,而你知道了这个选项就可以实验去控制这个动作!!
 

可是又报错了,原因是链接报错,因为gcc只能在系统默认路径和当前路径下去找这个库

 ——>解决方案:-L选项,告诉gcc,你假如默认路径和当前路径找不到,你就去我指定的这个目次里去找库


 ——>必须用-l显示告诉gcc要链接哪个库 却一般发起l之后紧跟库名称,因为有些时间可能不止链接一个库!!
 题目2:为什么-I的时间不用指明哪个头文件,但是-L的时间却要指明哪个库呢??
——>因为头文件的名称你已经在源文件里include了,我知道了文件名,你只需要告诉我路径我肯定可以或许找到,但是你并没有在源文件里告诉我要链接哪个库啊,我就算知蹊径径了又怎么样?我连他是谁的都不熟悉。所以你必须要显式地告诉我要链接哪个库!
题目3:怎么以前都不需要带选项,现在使用了第三方库就这么贫苦??
——>之前用不到是因为g++默认就能找到对应C++、C的一些库,但是你用的是一些第三方的库,就必须得如许做才可以!! 
 题目4:有什么其他解决方案吗??
——>-I和-L本质上都是gcc只能在默认路径和当前路径下找,所以我们可以把第三方文件和第三方库都编到系统的路径地下(不肯定要拷贝过去,也可以放软毗连),如许我们只需要-l告诉gcc要链接哪个库就可以了!——>所以第三方库使用的时间无论如何须须用-l 
2.4 errno的理解



2.5 理解库的安装 


实在我们将库拷贝到系统路径的过程就是——库的安装!! 
       比如我们再下载VS的时间里面就默认会有一些脚本语言,执行一些命令把干系需要的库的文件拷贝到系统的特定路径下,编译器可以找到,但是不发起第三方库如许做,因为可能会污染别人的库
      也可以搞软毗连

三、动态链接

3.1 动态库的原理和命令

动态库的原理和静态库一样,因为末了都要链接,所以都是先把-c酿成-o,然后再用命令打包起来
和静态库的区别:
(1)gcc编译多了一个选项 -fPIC 

(2)动态库的形成不需要用ar 因为他是gcc的亲儿子,默认就有内置的选项可以去形成,直接带-share选项就是告诉gcc:我不要天生可执行程序,我要天生共享库(条件是这些文件里面没有main函数!!)

3.2 实验动静态库分离




 题目1:x不是可执行权限吗??为什么动态库文件有x选项,而静态库文件没有x选项??

 ——>因为动态库需要我们在执行的时间跳转过去,而静态库没有-x是因为他的做优就是提供一个源代码拷贝过去,当拷贝完成后,你这个程序怎么样我并不关心。所以x选项的本质意思是当前的文件是否会以可执行程序的形式加载到内存中,只不过他没有main函数,而是只有方法,无法独立执行,需要依靠别人的调用!!
3.3 系统也得知道动态库在哪


题目1:为什么明明形成了a.out 却照旧出现这种情况呢?
——>因为你确实告诉了编译器动态库在那,他也帮你天生了可执行程序,而当你酿成可执行程序之后,就和编译器一点关系也没有了,而你的可执行程序运行不了,是因为你也得告诉系统(加载器)你的动态库在哪!
题目2:那为什么系统找得到C库却找不到我们的第三方库呢??
——>因为不但仅是编译,加载也需要提供路径!!系统不是神,不是你任意说个文件就可以链接,并且不同目次下可能还会有同名文件,C库可能早就被内置好大概是硬编码进系统的,所以系统找得到,但是其他的一些库,你必须想办法让系统找到。 
3.4 解决加载找不到动态库的方法

1、拷贝到系统默认的库路径/usr/lib64

2、在系统的默认库路径/usr/lib64 建立软毗连

3、将本身库所在的路径,添加到环境变量LD_LIBRARY_PATH(搜索用户自界说库路径)中

 但是你关闭shell之后就会失效,所以你想要恒久拥有的话,就得把环境变量写到系统启动时的设置文件脚本里面

4、/etc/ld.so.conf.d 建立本身的动态库路径的设置文件,然后ldconfig一下

——> 现实上我们用的库都是别人成熟的库,基本上都是使用安装到系统的方式!!
3.5  ncurses

基于终端的图形库界面

3.6 一些我的思考

1、实在一个语言你会用了,语言就不紧张了,你更渴望去理解软件的周边知识,就是有很多东西你在用但是你并不懂为什么,所以假如你能懂得为什么,当你再次去使用这个工具的时间,你就会特殊清楚,这就是懂得底层原理所带给我们的自信
2、穷则思变,积极不肯定成功,但是不谈场景的话都是耍流氓,这句话在普世规律里是对的,而且也是废话,假如我们选择的是一个上升的行业(纵然当前经济下行,计算机也是矮个里面挑大个)那么统统可能都会很大。
3、懂业务的程序员才是最紧张的程序员(成熟的,可以拿到台面上的那些技能并不值钱,但是可以或许去研究那些不成熟的技能,有发展趋势的技能才值钱),所以技能和知识是你的一个最底层、融会贯通的能力!!
4、场景越多你对环境变量的理解越深刻,环境变量是系统级别的全局变量,用来支撑编译器、毗连器、加载器…… 资助开发工具搜索他所需要的头文件、源文件、动态库!
5、以前我们写的代码的库是动态库,只不过无论是在windows照旧linux,写C、C++干系头文件和库,编译器和系统都可以找到,所以你才能实现无停滞编程,所以你想让第三方库也实现无停滞编程,关键在于如何如何让编译器和系统找到这个库 。
四、动态加载

4.1 动态库加载的底层原理


 1、 当cpu执行代码正文部分的时间,当发现需要被调用的库函数,就会跳到共享区去查找,假如此时库文件还没有被加载进内存,就发生缺页中断,然后将动态库文件加载进来,建立和页表的映射关系,从此往后我们执行的任何代码,都是在我们的进程地址空间中举行!
 2、系统运行时 ,一个进程可能会链接多个库,所以OS必然要把这些库管理起来——>系统中所有库的加载情况,OS非常清楚
4.2 进程地址空间

4.2.1 程序没有加载前的地址



        编译后的天然就给代码编址(逻辑地址)了(比如多态的虚函数表,call某个函数)——>阐明此时编译器已经在帮操作系统考虑加载和执行的题目了!! 
        在很早以前没有地址空间概念的时间,编址的逻辑就是段+偏移量,但现如今都酿成平展模式了(严酷遵照地址空间来编址0->4GB)
4.2.2 程序加载后的地址(进程)

 

题目1:如何执行第一条指令(main函数头)呢??
——>编译形成可执行程序的时间,会有一张表,存储的是各个段的地址,而表头的地址就是main函数的地址,cpu会拿到之后开始从正文部分执行
题目2:CPU会读取什么??
——>CPU内部读取到的指令,可能是数据,也可能是虚拟地址!!CPU在被设置的时间实在内部就做了很多可以或许熟悉这些基础指令的工作(实在就是把一些二进制汇编->一些指令级的东西->联合起来去完成我们要求他完成的工作)
题目3:为什么反汇编后显示出来的地址是不一样的??
 ——>因为每个指令的长度是不一样的!!
4.2.3 总结 

        编译后的可执行程序必须酿成进程,然后才能加载到内存中执行。一开动态库文件内容不肯定会被加载进来(因为可能很大),而是先创建干系的布局体和地址空间。
        编译后的可执行程序有一个表,表头是入口地址(也是虚拟地址),cpu拿到入口地址后开始执行
     当他检测到虚拟地址在页表中没有映射关系的时间,就会发生缺页中断,将需要的内容加载进内存,然后在页表中建立虚拟地址和物理地址的映射关系
4.3 动态库的地址



         转成汇编后printf已经酿成了地址,所以我们的cpu在执行的时间只熟悉编译时确定好的线性地址,所以我们必须需要包管我们的动态库确实已经加载到这个虚拟地址了,要否则就会找不到(也就是说缺页中断的时间必须把它加载到固定地址处)
——>可是我可能有十个八个库,我怎么包管每个库都恰好被加载到内存中的固定位置呢?我怎么包管哪个库先加载呢???因为这个位置可是在编译的时间就硬编码了啊,所以这是不可能做到的!
——>所以我们就要想办法让库在虚拟内存的恣意位置都可以加载
——>解决方法就是采用相对编址的方式,意思就是你可以任意加载,你要你在你的库秒速的布局体里面把加载进去的起始地址给我,然后我就会用起始地址+偏移量的方法找到我想要调用的库函数。
——>尚有一个题目就是:我必须得告诉编译器在分配地址的时间,让本身内部的函数不要采用绝对编址,只表示每个函数在库中的偏移量即可!
——>这就是为什么gcc选项需要有有-fPIC的原因,他就是在告诉编译器直接采用偏移量对库中的函数举行编址。

 


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

水军大提督

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表