注意在库中不能写main()函数。
复习gcc指令 预处理-E
-> xx.i
编译 -S
-> xx.s
汇编 -c
-> xx.o
汇编得到的 xx.o
称为目标可重定向二进制文件,此时的文件需要把第三方库链接进来才变成可执行程序。
gcc -o mymath main.c myadd.c mysub.c
得到的mymath可以执行
gcc -o mymath main.o myadd.o mysub.o
得到的mymath可以执行
故可以不给别人源码.c文件,只给别人重定位目标二进制文件.o文件【方法实现】也是可以的,当然头文件【方法声明】也得给对方,才能通过编译。
思路:将*.o所有文件整体打包,给对方提供库文件!多个.o打包形成1个.o文件(库),其中打包的方式就决定了动静态库。
结论:1、库相当于*.o文件的集合;2、交付库==库文件.a/.so+头文件.h;
静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。
生成静态库代码: ar -rc libxxx.a *.o
ar-archive-归档,Makefile文件内容
查看静态库中的目录列表ar -tv libxxx.a
libmymath.a:mysub.o myadd.o
ar -rc $@ $^
mysub.o:mysub.c
gcc -c mysub.c -o mysub.o
myadd.o:myadd.c
gcc -c myadd.c -o myadd.o
.PHONY:output
output:
mkdir -p mylib/include
mkdir -p mylib/lib
cp -f *.a mylib/lib
cp -f *.h mylib/include
.PHONY:clean
clean:
rm *.o libmymath.a
接着打包tar czf mylib.tgz mylib
,正常流程就是把mylib.tgz放到yum资源,供人下载,别人下载然后解压tar xzf mylib.tgz
,接着安装cp mylib/include/*.h /user/include/
和cp mylib/lib/*.o /user/lib/
若要链接第三方库,必须要指明库的名称!以及指明库所在的路径!gcc编译指定路径 -I选项(头文件所在路径) -L选项(库文件所在路径) -l选项(库文件名称)gcc -o mymath main.c -I ./mylib/include -L ./mylib/lib -l mymath
注意库名称是去掉lib和.a/.so!
那为什么平时直接编译不用指明路径和库名称?因为gcc就只是编译C语言(只需要libc.a),g++就是编译C++的(只需要C++的库)。
接下来看代码如何制作静态库。
[yyq@VM-8-13-centos 2023_02_09_lib_dll]$ ll
total 12
-rw-rw-r-- 1 yyq yyq 213 Feb 9 15:01 main.c
drwxrwxr-x 4 yyq yyq 4096 Feb 9 13:47 mylib
[yyq@VM-8-13-centos 2023_02_09_lib_dll]$ gcc -o mymath main.c -I ./mylib/include -L ./mylib/lib -l mymath
[yyq@VM-8-13-centos 2023_02_09_lib_dll]$ ll
total 24
-rw-rw-r-- 1 yyq yyq 213 Feb 9 15:01 main.c
drwxrwxr-x 4 yyq yyq 4096 Feb 9 13:47 mylib
-rwxrwxr-x 1 yyq yyq 8480 Feb 9 15:03 mymath
[yyq@VM-8-13-centos 2023_02_09_lib_dll]$ ./mymath
Enter Add Func:> 10 + 20 = ?
result:>30
Enter Sub Func:> 10 - 20 = ?
result:>-10
[yyq@VM-8-13-centos 2023_02_09_lib_dll]$ ldd mymath//怎么都是动态链接的库呢??怎么看不到我们自己写的库名称?
linux-vdso.so.1 => (0x00007fff23bb7000)
libc.so.6 => /lib64/libc.so.6 (0x00007fca344ce000)
/lib64/ld-linux-x86-64.so.2 (0x00007fca3489c000)
[yyq@VM-8-13-centos 2023_02_09_lib_dll]$ file mymath//为什么属性也是动态链接库呢??
mymath: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=0f7725a095306499436d1394ed0d0ac1e5308b02, not stripped
//因为gcc默认是动态链接,对于特定的库取决于库本身是动态库还是静态库。要静态链接需要选项-static,动态库只能动态链接,静态库只能静态链接,动静都有默认动态。
//只要有1个动态库,那这个可执行程序就是动态链接的。
直接在命令行使用gcc太慢了,改进方法:1、把库放到系统路径/usr/include/
和/lib64/
。
首先,静态库不需要加载到内存,是在链接阶段,将汇编生成的目标重定向二进制文件.o与引用到的库一起链接打包到可执行文件,即直接把那段需要的库代码拷贝到程序对应的代码段中【程序未被加载到内存的时候,就已经形成了虚拟地址(空间代码段、已初始化未初始化数据区、全局数据区),程序没有栈区和堆区】。当程序load到内存中,形成进程地址空间时,就有确定的地址位置(即进程地址空间的代码区只能访问程序的代码区)。
1、空间浪费。静态库在内存中存在多份拷贝导致空间浪费,
2、静态库对程序的更新、部署和发布页不友好。如果静态库libxx.lib更新了,所有使用它的应用程序都需要重新编译、发布给用户(对于用户来说,只是一个很小的改动,却导致整个程序重新下载,全量更新)。
动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码。在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)。动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。
生成动态库的代码
gcc -fPIC -c mysub.c myadd.c
gcc -shared -o libmymath.so *.o
[yyq@VM-8-13-centos dynamicFile]$ ll
total 16
-rw-rw-r-- 1 yyq yyq 114 Feb 9 15:57 myadd.c
-rw-rw-r-- 1 yyq yyq 57 Feb 9 15:57 myadd.h
-rw-rw-r-- 1 yyq yyq 114 Feb 9 15:57 mysub.c
-rw-rw-r-- 1 yyq yyq 56 Feb 9 15:57 mysub.h
[yyq@VM-8-13-centos dynamicFile]$ gcc -fPIC -c myadd.c
[yyq@VM-8-13-centos dynamicFile]$ gcc -fPIC -c mysub.c
[yyq@VM-8-13-centos dynamicFile]$ ll
total 24
-rw-rw-r-- 1 yyq yyq 114 Feb 9 15:57 myadd.c
-rw-rw-r-- 1 yyq yyq 57 Feb 9 15:57 myadd.h
-rw-rw-r-- 1 yyq yyq 1592 Feb 9 15:59 myadd.o
-rw-rw-r-- 1 yyq yyq 114 Feb 9 15:57 mysub.c
-rw-rw-r-- 1 yyq yyq 56 Feb 9 15:57 mysub.h
-rw-rw-r-- 1 yyq yyq 1592 Feb 9 15:59 mysub.o
[yyq@VM-8-13-centos dynamicFile]$ gcc -shared -o libmymath.so *.o
[yyq@VM-8-13-centos dynamicFile]$ ll
total 32
-rwxrwxr-x 1 yyq yyq 8088 Feb 9 15:59 libmymath.so
-rw-rw-r-- 1 yyq yyq 114 Feb 9 15:57 myadd.c
-rw-rw-r-- 1 yyq yyq 57 Feb 9 15:57 myadd.h
-rw-rw-r-- 1 yyq yyq 1592 Feb 9 15:59 myadd.o
-rw-rw-r-- 1 yyq yyq 114 Feb 9 15:57 mysub.c
-rw-rw-r-- 1 yyq yyq 56 Feb 9 15:57 mysub.h
-rw-rw-r-- 1 yyq yyq 1592 Feb 9 15:59 mysub.o
[yyq@VM-8-13-centos dynamicFile]$ file libmymath.so
libmymath.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=bcfe08193c5605299db2b28596b7b96dedb08ab3, not stripped
[yyq@VM-8-13-centos dynamicFile]$ ldd libmymath.so
linux-vdso.so.1 => (0x00007ffe02142000)
libc.so.6 => /lib64/libc.so.6 (0x00007f66e790e000)
/lib64/ld-linux-x86-64.so.2 (0x00007f66e7ede000)
[yyq@VM-8-13-centos mylib_dynamic]$ gcc -o mymath main.c -I./mylib/include -L./mylib/lib -lmymath//与静态库同样的编译代码再去编译形成可执行程序没问题,但是在执行时会报错,为什么呢?因为这里的指令都是告诉gcc需要的动态库在哪里以及叫什么名字,我们可以看一下这个可执行程序,它不知道我们自己写的动态库在哪里
[yyq@VM-8-13-centos mylib_dynamic]$ ldd mymath
linux-vdso.so.1 => (0x00007ffd4e268000)
libmymath.so => not found
libc.so.6 => /lib64/libc.so.6 (0x00007fd9ac027000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd9ac3f5000)
//我们知道当gcc把程序编译完形成可执行程序后,后续的执行就与gcc无关了
//且动态库是程序运行后才链接的,程序运行起来就和OS相关了,但是OS不知道需要的动态库在哪叫什么,因此执行时会报错
解决方法1:直接把.so文件拷贝到可执行程序的同级路径下;
解决办法2:把我们写的库放到环境变量LD_LIBRARY_PATH去,但仅限本次登陆有效;
[yyq@VM-8-13-centos mylib_dynamic]$ echo $LD_LIBRARY_PATH
:/home/yyq/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
[yyq@VM-8-13-centos lib]$ pwd
/home/yyq/linux-class/2023_02_09_lib_dll/mylib_dynamic/mylib/lib
[yyq@VM-8-13-centos mylib_dynamic]$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/yyq/linux-class/2023_02_09_lib_dll/mylib_dynamic/mylib/lib
//这样可执行程序就能运行了,但是仅在本次登陆有效,下线后再次执行又不能跑了
解决办法3:cd /etc/ld.so.conf.d/
在这个路径下我们自己也写一个conf出来;
[yyq@VM-8-13-centos lib]$ ls /etc/ld.so.conf.d/
bind-export-x86_64.conf
dyninst-x86_64.conf
kernel-3.10.0-1160.71.1.el7.x86_64.conf
kernel-3.10.0-1160.76.1.el7.x86_64.conf
mysql-x86_64.conf
[yyq@VM-8-13-centos ld.so.conf.d]$ touch lib.conf
//在里面写出动态库的路径/home/yyq/linux-class/2023_02_09_lib_dll/mylib_dynamic/mylib/lib
[yyq@VM-8-13-centos ld.so.conf.d]$ cat lib.conf
/home/yyq/linux-class/2023_02_09_lib_dll/mylib_dynamic/mylib/lib
//接着更新一下conf,每次都能执行了
[yyq@VM-8-13-centos ld.so.conf.d]$ sudo ldconfig
解决办法4:采用软链接的方式。即在要运行可执行程序的路径下执行ln -s /home/yyq/linux-class/2023_02_09_lib_dll/mylib_dynamic/mylib/lib/libmymath.so libmymath.so
;
[yyq@VM-8-13-centos mylib_dynamic]$ ll
total 20
-rw-rw-r-- 1 yyq yyq 213 Feb 9 16:06 main.c
drwxrwxr-x 4 yyq yyq 4096 Feb 9 16:03 mylib
-rwxrwxr-x 1 yyq yyq 8432 Feb 9 16:17 mymath
[yyq@VM-8-13-centos mylib_dynamic]$ ln -s /home/yyq/linux-class/2023_02_09_lib_dll/mylib_dynamic/mylib/lib/libmymath.so libmymath.so
[yyq@VM-8-13-centos mylib_dynamic]$ ll
total 24
lrwxrwxrwx 1 yyq yyq 77 Feb 9 16:48 libmymath.so -> /home/yyq/linux-class/2023_02_09_lib_dll/mylib_dynamic/mylib/lib/libmymath.so
-rw-rw-r-- 1 yyq yyq 213 Feb 9 16:06 main.c
drwxrwxr-x 4 yyq yyq 4096 Feb 9 16:03 mylib
-rwxrwxr-x 1 yyq yyq 8432 Feb 9 16:17 mymath
还可以直接把这个软链接直接建到系统默认的库目录下
sudo ln -s /home/yyq/linux-class/2023_02_09_lib_dll/mylib_dynamic/mylib/lib/libmymath.so /lib64/libmymath.so
以上我们所做的都是用gcc来完成编译,即把库路径、库名称、库文件告诉gcc,所以程序可以成功编译。那执行的时候呢?是要通过OS和shell来执行的,而这俩又不知道库路径这些数据,库也不在系统路径下,所以会出现最开始的链接失败的报错。给出的解决方案呢,是让OS和shell知道我们自己写的第三方动态库的名称、路径和文件,才能正确执行。
在执行时,OS将动态库中指定函数的偏移地址写入到我们的可执行程序中,OS提供页表将动态库代码映射到进程地址空间的共享区,此时库立马具备了在地址空间的起始地址,接着就能根据偏移量来找到指定函数(起始地址+偏移量=共享库中的地址->跳转访问)。【之前编译的时候使用该选项-fPIC与地址无关码,以该选项生成的动态库程序代码用的肯定是偏移地址(相对地址)】
动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行时才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦,用户只需要更新动态库即可,增量更新。
ncurses库是用来处理屏幕显示情况的库,直接yum安装