【Linux】基础IO —— 动静态库的制作与使用

简介: 【Linux】基础IO —— 动静态库的制作与使用

一.动态库 & 静态库


可能大家都快要忘记了这方面的知识,下面来复习一下 ,也可以来看看这篇博客->🔥动静态库🔥

在Linux中,一般库分为两种:动态库和静态库,它们就是文件!


静态库 :库文件以.a为后缀

动态库 :库文件以.so为后缀

库文件的命名规则:libname.so 或者 libname.a [.后面可能跟其他的内容]


库的真实名字:掐头去尾就是库名称。即去掉lib前缀并去掉.so/.a后缀,如上文中的c库


(在windows中,动态库以.lib为后缀,静态库以.dll为后缀)


我还记得gcc在编译的时候动态链接编译的,如果需要静态链接编译 则要带-static


0a2653c851af460fa595bd959398a8f1.png


可以观察到动态链接时文件体积较小,静态链接时文件体积较大。

原因很简单嘛,静态链接是把库文件的有关代码拷贝到我的可执行文件中,因此生成的文件比较大,可移植性较好。而动态链接,是在程序执行时由运行时链接文件加载库(地址),这样可以节省内存和硬盘的空间


我们买的云服务器没有内置语言的静态库,而只有动态库,需要yum安装C/C++静态库


sudo yum install glibc-static

sudo yum install libstdc++-static


二. 制作静态库


问了一个小问题:库里面要不要main函数呢?


答案是不能有,库是给别人用的,两个main函数会冲突

准备工作:

0a2653c851af460fa595bd959398a8f1.png

不出意外,编译成功了


2d65d23f6d4748949b924e4057485923.png


但库的制作只是这么简单?这还远不能称为制作库


我们知道编译时,只要把源文件编译成.o文件,再将其链接起来成为一个可执行程序


4cebaac233b3433da32a72337a77fc60.png


这下问题来了,我只把编好的.o和.h给人家,别人能用吗?


答案是:可以的


6de278e6d6694ce5bb08e7e842b7e74b.png


但是如果.o文件过多呢?

我们可以把一堆.o文件以某种方式打包,这种过程叫做形成库


🌈制作(编写者角度)


mklib目录下写这样一个Makefile ~


0a2653c851af460fa595bd959398a8f1.png


💦 1. 将库文件全部编译为.o


💦 2. 再用ar命令,把所有的.o打包在一起


可是怎么样打包呢?


ar -rc libname.a [待打包.o] 
//举例:
ar -rc libhello.a mymath.o myprint.o


ar是gnu的归档工具(Archive files),相当于打包成指定名称的文件


rc表示replace and create


hello库 —— include(包含库的所有头文件) —— lib (包含对应的库文件)


2d65d23f6d4748949b924e4057485923.png4cebaac233b3433da32a72337a77fc60.png


这样就打包了库


🌈使用(使用者角度)


立即把装好的库,拿来用一下


6de278e6d6694ce5bb08e7e842b7e74b.png


编辑main.c的时候发现一大堆错误


8ec4f2997fb246878c34ecd6d122b7c6.png


这下咋搞呢?


库的安装:把头文件和库文件拷贝进系统中(不建议:第三方库没经过测试,会污染其他的头文件)

头文件 gcc的默认搜索路径是 : /usr/include;库文件的默认搜索路径是: /lib64 or /usr / lib64

硬使用:显性的告诉编译器各种文件在哪

0a2653c851af460fa595bd959398a8f1.png

因为不在当前路径上,也不在库上,所以搜索不到


🌍直接编译会报错:找不到头文件。那当然找不到,编译器并不会查找你同级目录下目录中有啥, 因此要带-I./lib,指明在当前目录下的lib目录下找;


🌍又报错说找不到库函数的实现,因此带-L./lib,要告知库路径在哪儿


🌍但是实际情况可能有很多库,编译器也不知道链接这个路径下哪个库,因此同时要指定库。

-lhello(-l+库名)


2d65d23f6d4748949b924e4057485923.png


-I:指明头文件搜索路径

-L: 指明库文件搜索路径

-l: 指明要链接哪个库


三. 制作动态库


🥑制作


🎨还是要将库文件全部编译为.o,只不过要带选项-fPIC,形成与位置无关码


gcc -fPIC -c mymath.c -o mymath.o


什么叫做与位置无关的目标二进制文件呢?

静态库使用的是绝对编址,动态库采用相对编址(段地址 + 偏移),无需精准的地址


举个例子:


0a2653c851af460fa595bd959398a8f1.png


🎨 要把库打包,我们不再使用ar命令,记得带选项-shared ,告诉gcc形成动态库了而不是可执行程序


gcc -shared myprint.o mymath.o -o libhello.so


2d65d23f6d4748949b924e4057485923.png


🎨接下来就是在makefile形成库并且打包发布的过程,形成动态库和静态库


4cebaac233b3433da32a72337a77fc60.png


🥑使用


tar打包 发到网上


tar czf mylib.tgz output


0a2653c851af460fa595bd959398a8f1.png


🌊同样的我们需要指定路径搜索头文件-I./lib,同样的也需要指明库文件的搜索路径-L./lib,同样的也要指明链接哪个库


gcc main.c -I output/include -L output/lib -lhello


2d65d23f6d4748949b924e4057485923.png


编译器默认的就是动态链接


如果是只有静态库,就会把静态库拷贝进a.out中,gcc只能对该库进行静态链接

如果动静态库同时存在,默认就用动态库

如果我就想用静态库:-static:摒弃默认优先使用动态库的原则,使用静态库

但是一运行./a.out就报错了。因为静态库把目标模块直接拷贝进去,运行时不需要再找;而动态库,编译时需要找,运行时也需要加载动态库

0a2653c851af460fa595bd959398a8f1.png

静态库文化和本身的代码是放在了一起的;如果有重复的进程要调用,动态库:直接建立页表与内存的映射关系,也就可以跳转访问了;所以动态库加载一次就可以被多个进程所共同使用。静态库:多少次调用就有多少个拷贝,如果是十个一样的程序调用,那可能有9个是重复的


可是刚刚我编译时不是已经告诉了库路径了吗?为什么还是说我找不库? 奇怪


⚡但这只是告知了编译器gcc头文件库路径在哪里,当程序编译完成后,已与编译器无关,运行的时候加载器还是不知道它们在哪儿(所以报错)


也就是形成了a.out后,要和系统说动态库在哪


✨ 于是 —— 咱们需要在运行时前一步告知系统库在哪,有这样4种常见做法:


动态库头文件拷到共享库路径/lib64下(强烈不建议,污染)


通过导入LD_LIBRARY_PATH这个环境变量,指明程序启动后动态库的搜索路径(最推荐的)


在我们的服务器一般是空空如也的 —— 接下来我们导入进去


export LD_LIBRARY_PATH=路径  #导入环境变量


0a2653c851af460fa595bd959398a8f1.png


当然了,这种在命令行设置的环境变量,只在本次会话有效,退出登陆后再进来就没了。


如果想让环境变量永久生效,可以把它添加到登陆的启动脚本里(强烈不推荐)

比如vim ~/.bash_profile或者vim ~/.bashrc


修改系统文件:配置系统文件/etc/ld.so.conf.d/,是系统搜索动态库的路径,这种做法可以永久生效


在这里我们打开任意一个看看,其中也不过是一个配置路径


我们需要以sudo提升权限 进入:①添加配置文件,②在其中添加库的搜索路径,③并更新缓存。


0a2653c851af460fa595bd959398a8f1.png


然后我们执行,更新配置文件的缓存


ldconfig  //更新库路径缓存


后面就开始生效了(再次登录也有效)


0a2653c851af460fa595bd959398a8f1.png


如果你把这配置文件删了,那当然又不行了


[root@VM-24-5-centos ld.so.conf.d]# rm oneManBand.conf
[root@VM-24-5-centos ld.so.conf.d]# ldconfig


我们还是推荐添加环境变量的,虽然麻烦点,但是为了后面熟悉上手


为什么要有库?


使用库的角度:站在巨人的肩膀上,库的存在可以大大减少我们开发的周期,提高软件本身的质量

写库人的角度: 1.简单 2. 代码安全(不暴露底层代码)


总结一下


📢写在最后

基础IO总算完结了,进程通信我来了


相关文章
|
存储 Linux C语言
Linux C/C++之IO多路复用(aio)
这篇文章介绍了Linux中IO多路复用技术epoll和异步IO技术aio的区别、执行过程、编程模型以及具体的编程实现方式。
623 1
Linux C/C++之IO多路复用(aio)
|
5月前
|
Linux 编译器 vr&ar
Linux的动态库与静态库
静态库在编译时直接嵌入到最终的可执行文件中。
146 0
|
5月前
|
Linux C语言 网络架构
Linux的基础IO内容补充-FILE
而当我们将运行结果重定向到log.txt文件时,数据的刷新策略就变为了全缓冲,此时我们使用printf和fwrite函数打印的数据都打印到了C语言自带的缓冲区当中,之后当我们使用fork函数创建子进程时,由于进程间具有独立性,而之后当父进程或是子进程对要刷新缓冲区内容时,本质就是对父子进程共享的数据进行了修改,此时就需要对数据进行写时拷贝,至此缓冲区当中的数据就变成了两份,一份父进程的,一份子进程的,所以重定向到log.txt文件当中printf和fwrite函数打印的数据就有两份。此时我们就可以知道,
103 0
|
5月前
|
存储 Linux Shell
Linux的基础IO
那么,这里我们温习一下操作系统的概念我们在Linux平台下运行C代码时,C库函数就是对Linux系统调用接口进行的封装,在Windows平台下运行C代码时,C库函数就是对Windows系统调用接口进行的封装,这样做使得语言有了跨平台性,也方便进行二次开发。这就是因为在根本上操作系统确实像银行一样,并不完全信任用户程序,因为直接开放底层资源(如内存、磁盘、硬件访问权限)给用户程序会带来巨大的风险。所以就向银行一样他的服务是由工作人员隔着一层玻璃,然后对顾客进行服务的。
83 0
|
11月前
|
存储 编译器 Linux
动态链接的魔法:Linux下动态链接库机制探讨
本文将深入探讨Linux系统中的动态链接库机制,这其中包括但不限于全局符号介入、延迟绑定以及地址无关代码等内容。
2061 141
|
9月前
|
存储 网络协议 Linux
【Linux】进程IO|系统调用|open|write|文件描述符fd|封装|理解一切皆文件
本文详细介绍了Linux中的进程IO与系统调用,包括 `open`、`write`、`read`和 `close`函数及其用法,解释了文件描述符(fd)的概念,并深入探讨了Linux中的“一切皆文件”思想。这种设计极大地简化了系统编程,使得处理不同类型的IO设备变得更加一致和简单。通过本文的学习,您应该能够更好地理解和应用Linux中的进程IO操作,提高系统编程的效率和能力。
421 34
|
11月前
|
Linux API C语言
Linux基础IO
Linux基础IO操作是系统管理和开发的基本技能。通过掌握文件描述符、重定向与管道、性能分析工具、文件系统操作以及网络IO命令等内容,可以更高效地进行系统操作和脚本编写。希望本文提供的知识和示例能帮助读者更深入地理解和运用Linux IO操作。
237 14
|
9月前
|
网络协议 Linux Go
用 Go 基于 epoll 实现一个最小化的IO库
Go 语言社区中存在多个异步网络框架,如 evio、nbio、gnet 和 netpoll 等。这些框架旨在解决标准库 netpoll 的低效问题,如一个连接占用一个 goroutine 导致的资源浪费。easyio 是一个最小化的 IO 框架,核心代码不超过 500 行,仅实现 Linux 下的 epoll 和 TCP 协议。它通过 Worker Pool、Buffer 等优化提高了性能,并提供了简单的事件处理机制。
143 0
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
448 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库