【探索Linux】—— 强大的命令行工具 P.13(文件系统 | 软硬链接 | 动态库和静态库)

首页 linux 【探索Linux】—— 强大的命令行工具 P.13(文件系统 | 软硬链接 | 动态库和静态库)

引言

在计算机科学领域中,Linux 系统一直以来都是备受推崇的操作系统之一。其中,文件系统、软硬链接、动态库和静态库是 Linux 系统中非常重要的概念,在实际应用中扮演着不可或缺的角色。

在上一篇文章中,我们了解了 Linux 系统中文件描述符、重定向以及基础 IO 操作的相关知识。在本文中,我们将继续探讨 Linux 系统中的文件系统、软硬链接、动态库和静态库等概念,旨在帮助各位大佬深入理解 Linux 系统的工作原理,为日后的开发和使用提供更加丰富的知识背景和技能支持。
一、文件系统

我们使用ls -l的时候看到的除了看到文件名,还看到了文件元数据
在这里插入图片描述
每行包含7列分别是:

    文件/目录的权限:显示为10个字符,分别表示文件类型和对应的权限。例如,-rw-r–r-- 表示一个普通文件,所有者有读写权限,其他用户只有读权限。

    链接数:表示有多少个硬链接指向该文件或目录。

    所有者(Owner):表示文件或目录的所有者。

    所属组(Group):表示文件或目录所属的组。

    文件大小(Size):以字节为单位显示文件的大小。

    最后修改时间(Last Modified):显示文件或目录最后一次修改的时间。

    文件名(File Name):显示文件或目录的名称。

ls -l读取存储在磁盘上的文件信息,然后显示出来具体来说就是像下面这样
在这里插入图片描述

PS:文件系统可以分为磁盘文件系统和虚拟文件系统,我们会针对磁盘文件进行分析,虚拟文件仅仅作为了解。
1. 磁盘文件系统

其实上面的那个信息除了通过这种方式来读取,还有一个stat命令能够看到更多信息
在这里插入图片描述
2. 磁盘结构
(1)物理结构

磁盘是计算机中最重要的存储介质之一。它通常由多个磁盘片组成,每个磁盘片都有两个表面。这些磁盘片被安装在一个轴上,称为主轴或转子,通过电动机旋转。
在这里插入图片描述
磁盘的物理结构可以分为以下几个部分:

    磁盘片:硬盘通常由多个磁盘片(disk platter)组成,磁盘片是由金属或玻璃制成的圆盘,有时也称为碟片。每个磁盘片都有两个表面,且每个表面都被涂上了一层磁性材料。

    磁头:硬盘上磁头(head)数量与磁盘片数量相同,位于读写臂(actuator arm)的末端。磁头用于读取和写入磁盘上的数据,每个表面都有一个磁头,因此对于一个双面硬盘,有两个磁头。

    唱臂和读写臂:唱臂(platter arm)和读写臂(actuator arm)是用于控制磁头的位置和移动的机构。读写臂是连接唱臂和磁头的装置,可以控制磁头在磁盘上的位置。

    磁道和扇区:磁盘被划分成多个同心圆的轨迹,每个轨迹称为一个磁道(track),磁道又被划分成多个弧形的区域,每个区域称为一个扇区(sector)。每个扇区有一个编号,用于在读写时进行寻址。

    主轴:硬盘主轴是连接所有磁盘片的中心轴线,是驱动磁盘旋转的电动机的一部分。

(2)存储结构

磁盘的存储结构指的是在磁盘上数据的组织方式,包括文件系统、分区和格式化等方面。

    文件系统:文件系统是操作系统用于管理磁盘上文件和目录的一种机制。它定义了文件和目录的命名规则、存储位置以及权限等属性。常见的文件系统有FAT、NTFS、EXT4等。

    分区:为了更好地管理磁盘空间,将一个硬盘分成多个区域称为分区。每个分区都是一个独立的逻辑驱动器,可以单独进行格式化和管理。通常,在Windows系统中,我们使用分区来对硬盘进行组织;在Linux系统中,我们使用分区和卷组来管理磁盘。

    格式化:格式化是指将磁盘初始化为一个特定文件系统类型的过程,其中包括创建文件系统结构、写入文件系统元数据、建立文件索引等。在格式化硬盘之前,必须先对其进行分区操作。常见的格式化方式有快速格式化和完全格式化两种。

    扇区和簇:扇区和簇是磁盘空间的基本单位,扇区是磁盘上最小的可读写存储单元,通常为512字节;簇是文件系统中的逻辑块大小,它是一组相邻的扇区。在读写磁盘时,通常我们会按簇的大小进行操作。

在这里插入图片描述
3. stat 命令

stat命令用于显示文件或目录的详细信息,包括文件的权限、所有者、所属组、文件大小、最后访问时间、最后修改时间、最后更改时间等。它可以帮助用户查看和管理文件系统中的文件和目录。

stat命令有很多选项,以下是一些常用选项和用法示例:

    -c选项:自定义输出格式
    通过-c选项,我们可以自定义输出格式。可以使用一些特定的格式替代符号来输出所需的信息。例如:

stat -c "File %n Size %s bytes Owner %U Group %G Permissions %A Last modified %y" 文件路径

    1

    -f选项:显示文件系统信息
    通过-f选项,我们可以显示文件系统的相关信息,例如:

stat -f .

    1

这个命令将显示当前目录所在的文件系统的相关信息。

    -t选项:以指定格式显示时间
    通过-t选项,我们可以以指定格式显示时间信息。例如:

stat -t '%F %T' 文件路径

    1

这个命令将显示文件的最后修改时间和最后访问时间,格式为YYYY-MM-DD HH:MM:SS。

    -L选项:显示符号链接所指向的文件信息
    在默认情况下,stat命令将显示符号链接文件本身的信息,而不是所指向的文件的信息。如果要显示符号链接所指向的文件信息,可以使用-L选项。例如:

stat -L /usr/local/bin/python

    1

    -x选项:显示文件的扩展属性信息
    通过-x选项,我们可以显示文件的扩展属性信息。例如:

stat -x 文件路径

    1

需要注意的是,stat命令的具体用法取决于不同操作系统和文件系统的实现。
4. Linux ext2文件系统

在这里插入图片描述

Linux的ext2(第二扩展文件系统)是一种常见的文件系统类型,它是Linux操作系统中使用的早期文件系统之一。上图为磁盘文件系统图(内核内存映像肯定有所不同),磁盘是典型的块设备,硬盘分区被划分为一个个的block。一个block的大小是由格式化的时候确定的,并且不可以更改。例如mke2fs的-b选项可以设定block大小为1024、2048或4096字节。而上图中启动块(Boot Block)的大小是确定的。

    Block Group:ext2文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相同的结构组成。政府管理各区的例子
    超级块(Super Block):存放文件系统本身的结构信息。记录的信息主要有:bolck 和 inode的总量,未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个文件系统结构就被破坏了
    GDT(Group Descriptor Table):块组描述符,描述块组属性信息,有兴趣的同学可以在了解一下块位图(Block Bitmap):Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没有被占用。
    inode位图(inode Bitmap):每个bit表示一个inode是否空闲可用。i节点表:存放文件属性 如 文件大小,所有者,最近修改时间等
    数据区:存放文件内容

二、软硬链接
1. 软连接

软连接(Symbolic Link),也称为符号链接或软链接,是一种在文件系统中创建的特殊类型的文件。软连接是一个指向另一个文件或目录的快捷方式,类似于Windows操作系统中的快捷方式。软连接可以跨越不同的文件系统,并且可以指向任意类型的文件或目录。

下面是软连接的一些特点和使用方法:

    特点:
        软连接是一个独立的文件,它保存了目标文件的路径信息。
        软连接的文件大小为目标文件名长度加上存储路径所需要的空间,通常比目标文件小很多。
        删除软连接不会影响目标文件,但删除目标文件会导致软连接失效。
        修改软连接的权限、所有者或时间戳不会影响目标文件。

    创建软连接:
    在Linux系统中,可以使用ln -s命令创建软链接,具体格式为:

    ln -s <目标文件或目录> <软连接名称>
        1

    例如,创建一个名为link的软连接指向目标文件/path/to/file:

    ln -s /path/to/file link
        1

    使用软链接:
        当你打开软连接时,实际上访问的是目标文件或目录。
        在使用软连接的程序中,可以像使用目标文件一样操作软连接,无需关心实际的存储位置。
        软连接可以用于创建快捷方式,方便访问常用的文件或目录。

需要注意的是,软连接依赖于源文件或目录的存在。如果源文件被删除或移动,软连接将会失效,打开软连接将无法找到目标文件。因此,在使用软连接时,要确保源文件或目录的稳定性。
2. 硬链接

硬链接(Hard Link)是一种在文件系统中创建的特殊类型链接,用于将多个文件名指向同一个文件。与软链接不同,硬链接直接将多个文件名与同一个inode关联起来,它们共享相同的数据块。

下面是硬链接的一些特点和使用方法:

    特点:
        硬链接创建后,多个文件名指向相同的数据块,它们没有主次之分,任何一个文件名都可以被视为原始文件。
        修改其中一个硬链接的内容或属性,其他硬链接也会受到影响。
        删除一个硬链接并不会影响其他硬链接或原始文件,只有当所有硬链接和原始文件都被删除后,才会释放文件的存储空间。

    创建硬链接:
    在Linux系统中,可以使用ln命令创建硬链接,具体格式为:

    ln <目标文件> <硬链接名称>
        1

    例如,创建一个名为link的硬链接指向目标文件/path/to/file:

    ln /path/to/file link
        1

    使用硬链接:
        通过硬链接打开文件时,实际上访问的是原始文件。
        对硬链接的修改会反映在其他硬链接和原始文件上。
        可以通过任何一个硬链接对文件进行读写操作,对其他硬链接和原始文件的影响是一样的。

需要注意的是,硬链接必须在同一文件系统中创建,无法跨越不同的文件系统。另外,硬链接不能链接目录,只能用于文件。

与软链接相比,硬链接的一个主要特点是它们共享相同的inode和数据块,因此删除一个硬链接并不会立即释放存储空间。只有当所有硬链接和原始文件都被删除后,文件的存储空间才能被完全释放。这也是硬链接的一个优点,因为即使原始文件被删除,只要还有硬链接存在,文件内容仍然可用。

硬链接提供了一种有效管理文件的方式,可以在不同位置使用不同的文件名来引用同一份数据,而无需复制数据本身。
三、动态库和静态库
1. 动态库

在Linux下,动态库(Dynamic Link Library)是一种共享的可执行代码和数据的文件。动态库在程序运行时才加载并链接到内存中,可以由多个程序共享使用,从而节省系统资源和内存空间。
(1)动态库文件扩展名

在Linux上,动态库的文件扩展名为.so(Shared Object),例如libexample.so。
(2)动态库的优点

    节省内存:多个程序可以共享同一个动态库,避免了重复加载和存储相同的代码和数据。
    灵活升级:修改动态库后,无需重新编译依赖它的程序,只需重新加载更新的动态库即可。
    运行时链接:动态库在程序运行时才进行链接过程,使得程序的可执行文件更加精简。

(3)使用动态库的三种方法

    编译时链接:
    在编译程序时,可以通过指定动态库的名称和路径来进行链接。使用-l选项指定库的名称,使用-L选项指定库的搜索路径。例如,假设你有一个名为libexample.so的动态库文件,可以按照以下方式进行编译和链接:

    gcc -o program program.c -lexample -L/path/to/library
        1

    这会将program.c源文件编译为可执行文件program,并链接到libexample.so动态库。

    运行时链接:
    在运行程序时,可以使用LD_LIBRARY_PATH环境变量来指定动态库的搜索路径。该环境变量会告诉动态链接器在运行程序时从指定的路径中查找动态库文件。例如:

    export LD_LIBRARY_PATH=/path/to/library
    ./program
        1
        2

    这会将/path/to/library路径添加到动态链接器的搜索路径,并执行名为program的可执行文件。

    动态加载:
    动态加载是一种在程序运行时手动加载和链接动态库的方式。使用动态加载,程序可以根据需要在特定时刻加载和卸载动态库,提供更大的灵活性。在Linux环境下,可以使用dlfcn.h头文件中定义的函数来进行动态加载。以下是一个简单的示例:

    #include <stdio.h>
    #include <dlfcn.h>

    int main() {
        void *handle;
        void (*function)();

        handle = dlopen("/path/to/library/libexample.so", RTLD_LAZY);
        if (handle) {
            function = dlsym(handle, "example_function");
            if (function) {
                function();
            }
            dlclose(handle);
        }

        return 0;
    }


    上述代码使用dlopen函数加载动态库,使用dlsym函数获取动态库中的函数指针,并进行调用。最后使用dlclose函数关闭动态库。

(4)常见的系统动态库

Linux操作系统提供了许多常用的系统动态库,例如:

    libc:C语言标准库,包含了许多常用的函数和工具。
    libpthread:线程支持库,提供了多线程相关的函数和工具。
    libdl:动态加载库,用于在运行时加载和链接其他动态库。

(5)自定义动态库:

    编写源代码:
    首先,你需要编写自己的C语言源代码实现所需的功能。可以将相关函数、结构体和其他必要的内容放入一个或多个源文件中。

    编写头文件:
    在编写源代码的同时,还需要编写相应的头文件(通常以.h为扩展名),其中包含了函数声明、结构体定义以及其他必要的类型和常量。头文件将用于在其他程序中引用你的动态库。

    编译生成目标文件:
    使用编译器将源代码编译为目标文件(通常以.o为扩展名)。在编译时,需要使用-fPIC选项生成与位置无关的代码。例如,假设你有一个名为example.c的源文件,可以按照以下方式进行编译:

    gcc -c -fPIC example.c -o example.o
        1

    链接生成动态库:
    将目标文件链接为动态库。使用-shared选项指定生成共享库,并使用-o选项指定输出的动态库文件名。例如,下面的命令将生成名为libexample.so的动态库文件:

    gcc -shared -o libexample.so example.o
        1

    设置库搜索路径:
    将生成的动态库文件所在的路径添加到库搜索路径中,以便其他程序能够找到并使用该动态库。可以通过以下方式来设置库搜索路径:
        将动态库文件复制到标准的库路径(如/usr/lib或/usr/local/lib)。
        使用LD_LIBRARY_PATH环境变量指定动态库搜索路径。例如:

        export LD_LIBRARY_PATH=/path/to/library
            1

    在其他程序中使用动态库:
    在其他程序中,可以使用动态库提供的函数和数据。在C语言中,需要包含相应的头文件,并使用动态库中的函数和结构体等。在编译和链接其他程序时,需要指定动态库的名称和路径。例如,假设你有一个程序main.c需要使用动态库libexample.so,可以按照以下方式进行编译和链接:

    gcc main.c -o program -lexample -L/path/to/library
        1

这里的-L 指定库路径-l 指定库名,这会将main.c源文件编译为可执行文件program,并链接到libexample.so动态库。
2. 静态库

在Linux中,静态库是一种包含多个目标文件的归档文件(Archive File),它们被打包在一个单独的文件中,以便能够轻松地将它们链接到程序中。在编译源代码时,可以将静态库链接到可执行文件中,使其包含所需的函数和符号,使程序能够成功运行。

静态库通常以“.a”为扩展名,并使用ar命令打包。在创建静态库之前,必须先编译源文件并将它们转换为目标文件(.o)。然后,将这些目标文件打包成一个静态库,这可以通过以下命令完成:

ar rcs libxxx.a file1.o file2.o file3.o

    1

其中“libxxx.a”是你要创建的库的名称,“file1.o”、“file2.o”、“file3.o””是你要打包成库的目标文件。

使用静态库的好处是,它们可以保证程序的可移植性和稳定性,因为所有必需的代码都被打包在单个文件中,并且与可执行文件一起分发。这意味着,你不需要安装其他库或依赖项,因为所有的功能都包含在单个文件中。此外,由于静态库直接链接到程序,因此可以获得更好的性能和速度。

但是,静态库也有一些缺点。首先,它们会增加可执行文件的大小,因为所有必需的代码都被打包在一起。其次,如果多个程序使用相同的静态库,则每个程序都会包含该库的副本,这可能会导致浪费空间和资源。
自定义静态库

下面是用C语言在Linux系统下创建自定义静态库的详细步骤:

    编写源代码:
    首先,你需要编写自己的C语言源代码实现所需的功能。可以将相关函数、结构体和其他必要的内容放入一个或多个源文件中。注意,在编写源代码时,应该遵循良好的编程实践,包括添加注释和错误处理等。

    编写头文件:
    在编写源代码的同时,还需要编写相应的头文件(通常以.h为扩展名),其中包含了函数声明、结构体定义以及其他必要的类型和常量。头文件将用于在其他程序中引用你的静态库。确保头文件中包含了所有必要的内容,以便在其他程序中正确地使用静态库。

    生成目标文件:
    使用编译器将源代码编译为目标文件(通常以.o为扩展名)。例如,假设你有一个名为example.c的源文件,可以按照以下方式进行编译:

    gcc -c example.c -o example.o
        1

    如果有多个源文件,可以分别进行编译:

    gcc -c file1.c -o file1.o
    gcc -c file2.c -o file2.o
        1
        2

    打包为静态库:
    将所有目标文件打包为静态库文件。使用ar命令创建静态库,指定其名称和包含的目标文件。例如,下面的命令将生成名为libexample.a的静态库文件:

    ar rcs libexample.a example.o
        1

    如果有多个目标文件,可以同时进行打包:

    ar rcs libexample.a file1.o file2.o
        1

    使用静态库:
    在其他程序中使用静态库提供的函数和数据。在C语言中,需要包含相应的头文件,并使用静态库中的函数和结构体等。在编译和链接其他程序时,需要指定静态库的名称和路径。例如,假设你有一个程序main.c需要使用静态库libexample.a,可以按照以下方式进行编译和链接:

    gcc main.c -o program /path/to/libexample.a
        1

    这会将main.c源文件编译为可执行文件program,并链接到libexample.a静态库。

在使用静态库时,需要注意以下几点:

    静态库的所有代码都将被复制到编译后的可执行文件中,因此它们将占用更多的磁盘空间。
    如果静态库中的代码发生了更改,则必须重新编译和链接使用该库的所有程序,并重新分发更新的可执行文件。
    如果静态库依赖于其他库或共享对象,则也需要将这些库或对象链接到程序中,否则可能会导致链接错误。

144    2024-01-15 16:44:31