1、系统预装了语言的头文件和库文件, 头文件提供方法说明,库提供方法的视线,头和库是有对应关系的,要组合在一起使用
2、头文件是在预处理阶段引入的,链接本质其实就是链接库
3、安装vs2019等的开发环境实质上是在安装语言配套的库和头文件,以及编译器软件。
4、头文件中有语法提醒功能,这需要编译器不断地把用户输入的内容在头文件中搜索,提醒功能依赖于头文件。
5、环境知道我们的代码哪里有问题,编译器有命令行的模式,还有其他自动化的模式在进行语法检查。在内部,操作者写代码时,编译器就不断地进行预处理,编译,不链接,如果有错,编译阶段就报出来了。
为了提高开发效率。
节省了操作者实现代码的时间,不需要每个函数都自己写。
静态库的后缀是.a,动态库的后缀是.so。 windows里静态是.lib,动态是.dll。
库的命名 像libstdc++.so.6 libc-2.17.so等,以lib为前缀,它的真实名字要去掉前缀和后缀,所以就是stdc++, c-2.17。
一般云服务,默认只存在动态库,不存在静态库,静态库需要单独安装。
这篇写一个加减乘除的库
加法:myadd.c和.h 减法:mysub.c和.h
但是这样是把源代码直接交给了用户,实际上不是这样,而是要打包成库。
创建两个目录,mylib和otherPerson,mylib就是我们定义的库。
现在暂且不知道打包成库,但是可以变成.o文件,这是在链接阶段之前的文件,程序链接时是会引入库的,所以这样就能进库了。
这是一个库的底层实现原理。但是这样写是不够高效的。
ar是打包静态库,-r是replace,形成.o文件后如果源代码改变,那么用这个命令就可以。-c是create。然后复制到用户目录,删除里面的.o文件
但是现在不能编译,会报出链接式错误,是因为这个库没办法直接使用。我们要做的是让编译器找到库文件+头文件。刚才我们写的库是第三方库,gcc g++不认识这个库,但是它们认识C++的库。用户必须让gcc知道应该链接哪个库,否则操作系统不会自己去看。
现在让gcc知道
大小写l都是表示link,但-L表示寻找的路径,-l后面跟库,不包括lib前缀,中间可以省去空格。
但是实际上不是这样给库的。
用户目录只有一个mymian.c。
cp *.h include/
cp *.a lib
tar -czf mymath.tgz include lib
cp mymath.tgz …/otherPerson
tar xzf mymath.tgz
最后一步解压包后就得到了两个目录,gcc -o mytest mymain.c -I ./include -L ./lib -l mymath 就可以生成可执行文件mytest。
不过实际上也不会这样。两个文件会放到默认搜索路径里
sudo cp -rf include/* /usr/include/
sudo cp lib/* /lib64
这时候就不需要gcc的时候不需要再去标明库了。不过这个库还是第三方库,要gcc mymain.c -lmymath才行。
库的使用
1、需要指定的头和库文件
2、如果没有默认安装到gcc、g++默认的搜索路径下,用户必须指明对应的选项,告诉编译器:头、库文件在哪里,库文件具体在哪。
3、将下载下来的库和头文件拷贝到系统默认路径下就是在Linux安装库。安装卸载的本质就是拷贝到系统特定的路径
4、如果安装的库是第三方库(语言和操作系统接口是第一方和第二方),需要正常使用,即便是已经全部安装到了系统中,gcc/g++也必须用-l指明具体库的名称,这个不能省
无论是下载的库还是源代码(编译方法),都会提供一个安装的命令,就是在拷贝到系统路径中,所有安装大部分质指令和库都需要超级用户(root)权限。
还是要制作.o文件,但命令不同
gcc -fPIC -c myadd.c
这里要生成一个与位置无关码的文件,然后剩下的操作都一样。不过用gcc打包就好
shared表示共享,动态库。然后创建两个目录,include放.h,lib放动态库,打包,然后给到用户,用户解包,两个目录放到默认搜索路径下,在然后gcc后面填上该填的东西,就出来可执行程序了。
但是没法执行,出错了,报错没有可用的目录。
虽然我们觉得已经告诉了编译器库在哪里,但是没告诉操作系统,库没有安装在操作系统中,所以运行时就出错了。但静态库可以找到,是因为静态库的连接原则是将用户所使用的二进制代码直接拷贝到目标可执行程序中,但动态库不会。
运行时,操作系统是如何查找动态库的?
1、查看环境变量:LD_LIBRARY_PATH,然后填加进去
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:路径,然后再运行就可以了(临时方案,因为重登后环境变量就恢复了)
2、so ln -s 路径/libmymath.so /lib64/libmymath.so。这就是在系统一个路径下建立一个软链接,也可以不选lib64,然后/后面写上要创建的名称。即使重登也没有关系。
3、配置文件方案
系统有配置文件ls /etc/ld.so.conf.d/,我们可以在这里再创建一个文件,打开文件,写入动态库的路径,然后sudo ldconfig就可以让所有配置文件都生效。
之前的Linux博客中写到过,动态库删掉就无用了,静态库删掉也都可以用。
静态链接形成的可执行程序中有静态库对应方法的实现。如果是多个程序,那就一些相同的代码重复出现,非常占用资源。
动态库的链接会把可执行程序中的外部符号替换为库中的具体地址。程序没有拷贝,而是替换。程序加载进内存后,程序变成了进程,创建进程的pcb,进程地址空间,映射关系等等,在地址空间的代码区,当程序运行到一个语法,比如printf,通过页表,找映射关系,找到物理空间发现没有这个printf,这时候系统就会发现printf在某个库文件中,找到动态库后就把动态库加载进物理内存中,并且动态库也会映射进地址空间的一块地方,这块地方是处于栈区和堆区之间的共享区,除此之外,printf此时也是动态库的对应部分。这些都是操作系统自动做的,当重新建立映射关系后,地址空间中,遇到printf就会找打动态库所处的空间,然后再回到代码区,再去映射,相当于一个函数跳转,只在地址空间里跳转。
加载另外一个进程后,还是上面的操作。所以它只需要在内存存一份,然后每个调用都在各自的地址空间里放入动态库就好。
库实际上也是一个文件,系统加载它就和加载文件一样。
在程序翻译链接形成可执行程序的时候,可执行程序内部有没有地址?程序没有被系统加载进内存时是有地址的,所以当被编译成可执行程序时内部已经有地址了,不过这是虚拟地址。
地址有绝对编址和相对编址,静态库放进程序的绝对编址中;动态库面临一个问题,不同的进程,运行程度不同,需要使用的第三方库是不同的,所以每个进程的共享区的空闲位置是不确定的。可以这样理解,动态库中的所有地址,都是偏移量,默认从0地址开始。库被映射进地址空间的时候,偏移量才确定。当执行到printf时,就从动态库文件的真正的起始地址 + 偏移量就找到了对应的代码。这也就是与位置无关码。
动态库和静态库同时存在,系统默认采用动态链接,gcc后面+一些确定库确定的命令后加上空格-static就会使用静态库。如果只有静态库,那创建出来的文件是一个静态库。
云服务器通常提供动态库,想要装c的静态库的话yum install -y glibc-static。C++的话是yum install -y libc+±static。
结束。