Linux环境搭建
2024-06-18 14:46:26 4 举报
AI智能生成
Linux后端
作者其他创作
大纲/内容
库
简介
库文件是计算机上的一类文件,可以简单的把库文件看成一种代码仓库,它提供给使用
者一些可以直接拿来用的变量、函数或类。
者一些可以直接拿来用的变量、函数或类。
库是特殊的一种程序,编写库的程序和编写一般的程序区别不大,只是库不能单独运行
结构
文件头
包含元数据。如归档格式的版本信息
归档成员
这些是实际的对象文件或其他成员文件,每个都有一个独立的头部来描述其属性(如文件名、时间戳、所有者、权限等)
符号表(可选)
这是一个索引,列出库中所有全局符号(变量和函数名)以及它们在哪个归档成员中可以找到。这加速了链接过程
类型
静态库
库在程序的链接阶段被复制
到了程序中
到了程序中
制作
指令作用
ar
创建、修改和提取从归档文件(也称为库文件)的 UNIX 工具
r
替换。如果库中已经有一个与你要添加的对象文件(.o 文件)同名的文件,ar 会用新的文件替换它。如果库中没有同名的文件,ar 会添加新的文件
c
创建。如果指定的库(在这里是 libxxx.a)不存在,这个选项告诉 ar 创建一个新的库。
s
索引。这个选项会创建一个对象文件的索引,以加速链接器(linker)的查找速度
没有这个索引,链接过程可能会很慢,因为链接器需要搜索整个库来找到需要的对象文件
使用
gcc (源代码.c) -o (可执行文件名称) -I (头文件路径) -l (库文件名称) -L(库文件路径)
优点
加载速度快
程序无需提供静态库,移植方便
缺点
消耗系统资源,浪费内存
更新部署发布麻烦
动态库
在链接阶段没有被复制到程序中,而是程序在运行时由系统动态加
载到内存中供程序调用
载到内存中供程序调用
制作
-fpic/-fPIC
生成位置无关代码
这种代码可以在内存中的任何位置执行,而不依赖于绝对地址
当操作系统加载一个动态库时,它通常会将其放置在可用的内存地址空间的某个位置。这个位置在不同的运行实例或不同的程序中可能会有所不同。因此,动态库需要能够在不同的地址空间中运行,而不需要重新链接或重新编译
-fpic的全局偏移表的大小受到限制,使得代码更快更小但是不适用于所有场合,特别是当共享库很大或者有很多全局变量和函数的时候
工作原理
程序启动之后,动态库会被动态加载到内存中,通过 ldd (list dynamic
dependencies)命令检查动态库依赖关系
dependencies)命令检查动态库依赖关系
如何定位共享库文件
当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路
径。此时就需要系统的动态载入器来获取该绝对路径。
径。此时就需要系统的动态载入器来获取该绝对路径。
对于elf格式的可执行程序,是
由ld-linux.so来完成的,它先后搜索elf文件的 DT_RPATH段 ——> 环境变量
LD_LIBRARY_PATH ——> /etc/ld.so.cache文件列表 ——> /lib/,/usr/lib
目录找到库文件后将其载入内存
由ld-linux.so来完成的,它先后搜索elf文件的 DT_RPATH段 ——> 环境变量
LD_LIBRARY_PATH ——> /etc/ld.so.cache文件列表 ——> /lib/,/usr/lib
目录找到库文件后将其载入内存
export LD_LIBRARY_PATH:(so文件的绝对路径(可以使用pwd查看))并加入到.bashrc中去
将动态库文件添加到环境变量中让动态载入器找到程序
全局偏移表(GOT,Global Offset Table)
一种机制,用于在程序和其依赖的多个库之间实现符号(通常是函数或全局变量)的动态解析
工作步骤
初始化
在程序启动时,动态链接器会初始化 GOT。这通常涉及将 GOT 中的一些条目设置为指向动态链接器自身的代码,这些代码用于解析尚未解析的符号
第一次访问
当程序第一次访问一个动态链接的符号时,控制权会传递给动态链接器的特殊代码。这个代码会查找符号的实际地址,并更新 GOT 中的相应条目
后续访问
一旦 GOT 中的条目被更新,后续对同一符号的访问就会非常快,因为程序会直接从 GOT 中获取地址,而无需再次调用动态链接器
优点
在第一次解析之后,符号的查找和访问非常快。
多个程序实例可以共享相同的库代码,但只需要一个 GOT 副本。
缺点
动态解析会增加程序启动时的延迟。
需要更复杂的链接器和加载器支持。
优点
内存效率更高
多个程序用同一份内存副本
模块化和易于更新
如果需要更新库中的某个函数,只需要替换库文件本身,而不需要重新编译使用该库的所有程序
缺点
依赖问题
如果库不存在或版本不匹配,程序可能无法运行
启动时间
因为需要在运行时进行符号解析和链接,使用动态库的程序通常有更长的启动时间。
复杂性
动态链接增加了程序的复杂性,因为它依赖于运行时环境和动态链接器
.o或者.obj或者.so文件是二进制代码
因此库文件和头文件应该一起分发,否则使用者不清楚库中有什么
优点
代码保密
方便部署分发
Makefile
简介
自动化编译软件项目的特殊文件
make 命令工具的配置文件,包含一组规则和依赖关系,用于指导如何构建目标文件
文件命名和规则
示例
工作原理
命令在执行之前,需要先检查规则中的依赖是否存在
如果存在,执行命令
如果不存在,向下检查其它的规则,检查有没有一个规则是用来生成这个依赖的,
如果找到了,则执行该规则中的命令
如果找到了,则执行该规则中的命令
检测更新,在执行规则中的命令时,会比较目标和依赖文件的时间
如果依赖的时间比目标的时间晚,需要重新生成目标
如果依赖的时间比目标的时间早,目标不需要更新,对应规则中的命令不需要被
执行
执行
变量
自定义变量
变量名=变量值 var=hello
预定义变量
AR
归档维护程序的名称,默认值为 ar
CC
C 编译器的名称,默认值为 cc
CXX
C++ 编译器的名称,默认值为 g++
$@
目标的完整名称
$<
第一个依赖文件的名称
$^
所有的依赖文件
获取变量的值
$(变量名)
$(var)
模式匹配
函数
$(wildcard PATTERN...)
功能
获取指定目录下指定类型的文件列表
参数
PATTERN 指的是某个或多个目录下的对应的某种类型的文件,如果有多
个目录,一般使用空格间隔
个目录,一般使用空格间隔
返回
得到的若干个文件的文件列表,文件名之间使用空格间隔
示例
$(patsubst <pattern>,<replacement>,<text>)
功能
查找<text>中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合
模式<pattern>,如果匹配的话,则以<replacement>替换
模式<pattern>,如果匹配的话,则以<replacement>替换
<pattern>可以包括通配符`%`,表示任意长度的字串。如果<replacement>
中也包含`%`,那么,<replacement>中的这个`%`将是<pattern>中的那个%
所代表的字串。(可以用`\`来转义,以`\%`来表示真实含义的`%`字符)
中也包含`%`,那么,<replacement>中的这个`%`将是<pattern>中的那个%
所代表的字串。(可以用`\`来转义,以`\%`来表示真实含义的`%`字符)
返回
函数返回被替换过后的字符串
示例
GDB
简介
由 GNU 软件系统社区提供的调试工具,同 GCC 配套组成了一套完整的开发环
境,GDB 是 Linux 和许多类 Unix 系统中的标准开发环境
境,GDB 是 Linux 和许多类 Unix 系统中的标准开发环境
功能
启动程序,可以按照自定义的要求随心所欲的运行程序
可让被调试的程序在所指定的调置的断点处停住(断点可以是条件表达式)
当程序被停住时,可以检查此时程序中所发生的事
可以改变程序,将一个 BUG 产生的影响修正从而测试其他 BUG
准备工作
通常,在为调试而编译时,我们会()关掉编译器的优化选项(`-O`), 并打开调
试选项(`-g`)。另外,`-Wall`在尽量不影响程序行为的情况下选项打开所有
warning,也可以发现许多问题,避免一些不必要的 BUG。
试选项(`-g`)。另外,`-Wall`在尽量不影响程序行为的情况下选项打开所有
warning,也可以发现许多问题,避免一些不必要的 BUG。
gcc -g -Wall program.c -o program
`-g` 选项的作用是在可执行文件中加入源代码的信息,比如可执行文件中第几条机
器指令对应源代码的第几行,但并不是把整个源文件嵌入到可执行文件中,所以在调
试时必须保证 gdb 能找到源文件
器指令对应源代码的第几行,但并不是把整个源文件嵌入到可执行文件中,所以在调
试时必须保证 gdb 能找到源文件
GDB命令
启动、退出、查看代码
启动和退出
gdb 可执行程序
quit
给程序设置参数/获取设置参数
set args 10 20
show args
show args
GDB 使用帮助
help
查看当前文件代码
list/l
从默认位置显示
list/l 行号
从指定的行显示
list/l 函数名
从指定的函数显示
查看非当前文件代码
list/l 文件名:行号
list/l 文件名:函数名
设置显示的行数
show list/listsize
set list/listsize 行数
断点操作
设置断点
b/break 行号
b/break 函数名
b/break 文件名:行号
b/break 文件名:函数
查看断点
i/info b/break
删除断点
d/del/delete 断点编号
设置断点无效
dis/disable 断点编号
设置断点生效
ena/enable 断点编号
设置条件断点(一般用在循环的位置)
b/break 10 if i==5
调试命令
运行GDB程序
start(程序停在第一行)
run(遇到断点才停)
继续运行,到下一个断点停
c/continue
向下执行一行代码(不会进入函数体)
n/next
变量操作
p/print 变量名(打印变量值)
ptype 变量名(打印变量类型)
向下单步调试(遇到函数进入函数体)
s/step
finish(跳出函数体)
自动变量操作
display 变量名(自动打印指定变量的值)
i/info display
undisplay 编号
其它操作
set var 变量名=变量值 (循环中用的较多)
until (跳出循环)
文件IO
两种IO操作方式
标准C库IO函数
特点
高级接口
标准C库(通常是 <stdio.h>)提供了一组高级的I/O接口,如 fopen(), fclose(), fread(), fwrite(), printf(), scanf() 等
缓冲机制
标准C库提供了缓冲机制,可以减少系统调用的次数,提高I/O效率
跨平台
由于标准C库是ANSI C标准的一部分,因此其I/O函数具有跨平台性
方便和易用
函数接口设计得相对简单,易于使用和理解
Linux系统IO
特点
低级接口
Linux系统I/O(通常是 <unistd.h> 和 <fcntl.h>)提供了一组比较低级的接口,如 open(), close(), read(), write() 等。
灵活性和控制性
由于接口是低级的,因此提供了更多的控制选项
没有缓冲机制
默认情况下,系统I/O是没有缓冲的,每次I/O操作都会导致系统调用。
依赖于操作系统
这些接口通常是特定于操作系统的,因此在不同的操作系统上可能需要不同的实现。
关系
封装关系
标准C库I/O实际上是对系统I/O的一层封装
在Linux系统中,当你调用 fread() 或 fwrite() 这样的函数时,底层最终还是会调用 read() 或 write() 这样的系统调用
缓冲区差异
标准C库I/O通常有一个用户空间的缓冲区,用于减少系统调用的次数
而系统I/O通常没有这样的缓冲机制,除非程序员自己实现。
性能差异
由于标准C库I/O有缓冲机制,因此在某些场景下可能比系统I/O更高效
然而,系统I/O由于其更低级的控制,可以在需要高度定制化I/O行为的场景下表现得更好
适用场景
标准C库I/O更适用于文件操作和文本处理等高级应用,而系统I/O更多地用于需要低级控制的场合,如网络编程、设备控制等
虚拟地址
更多可见思维导图虚拟内存
文件描述符
简介
一个用于识别已打开文件或I/O流的整数标识符
文件描述符是一个非常底层的概念,它提供了一个抽象层,使得操作系统可以统一地处理各种I/O操作,不仅包括传统的文件,还包括套接字(sockets)、管道(pipes)等
特点
整数标识符
文件描述符通常是一个非负整数。按照传统,0、1、2 分别被用作标准输入(stdin)、标准输出(stdout)、和标准错误(stderr)的文件描述符
全局唯一
在同一个进程中,每个文件描述符是唯一的
进程级别
文件描述符是进程级别的资源,通常不会被继承(除非设置了特定标志)。
多重使用
一个文件描述符可以用于文件、套接字、管道等多种类型的I/O操作
限制
数量限制
每个进程通常有一个文件描述符的数量限制
生命周期
文件描述符在进程终止时会被自动关闭,除非特别设置了某种标志(如 FD_CLOEXEC)
stat 结构体
存储关于一个文件(或其他类型的文件系统对象,如目录、设备等)的各种属性和元数据
主要字段
dev_t st_dev
存储设备的 ID
ino_t st_ino
文件的 inode 编号。
这是文件在文件系统中的唯一标识
mode_t st_mode
文件类型和访问权限
这个字段通常用一系列宏来解析,如 S_ISREG(普通文件)、S_ISDIR(目录)以及权限位(S_IRWXU、S_IRWXG、S_IRWXO 等)。
nlink_t st_nlink
连到该文件的硬连接数目
uid_t st_uid
文件所有者的用户 ID
gid_t st_gid
文件所属组的组 ID
dev_t st_rdev
若文件是特殊设备文件,此字段则存储特殊设备的 ID
off_t st_size
文件大小(以字节为单位)
blksize_t st_blksize
文件系统的 I/O 块大小
blkcnt_t st_blocks
文件占用的磁盘块数
time_t st_atime
最后一次访问时间
time_t st_mtime
最后一次修改时间
time_t st_ctime
最后一次改变时间(指属性)
stat和lstat函数
用来获取文件信息
if(stat("somefile.txt", &fileStat) < 0)
当文件是一个符号链接时,stat 返回的是链接目标的信息,而 lstat 返回的是符号链接本身的信息
时间字段可能有不同的精度和范围,具体取决于操作系统和文件系统
st_mode变量
dirent 结构体
包含了与目录项相关的一些基本信息
主要字段
ino_t d_ino;
此目录进入点的inode
off_t d_off;
目录文件开头至此目录进入点的位移
unsigned short int d_reclen;
d_name 的长度, 不包含NULL字符
unsigned char d_type
DT_BLK - 块设备
DT_CHR - 字符设备
DT_DIR - 目录
DT_LNK - 软连接
DT_FIFO - 管道
DT_REG - 普通文件
DT_SOCK - 套接字
DT_UNKNOWN - 未知
DT_CHR - 字符设备
DT_DIR - 目录
DT_LNK - 软连接
DT_FIFO - 管道
DT_REG - 普通文件
DT_SOCK - 套接字
DT_UNKNOWN - 未知
char d_name[256]
文件名
文件IO函数
open函数
简介
用于打开或创建文件的系统调用
返回一个文件描述符,这个文件描述符用于后续的文件操作,例如读取(read)、写入(write)或关闭(close)文件
函数原型
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
字段含义
pathname: 要打开或创建的文件的路径。
flags指定文件应如何打开。这些标志可以通过 | 运算符进行组合。一些常用的标志包括
O_RDONLY: 以只读方式打开文件
O_WRONLY: 以只写方式打开文件
O_RDWR: 以读写方式打开文件
O_CREAT: 如果文件不存在,则创建它
O_EXCL: 与 O_CREAT 一起使用时,如果文件已经存在,则 open 调用会失败。
O_APPEND: 在写入前将文件偏移量设置为文件末尾,用于追加
O_TRUNC: 删除所有文件内容
mode
如果创建新文件,这个参数指定了新文件的权限
这是一个八进制数(通常使用八进制字面量,例如 0644 或 0755)
返回值
成功时,返回一个非负整数,即文件描述符
失败时,返回 -1 并设置 errno
read和write函数
分别用于从文件或其他 I/O 设备(如键盘、网络套接字等)中读取数据和向其中写入数据
函数原型
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
字段含义
fd: 文件描述符,通常是通过 open 函数获得的
buf: 用于存储读取/写入数据的缓冲区
count: 要读取/写入的最大字节数
返回值
成功时,返回实际读取/写入的字节数(可能小于或等于 count)
对于read函数,如果到达文件末尾,返回 0
while ((bytesRead = read(fd, buffer, sizeof(buffer))) > 0)
可以持续处理读取到的数据,存储在 buffer 中,长度为 bytesRead
失败时,返回 -1 并设置 errno
lseek函数
用于改变文件读写指针位置的系统调用
函数原型
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
off_t lseek(int fd, off_t offset, int whence);
字段含义
fd: 文件描述符,通常是通过 open 函数获得的。
offset: 偏移量,表示从 whence 参数指定的位置开始要移动多少个字节
whence: 可以是以下几个选项之一
SEEK_SET: 将文件指针设置为距离文件开头 offset 个字节。
SEEK_CUR: 将文件指针设置为当前位置加上 offset 个字节
SEEK_END: 将文件指针设置为文件结尾加上 offset 个字节(offset 通常是负数)
返回值
成功时,返回新的文件偏移量
失败时,返回 -1,并设置 errno
文件属性操作函数
int access(const char *pathname, int mode);
检查调用进程对指定文件是否有权限访问
pathname: 文件路径。
mode: 要检查的权限,如 F_OK(文件是否存在)、R_OK(可读)、W_OK(可写)和 X_OK(可执行)。
返回值
成功:返回 0
失败:返回 -1 并设置 errno
int chmod(const char *filename, int mode);
用于改变文件的权限
pathname: 文件路径。
mode: 新的权限设置,是一个八进制数。
返回值
0
-1,设置errno
int chown(const char *path, uid_t owner, gid_t group);
改变文件的所有者和所属组
pathname: 文件路径
owner: 新的所有者的用户 ID
group: 新的所属组的组 ID
返回值
0
-1,设置errno
int truncate(const char *path, off_t length);
改变一个文件的大小
pathname: 文件路径。
length: 新的文件大小
返回值
0
-1,设置errno
目录操作函数
int rename(const char *oldpath, const char *newpath);
重命名或移动文件或目录
oldpath: 原始文件或目录的路径
newpath: 新的文件或目录路径
返回值
0
-1,设置errno
int chdir(const char *path);
改变当前工作目录
path: 新的工作目录路径
返回值
0
-1,设置errno
char *getcwd(char *buf, size_t size);
获取当前工作目录
buf: 存储结果的缓冲区
size: 缓冲区的大小
返回值
0
-1,设置errno
int mkdir(const char *pathname, mode_t mode);
创建一个新的目录
pathname: 新目录的路径
mode: 新目录的权限设置
返回值
0
-1,设置errno
int rmdir(const char *pathname);
删除一个空目录
pathname: 要删除的目录的路径
返回值
0
-1,设置errno
目录遍历函数
DIR *opendir(const char *name);
打开一个目录流
name: 要打开的目录的路径
返回值
成功:返回一个指向 DIR 结构的指针
失败:返回 NULL 并设置 errno
struct dirent *readdir(DIR *dirp);
从目录流中读取一个目录项
dirp: 由 opendir 打开的目录流
返回值
成功:返回一个指向 struct dirent 的指针,该结构包含了目录项的信息
如果到达目录末尾或发生错误:返回 NULL
int closedir(DIR *dirp);
关闭一个目录流
dirp: 由 opendir 打开的目录流
返回值
0
-1,设置errno
dup和dup2函数
用于复制文件描述符的系统调用
函数原型
#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
int dup(int oldfd);
int dup2(int oldfd, int newfd);
字段含义
oldfd: 要复制的文件描述符。
返回值
返回值
newfd: 目标文件描述符
返回值
成功:返回一个新的文件描述符
失败:返回 -1 并设置 errno
区别
dup 返回的是最小可用的文件描述符
dup2 允许你指定新的文件描述符的值(newfd)
如果 newfd 已经打开,dup2 会先关闭它。如果 newfd 等于 oldfd,则 dup2 返回 newfd 而不关闭它。
场景
重定向标准输入/输出
你可以使用 dup 或 dup2 将标准输入或输出重定向到文件
文件描述符的保存和恢复
如果一个程序需要临时使用某个特定的文件描述符,它可以使用 dup 保存旧的文件描述符,然后在完成任务后使用 dup2 恢复它
多进程间的文件共享
fork 创建的子进程继承了父进程的文件描述符
通过 dup 或 dup2,你可以让多个进程共享同一个文件描述符
示例
// 重定向标准输出到文件
if (dup2(fd, STDOUT_FILENO) == -1) {
perror("dup2");
return 1;
}
// 现在,写到标准输出(printf)实际上会写入到文件
printf("This will go into the file!\n");
if (dup2(fd, STDOUT_FILENO) == -1) {
perror("dup2");
return 1;
}
// 现在,写到标准输出(printf)实际上会写入到文件
printf("This will go into the file!\n");
fcntl函数
多功能的系统调用,用于执行各种文件操作
函数原型
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
int fcntl(int fd, int cmd, ... /* arg */ );
字段含义
fd: 文件描述符
cmd: 指定要执行的操作
arg: 额外的参数,取决于 cmd
返回值
成功:取决于操作
失败:返回 -1 并设置 errno
常用的 cmd 操作
F_DUPFD: 复制文件描述符。
F_GETFD / F_SETFD: 获取或设置文件描述符标志。
F_GETFL / F_SETFL: 获取或设置文件状态标志。
F_GETLK / F_SETLK / F_SETLKW: 文件锁定。
F_GETOWN / F_SETOWN: 获取或设置异步 I/O 所有权。
注意
不是所有的文件系统或所有类型的文件都支持所有的 fcntl 操作
文件锁(F_SETLK、F_SETLKW)是进程级别的,而不是线程级别的
在一些情况下,fcntl 可以用于套接字(socket)描述符,不仅仅是普通文件
硬链接和软连接
符号链接(软连接)
简介
一种特殊类型的文件
它包含了一个指向另一个文件或目录的路径
你可以将它看作是一个文件或目录的“快捷方式”
特点
轻量级
符号链接本身只是一个包含目标路径的小文件,不占用多少磁盘空间
灵活性
符号链接可以指向任何类型的文件和目录,甚至可以跨越不同的文件系统和磁盘分区。
透明性
大多数应用程序和用户会在访问符号链接时自动“跟随”到其指向的实际文件或目录。
标识符不共享
如果你删除或移动了目标文件,符号链接将变得“失效”。
使用方式
ln -s target_file symlink
这会创建一个名为 symlink 的符号链接,指向 target_file
场景
版本管理
一个符号链接 latest 可以总是指向最新版本的文件或目录
目录结构组织
可以创建符号链接来模拟不存在的目录结构,而不需要复制整个文件或目录。
跨文件系统操作
由于符号链接可以跨文件系统,因此它们经常用于链接位于不同磁盘分区或网络位置的文件
兼容性和便利性
在软件部署中,符号链接可以用来指向特定版本的库或程序,以确保兼容性和简化配置
实现机理
在独立的Inode中存储文本字符串
硬链接
简介
UNIX 和 Linux 文件系统中的一个高级特性
允许一个文件在文件系统中拥有多个不同的名称和路径
硬链接实际上是目标文件的一个直接引用,而不仅仅是一个指向目标文件或目录的路径
特点
同一文件,多个名字
硬链接实际上是同一个文件的不同名字。它们共享相同的 inode(文件系统内部的唯一标识符)和数据块
不占用额外空间
不占用额外空间
因为硬链接是对同一文件的多个引用,所以它们不会占用额外的磁盘空间。
权限和属性共享
硬链接共享相同的权限、所有者和其他属性
仅限于同一文件系统
硬链接不能跨越不同的文件系统或磁盘分区
不支持目录
出于文件系统完整性和安全性的考虑,大多数 UNIX 和 Linux 系统不允许创建指向目录的硬链接
引用计数
每个文件都有一个与之关联的“硬链接计数”(通常在 inode 中存储)
当创建一个新的硬链接时,这个计数会增加;当删除一个硬链接时,这个计数会减少。
创建
ln target_file link_name
这将会创建一个名为 link_name 的硬链接,该链接指向 target_file
场景
数据备份和恢复
通过创建文件的硬链接,你可以在不占用额外磁盘空间的情况下,将文件“存在”于多个位置
文件组织
硬链接可以用于在不同的目录或项目中共享同一份数据
版本和配置管理
硬链接也可以用于版本控制,允许多个版本或配置共享相同的文件
历史和审计
某些系统工具和服务(如日志管理器)可能使用硬链接来维护文件版本和历史记录
实现机理
共享Inode
0 条评论
下一页
为你推荐
查看更多