做淘宝店招的网站,网站登录系统源码,东莞建筑,上海住房城乡建设网站上次介绍了基础IO#xff08;二#xff09;#xff1a;Linux#xff1a;基础IO#xff08;二.缓冲区、模拟一下缓冲区、详细讲解文件系统#xff09; 文章目录 1.软硬链接1.1硬链接1.2软链接使用场景 2.动态库和静态库1.1回顾1.2静态库的制作和使用为什么要有库制作者角度…上次介绍了基础IO二Linux基础IO二.缓冲区、模拟一下缓冲区、详细讲解文件系统 文章目录 1.软硬链接1.1硬链接1.2软链接使用场景 2.动态库和静态库1.1回顾1.2静态库的制作和使用为什么要有库制作者角度ar指令—提取静态库文件gcc -l -L 来使用自己的静态库 1.3动态库制作和使用形成.o文件与生成共享库使用makefile文件整合操作开始使用库 1.4完善动态库过程gcc -I拷贝文件简化选项解决运行找不到问题 3.理解动态库的加载3.1系统角度3.2编址3.3一般程序的加载3.4动态库的加载 1.软硬链接
1.1硬链接
硬链接Hard Link是Linux系统中的一种文件链接方式它允许多个文件名指向同一个inode索引节点从而实现多个文件名指向同一个物理文件数据块。
硬链接与原始文件之间没有任何区别它们共享相同的inode和数据块因此对任意一个文件的修改都会影响其他所有硬链接指向的文件。当您修改目标文件时硬链接也会反映这些修改因为它们实际上指向同一个数据块。同样删除硬链接并不会影响目标文件的数据只是删除了硬链接与inode号的映射关系硬链接本质上是一个新的文件名它与目标文件具有相同的inode编号这种关系可以理解为新的文件名与目标文件的inode号之间的映射关系硬链接本质就是在指定的目录下插入新的文件名和目标文件的映射关系并让inode的引用计数
创建硬链接要创建硬链接可以使用ln命令语法如下
ln target link_nametarget是要创建硬链接指向的目标文件的路径。link_name是要创建的硬链接的名称。 上面的那个数字是硬链接数 文件的硬链接数是指指向该文件的硬链接的数量。每个文件都会有一个链接计数link count用来记录指向该文件的硬链接数量。 当创建一个文件时该文件的链接计数会初始化为1。当创建一个硬链接时系统会增加该文件的链接计数。当删除一个硬链接时系统会减少该文件的链接计数。只有当文件的链接计数减少到0时系统才会真正删除该文件的数据块并释放文件占用的空间。 我们知道文件都有一个与之相关联的inode结构体inode中包含了文件的元数据信息例如文件类型、大小、权限等同时也包含了一个引用计数reference count字段。 引用计数记录了指向该inode的硬链接数量也就是指向该文件的硬链接数。当创建一个硬链接时系统会增加该文件对应inode的引用计数当删除一个硬链接时系统会减少该文件对应inode的引用计数 当文件的引用计数减少到0时系统会执行以下操作 将文件的inode标记为未使用即将inode所在的数据结构标记为可重用。将文件数据块的位图标记为未使用表示这些数据块可以被其他文件使用。将文件数据块内容清空但并不立即释放磁盘空间而是保留这些数据块的位置信息以便之后写入新的数据时可以直接覆盖这些块避免频繁的磁盘写入操作提高性能。 这种方式称为延迟释放delayed freeing 1.2软链接
软链接Symbolic Link是Linux系统中用于创建文件链接的一种方式。它是一个特殊类型的文件其中包含指向另一个文件或目录的路径。软链接与硬链接不同软链接与原始文件之间是独立的它们有不同的inode编号。
软链接符号链接是一个独立的文件其中存储着指向目标文件的路径信息当系统访问软链接文件时实际上会根据软链接文件中存储的路径信息找到目标文件。软链接文件类似于Windows下的快捷方式它提供了一种间接引用目标文件的方式
创建软链接要创建软链接可以使用ln -s命令ln是Link的缩写-s代表soft
ln -s target link_nametarget是要创建软链接指向的目标文件或目录的路径。link_name是要创建的软链接的名称。 使用场景
软链接软链接本身并不包含可执行代码而是指向其他文件的路径。如果指向的文件是一个可执行文件并且符号链接本身具有执行权限那么可以通过符号链接执行目标文件。
可以在当前路径下建立一个软链接指向较深出的文件。方便我们快速找到
通过在当前路径下创建一个软链接可以方便地访问位于其他较深路径的文件。这样可以简化文件路径的输入提高操作效率同时也可以避免频繁切换目录。 假设您经常需要访问一个深层次的文件/path/to/deep/file.txt可以在当前路径下创建一个软链接比如ln -s /path/to/deep/file.txt shortcut.txt这样您只需要在当前路径下使用./shortcut.txt就可以访问目标文件了。 软链接的特性使得它可以指向任意路径下的文件包括目录。这种灵活性使得软链接成为管理文件系统中复杂结构的有用工具提高了文件系统的可访问性和可操作性。 硬链接一个目录下有多少个目录可以通过当前目录的硬链接个数减2 Linux中不能给目录建立硬链接! 因为环路问题不允许给目录建立硬链接! 除非系统自己给目录建立硬链接.和.. 2.动态库和静态库
1.1回顾 ldd是一个Linux命令用于打印出一个可执行文件或共享库的动态链接依赖关系。通过运行ldd命令您可以查看一个可执行文件或共享库所依赖的其他库文件以及这些库文件的路径。 使用ldd命令的基本语法 ldd executable_fileexecutable_file是您要查看动态链接依赖关系的可执行文件的路径。 为什么需要库 库是预先编译的可重用代码的集合用于提供常用功能和服务。通过使用库开发人员可以避免重复编写相同的代码提高代码重用性和开发效率。此外库还可帮助开发人员处理系统差异性使得程序在不同系统上能够正确运行。 有哪些类型的库 库分为动态库和静态库两种类型。动态库Dynamic Link Libraries在程序运行时加载到内存中而静态库Static Libraries在编译时被链接到可执行文件中。在云服务器中默认安装的是动态库。云服务器是默认安装动态库的没有安装静态库 如何查询程序的依赖关系 使用ldd命令可以查询一个可执行文件所依赖的动态链接库。 什么是静态链接 静态链接是将库的代码和数据在编译时直接复制到可执行文件中的链接方式。通过在编译时静态链接库可生成一个独立于系统环境的可执行文件。 默认编译程序时使用的是动态编译。如果想要使用静态编译需要加上-static选项库的命名规则 动态库通常以libXXX.so的形式命名而静态库通常以libXXX.a的形式命名在库的真实名称中通常会去除lib前缀和.so或.a后缀只保留名称部分。剩下的就是真实名称
1.2静态库的制作和使用
为什么要有库
提高代码的重用性和开发效率库中包含了经过封装和优化的代码片段可以提供常用功能和服务。通过使用库开发人员可以避免重复编写相同的代码提高代码的重用性和开发效率。隐藏源代码简化开发库提供了现成的解决方案和功能模块可以帮助开发人员快速构建应用程序减少开发时间和工作量。提高可靠性经过广泛测试和验证的库通常具有较高的可靠性和稳定性可以减少程序中的错误和bug提高程序的质量。处理系统差异性不同操作系统和平台之间存在差异库可以帮助开发人员处理这些差异性使得程序能够在不同系统上正确运行。提供标准接口库通常提供了标准化的接口和API开发人员可以通过这些接口与库进行交互而不必关心底层实现细节。
制作者角度 这里我们写好了4个文件分别是mymath.c、mymath.h、mystdio.c、mystdio.h
使用gcc -c
用于将源代码编译为目标文件object file。目标文件包含了机器代码但它还不是一个完整的可执行程序因为它还缺少一些信息如启动代码、库函数的链接等。
-c 选项告诉 GCC 只进行编译阶段不进行链接阶段。这样你可以得到 .oobject file后缀的目标文件而不是可执行文件。而且文件默认生成的名字与源文件相同改下后缀 我们把二者进行打包传给user(给这个user使用)
ar指令—提取静态库文件
ar命令是一个用于创建、修改和提取静态库文件的工具。静态库是编译后的程序代码集合包含一组函数或其他对象文件可以在链接时与可执行文件一起使用。通过将多个目标文件合并到一个静态库中可以将其作为单个实体进行管理和分发有助于减小可执行文件的大小和编译时间。
ar命令的基本语法如下ar [参数选项] [归档文件名] [目标文件列表]。 c创建归档文件。r向归档文件中添加目标文件。d从归档文件中删除目标文件。t列出归档文件中包含的目标文件列表。x从归档文件中提取目标文件。a在库的一个已经存在的成员后面增加一个新的文件。b在库的一个已经存在的成员前面增加一个新的文件。m移动成员在库中的位置。u替换或更新库中的成员。v显示操作过程。 ar -rc libmyc.a *.o写一份测试代码来使用一下这个库
#include mymath.h
#include mystdio.h
#include stdio.hint main()
{int res myAdd(10, 20);printf(%d%d%d\n, 10, 20, res);myFILE *fp my_fopen(log.txt, w);if(fp NULL) return 1;return 0;
}gcc -l -L 来使用自己的静态库 在GCC编译器中-l选项用于链接库文件。它后面跟着要链接的库的名称不包括前缀“lib”和扩展名。例如如果想链接一个名为libmath.so的数学库可以使用-lmath选项。GCC会在默认的库路径中搜索该库并将其链接到生成的可执行文件中。 此外-l选项仅仅告诉GCC在链接阶段使用哪个库但它并不指定库文件的搜索路径。 如果需要指定库文件的搜索路径可以使用-L选项。例如-L/path/to/library将告诉GCC在/path/to/library目录下搜索库文件。 1.3动态库制作和使用
形成.o文件与生成共享库
上面我讲解了静态库的制作和使用我们在形成.o文件时都是使用gcc -c code.c code.o。我们在打包时也是使用功能ar
现在我们使用
shared: 当我们在编译或链接一个库时我们通常会指定它应该是一个共享库。这意味着该库的文件格式是为了与其他程序共享而设计的。在GCC或其他类似的编译器中通常使用-shared选项来指示应该生成一个共享库。(共享库就是动态链接库)fPIC (Position Independent Code): 位置无关代码PIC是一种代码类型它不依赖于它在内存中的具体地址来运行。这是共享库所需要的因为共享库可以在程序的运行时被加载到任何内存地址。使用-fPIC选项在GCC中告诉编译器生成这样的代码产生与位置无关码。库名规则libxxx.so: 在Linux系统中共享库通常遵循特定的命名约定。它们通常以lib开头后跟库的名字例如xxx并以.so结尾。这里的.so代表“shared object”即共享对象
为了创建一个名为libmyc.so的共享库使用如下的命令
gcc -shared -fPIC -o libmyc.so *.o使用makefile文件整合操作
makefile文件内容如下
libmyc.so:mymath.o mystdio.ogcc -shared -o $ $^
mymath.o:mymath.cgcc -c -fPIC $
mystdio.o:mystdio.cgcc -c -fPIC $.PHONY:clean
clean:rm *.o libmyc.so -rf过程与细节 首先Make 工具会查找当前目录下的 Makefile 文件并读取其中的规则和指令。Make 工具会检查每个规则中的目标文件target和依赖文件prerequisites的时间戳以确定哪些文件需要重新生成。如果某个目标文件不存在或者某个依赖文件的时间戳比目标文件的时间戳更新那么 Make 工具会执行该规则中定义的命令来生成目标文件。对于上述的 Makefile 文件首先会检查 libmyc.so 文件是否存在以及其依赖文件 mymath.o 和 mystdio.o 的时间戳情况。使用 make 指令时Makefile 会按照默认规则执行第一个目标在这里是 libmyc.so并且只会执行第一个目标所依赖的规则。在这个例子中libmyc.so 依赖于 mymath.o 和 mystdio.o因此会执行这两个规则来生成对应的目标文件 在 Makefile 中$ 是一个自动变量用于表示当前规则中的第一个依赖文件prerequisite。当 Make 工具执行规则时$ 会被替换为当前规则中的第一个依赖文件的文件名。 使用 $^ 会将所有的依赖文件传递给命令而使用 $ 只会传递当前规则中的第一个依赖文件。如果有多个依赖文件$ 会在每个依赖文件上执行一次。 因此在 Makefile 中如果有多个依赖文件并且在命令中使用了 $那么命令会在每个依赖文件上执行一次。 也能写成这样
libmyc.so:mymath.o mystdio.ogcc -shared -o $ $^
%.o:%.cgcc -c -fPIC $.PHONY:clean
clean:rm *.o libmyc.so -rf%.o: %.c 这个规则是一个模式规则pattern rule表示所有以 .o 结尾的目标文件依赖于对应的 .c 源文件。在生成目标文件的命令中使用了 $。$ 表示当前规则中的第一个依赖文件即对应的 .c 源文件。因此命令 gcc -c -fPIC $ 会将对应的 .c 源文件编译成目标文件。 开始使用库
首先把.h文件交给使用者
cp ./*.h user1.4完善动态库过程
首先我们完善一下makefile文件添加能生成一个压缩包里面分有include目录和lib目录分别放有头文件和库
libmyc.so:mymath.o mystdio.ogcc -shared -o $ $^
mymath.o:mymath.cgcc -c -fPIC $
mystdio.o:mystdio.cgcc -c -fPIC $.PHONY:clean
clean:rm *.o libmyc.so -rf.PHONY:output
output:mkdir -p mylib/includemkdir -p mylib/libcp -rf *.h mylib/includecp -rf *.so mylib/libtar czf mylib.tgz mylib使用make output后就能生成满足要求的压缩包 现在如果直接进行编译会报错 gcc -I
在使用 gcc 编译器时-I 选项用于指定头文件的搜索路径在展开头文件时。头文件通常包含在 #include 指令中用于引入外部库或自定义的头文件。告诉编译器可以在-1指定的路径下进行搜索头文件
gcc -I include_path其中 include_path 是指定的头文件搜索路径。您可以在 -I 后面指定多个路径用冒号或空格分隔。
现在要使用
gcc -I ./mylib/include -lmyc -L ./mylib/lib/libmyc.so main.c-I ./mylib/include这个选项告诉编译器在 ./mylib/include 目录中查找头文件。在编译过程中编译器会在指定的路径中搜索您在源代码中包含的头文件。 -lmyc这个选项告诉编译器链接名为 libmyc.so 的库文件。通常-l 选项后面跟着的是库文件的名称编译器会在指定的库路径中查找该库文件。 -L ./mylib/lib这个选项告诉编译器在 ./mylib/lib 目录中查找库文件。编译器会在指定的路径中搜索您指定的库文件以便在链接阶段正确地链接库文件。 拷贝文件简化选项 所谓的把库(其他软件)安装到系统中本质就是把对应的文件拷贝到系统指定的路径中机器规定好的一般都是root的目录 所以经常需要我们普通用户使用sudo 我们可以把自己的头文件与库分别拷贝到系统头文件目录下和库目录下
sudo mylib/include/* /usr/includesudo mylib/lib/libmyc.so /lib64这样后直接使用
gcc -lmyc main.c就能实现编译链接 还是不建议大家把自己写的不是很成熟的代码放到系统库里 解决运行找不到问题 链接生成可执行程序后但在执行可执行文件时出现 “not found” 错误通常是由于系统无法找到所需的动态库文件导致的。这种情况通常发生在链接的是动态库并且操作系统无法实时找到该动态库的情况下。所以对于动态库编译器要能找到OS也要能找到
解决这个问题的方法可以采用以下几种方式 默认路径拷贝 将头文件.h 文件和动态库文件.so 文件拷贝到系统默认路径如 /usr/include 和 /lib/ 目录中。 增加LD_LIBRARY_PATH环境变量 使用环境变量 LD_LIBRARY_PATH 来指定动态库的路径系统运行程序时动态库查找的辅助路径 没有的话可以通过以下命令设置 export LD_LIBRARY_PATH/path/to/your/library:$LD_LIBRARY_PATH本身就有的话使用以下命令把自己的库的绝对路径添加到LD_LIBRARY_PATH里面 export LD_LIBRARY_PATH$LD_LIBRARY_PATH:自己库的绝对路径$LD_LIBRARY_PATH在 Shell 脚本或命令中$LD_LIBRARY_PATH 表示引用环境变量 LD_LIBRARY_PATH 的值。使用 $ 符号可以获取环境变量的值而不是直接使用变量名。因此$LD_LIBRARY_PATH 将会被替换为 LD_LIBRARY_PATH 环境变量的当前值。 LD_LIBRARY_PATHLD_LIBRARY_PATH 是一个环境变量用于指定动态链接库共享库的搜索路径。通过设置 LD_LIBRARY_PATH 环境变量您可以告诉系统在哪些路径中查找动态链接库。这个环境变量在编译和运行需要动态链接库的程序时非常有用。 使用软连接 使用 ln -s 命令创建动态库文件的软链接到系统默认搜索路径中例如 sudo ln -s /path/to/my/libmyc.so /lib64/libmyc.so这样可以在默认搜索路径中建立软链接使系统能够找到动态库文件。 修改配置文件 在 /etc/ld.so.conf.d/ 目录下新建文件并在文件中加入自定义库的路径。然后运行 sudo ldconfig 命令来更新系统的动态链接库配置使系统能够实时找到动态库文件。 如果是官方的库的话我们最好还是直接使用方法一。这样方便我们查找 自己的库的话使用方法三就行 3.理解动态库的加载
动态库的加载是指在程序运行时操作系统会将动态库加载到内存中并将程序与动态库建立链接以便程序能够调用动态库中的函数和资源。动态库的加载是延迟加载的即在程序需要调用动态库中的函数时才会加载相应的库。 静态库在编译时会被整合到可执行文件中因此在程序运行时不需要额外加载库文件。静态库的所有代码和数据都会被复制到可执行文件中因此程序在运行时可以独立执行不需要依赖外部的库文件。 总的来说动态库的加载是指在程序运行时将库文件加载到内存中并建立链接关系使得程序能够调用库中的函数和资源。而静态库在编译时已经被整合到可执行文件中因此在程序运行时不需要加载外部库文件。 动态库、共享库的本质就是所有系统进程中公共的代码和数据只需要存在一份! 3.1系统角度 在程序加载时代码通常被加载到进程的虚拟地址空间中的代码段中。当程序执行到调用库函数的代码时CPU会跳转到库函数的代码所在的内存地址并开始执行库函数的代码 在动态库加载之后动态库的代码和数据会被映射到进程的共享区中使得进程可以直接访问和调用动态库中的函数和资源 当一个动态库已经加载到物理内存中已有进程正在使用该库时如果另一个进程也需要使用同一个动态库操作系统会采取共享内存的方式使新的进程的地址空间直接映射到已加载的动态库的内存处 谁来决定那些库加载了哪些没加载0S会自动完成 在Linux系统中决定哪些库会加载哪些库不会加载的主要责任在于动态链接器dynamic linker和运行时链接器runtime linker。这两个组件是操作系统的一部分负责在程序运行时动态加载和链接库文件。 在操作系统中可以同时存在大量的已加载库这些库可能是系统自带的标准库、第三方库或用户自定义的库。操作系统需要管理这些库以确保程序能够正确运行并提供良好的性能。 先描述再组织 3.2编址
编址是指为内存中的每个存储单元分配一个唯一的地址以便程序可以准确地访问和操作内存中的数据。在计算机系统中编址是非常重要的因为程序需要通过地址来定位和访问内存中的数据和指令。编址的过程涉及到操作系统、编译器以及可执行程序本身的格式信息。 尽管可执行程序还没有被加载到内存中但它仍然具有地址的概念。在编译过程中编译器会为程序中的各个变量、函数等符号分配地址这些地址通常是相对地址或者符号表中的偏移量。这样即使程序还没有被加载到内存中各个符号仍然具有自己的地址。 此外可执行程序在磁盘上已经被划分为不同的区域这些区域通常包括代码段、数据段、符号表等。这些区域的划分通常是在编译器生成可执行文件时完成的根据程序的结构和需要编译器会将程序划分为不同的区域并为每个区域分配相应的权限和访问属性。 因此即使可执行程序还没有被加载到内存中它仍然具有地址的概念并且已经被划分为不同的区域。这些地址和区域的信息在程序加载到内存时会被操作系统读取并根据这些信息将程序的各个部分映射到进程的虚拟地址空间中。 所以我们之前讲解的进程地址空间里的虚拟地址其实是由编译器在编译过程生成的。后来由操作系统读取成为虚拟地址
编址方式有两个绝对编址和相对编址/逻辑编址
绝对编址平坦模式地址都是连续的相对编址/逻辑编址会为每个不同的区域的开始处start分配一个地址后其后地址为相对于start的偏移量0、1、2····、n
对于平坦模式Linux在使用我们能认为可执行程序只有一个区域地址start偏移量现在start0偏移量就是本身的绝对编址
3.3一般程序的加载 在CPU中CR3Control Register 3是一种控制寄存器用于存储页目录表的物理地址。在x86架构的处理器中CR3寄存器用于指示当前进程的页目录表的物理地址从而实现虚拟内存的地址转换。当处理器需要访问内存时会使用CR3寄存器中存储的页目录表地址来查找对应的页表进而将虚拟地址转换为物理地址。 PC指针Program Counter是另一个重要的寄存器用于存储当前正在执行的指令的地址或下一条将要执行的指令的地址。PC指针在程序执行过程中不断更新指向当前指令或即将执行的下一条指令的地址。处理器根据PC指针中存储的地址来获取下一条指令的内容并执行相应的操作。 地址空间既然是一个数据结构对象谁来用什么数据初始化呢——使用ELF格式的表头信息来进行初始化 在操作系统中地址空间的初始化通常是由操作系统内核来完成的。当一个进程被创建时操作系统会负责为该进程分配新的地址空间并初始化这个地址空间。在这个过程中操作系统可以使用可执行和可加载文件的头部信息如ELF格式的表头信息来进行初始化 ELFExecutable and Linkable Format是一种常见的可执行文件格式用于存储可执行程序的代码、数据和其他相关信息。ELF文件包含了各种表头信息用于描述程序的布局、段的位置、大小、权限等信息。操作系统可以解析这些表头信息根据其中的段表Section Header Table和程序头表Program Header Table等内容来初始化进程的地址空间。 所谓的地址空间本质是由操作系统 编译器 计算机体系结构(CPU)三者共同配合完成的 OS要能构建进程进程地址空间页表等编译器要能在编译时产生逻辑地址和表头信息供地址空间与页表的初始化CPU要有相关寄存器cr3与pc指针等还要能从虚拟地址转换为物理地址 3.4动态库的加载 动态库库的数据和方法的访问都是可以通过库在地址空间 起始地址我们程序内部的偏移量即可 现在IO基础也是全部讲完啦。下面开始进程间通讯了这周是考试周好累啊