File system
目录
无环图目录(Acyclic Graph Directory) 是文件系统中一种目录组织结构,允许文件或子目录被多个父目录共享,但整体结构不形成环路。它在树形目录的基础上扩展了灵活性,同时避免了循环引用带来的问题(如无限遍历)。以下是详细解析:
一、核心概念
- 定义
-
无环图目录:目录和文件通过链接形成有向无环图(DAG),即:
- 允许一个文件或子目录被多个父目录共享(通过硬链接或符号链接)。
- 从根目录出发的所有路径不形成环路。
-
与树形目录的区别
特性 | 树形目录 | 无环图目录 |
---|---|---|
子目录归属 | 唯一父目录 | 多个父目录(共享) |
环路 | 严格禁止 | 禁止 |
灵活性 | 低 | 高 |
实现复杂度 | 简单 | 较高(需管理链接与引用计数) |
- 典型应用场景
- 共享公共配置文件(如多个项目共用同一日志目录)。
- 避免重复存储相同文件(节省空间)。
二、实现机制
- 链接类型
- 硬链接(Hard Link):
- 直接指向文件的 inode,与原始文件无区别。
- 限制:不能跨文件系统,不能链接目录(避免环路)。
- 命令:
ln <source> <link>
。
-
符号链接(Symbolic Link,软链接):
- 存储目标路径的字符串,类似快捷方式。
- 允许跨文件系统、链接目录,但可能形成“悬空链接”(目标被删除后失效)。
- 命令:
ln -s <source> <link>
。
-
引用计数
- 硬链接的 inode 中维护
引用计数
,表示被多少个目录项指向。 -
当引用计数归零时,文件数据才会被真正删除。
-
环路检测
- 文件系统禁止创建会导致环路的硬链接(例如,禁止对目录创建硬链接)。
- 符号链接可指向目录,但遍历时需检测环路(如限制递归深度或记录已访问路径)。
三、结构与示例
- 无环图目录示意图
/ (根目录)
├── home
│ ├── user1 → /shared/docs # 符号链接
│ └── user2 → /shared/docs
└── shared
└── docs
├── file1.txt
└── file2.txt
-
user1
和user2
共享/shared/docs
目录,但不形成环路。 -
硬链接与符号链接对比
操作 | 硬链接 | 符号链接 |
---|---|---|
删除原始文件 | 文件仍存在(引用计数减1) | 链接失效(悬空) |
跨文件系统 | 不支持 | 支持 |
inode 是否共享 | 是 | 否(链接文件有独立 inode) |
四、优点与缺点
- 优点
- 节省存储空间:多个目录共享同一文件或子目录。
- 灵活性高:方便组织复杂项目或公共资源。
-
维护一致性:修改共享文件时,所有链接处同步更新。
-
缺点
- 管理复杂度:需维护引用计数,防止悬空链接或误删。
- 遍历复杂度:递归遍历目录时需避免重复访问共享节点。
- 安全风险:符号链接可能被恶意利用(如链接到敏感路径)。
五、实际应用与命令
- 创建共享目录(符号链接)
# 创建共享目录
mkdir /shared/docs
# 为用户目录创建符号链接
ln -s /shared/docs /home/user1/docs
ln -s /shared/docs /home/user2/docs
- 查看链接信息
-
ls -l
显示链接指向: -
stat <file>
查看 inode 和硬链接数。 -
删除链接
- 删除硬链接:
unlink <link>
或rm <link>
(仅减少引用计数)。 - 删除符号链接:直接删除链接文件,不影响目标。
六、常见问题
- 如何避免悬空链接?
- 尽量使用相对路径的符号链接(如
ln -s ../shared/docs ./docs
)。 -
删除目标前检查是否有符号链接依赖。
-
硬链接为何不能链接目录?
- 防止形成环路(目录硬链接可能导致递归遍历死循环)。
-
例外:文件系统允许根目录的硬链接(如
.
和..
目录项)。 -
如何检测无环图目录中的环路?
- 工具:
fsck
检查文件系统一致性。 - 编程实现:深度优先遍历(DFS)时记录访问路径,检测重复节点。
总结
无环图目录通过允许共享打破了树形目录的严格层级,在灵活性与安全性之间取得平衡。理解其实现机制(如硬链接、符号链接和引用计数)和潜在问题(环路、悬空链接),能够更高效地管理复杂文件组织结构。
Link
硬链接(Hard Link) 和 符号链接(Symbolic Link,软链接) 是文件系统中两种不同的文件链接方式,它们在实现机制、功能和使用场景上有显著区别。以下是详细对比与分析:
核心区别对比
特性 | 硬链接(Hard Link) | 符号链接(Symbolic Link) |
---|---|---|
本质 | 直接指向文件的 inode | 存储目标文件的路径字符串(类似快捷方式) |
inode | 与源文件共享同一 inode | 有独立的 inode |
跨文件系统 | 不支持(必须与源文件在同一文件系统) | 支持 |
链接目录 | 不允许(防止形成环路) | 允许 |
删除源文件 | 文件数据仍存在(引用计数减1) | 链接失效(成为悬空链接) |
存储内容 | 直接关联 inode | 存储目标路径(如 /path/to/file ) |
文件大小 | 与源文件相同(不额外占用空间) | 占用独立空间(存储路径字符串) |
权限与属性 | 与源文件完全一致(共享 inode 元数据) | 独立权限(如链接文件可设置不同权限) |
更新行为 | 修改硬链接或源文件,内容同步更新 | 修改符号链接本身不影响源文件 |
使用场景 | 节省空间、确保文件存在性 | 跨文件系统、动态指向、灵活管理 |
总结:
- 硬链接是文件系统的底层机制,通过共享 inode 实现高效文件共享,但限制较多。
- 符号链接是高层抽象,通过路径字符串提供灵活性,适合动态管理和跨文件系统操作。
- 理解两者区别后,可根据实际需求选择合适的链接类型。
ACL
ACL(Access Control List,访问控制列表) 是一种细粒度的权限管理机制,用于扩展传统的文件系统权限模型(如Unix的用户/组/其他
基础权限)。它允许为特定用户或组定义更精确的访问规则,适用于复杂的权限管理场景。以下是ACL的详细解析:
一、ACL的核心概念
- 传统权限模型的局限性
- Unix/Linux的基础权限模型仅支持三类角色:所有者、所属组和其他用户。
-
无法为多个用户或组设置不同权限,例如:允许用户A读写,用户B只读,组C禁止访问。
-
ACL的扩展能力
- 允许为任意数量的用户、组或其他实体单独定义权限。
- 支持继承规则(如目录的默认ACL),自动应用到新创建的子文件和子目录。
二、ACL的类型
- 访问型ACL(Access ACL)
- 定义文件或目录本身的访问规则。
-
示例:用户Alice可读写,组Developers只读。
-
默认型ACL(Default ACL)
- 仅适用于目录,定义其下新建文件/目录的默认权限。
- 示例:为目录
/data
设置默认ACL后,其子文件自动继承该ACL。
三、ACL的组成
每个ACL条目包含以下字段:
- 类型:
user
(用户)、group
(组)、mask
(权限掩码)、other
(其他用户)。- 主体:
- 用户或组的名称(如
alice
或developers
),mask
和other
无需指定主体。 - 权限:
- 读写执行权限的组合(如
rwx
、r--
)。
示例:
user:alice:rwx # 用户alice拥有读写执行权限
group:developers:r-x # 组developers拥有读和执行权限
mask::r-x # 限制最大有效权限(覆盖其他条目)
other::r-- # 其他用户只读
四、ACL的操作命令(Linux)
- 查看ACL
- 设置ACL
- 删除ACL
- 设置默认ACL(目录)
五、ACL的优先级规则
- 精确匹配优先
- 用户权限 > 用户所属组的权限 > 其他ACL规则 > 传统
other
权限。 - 权限掩码(mask)的作用
- 限制除所有者和
other
外的所有ACL条目的最大权限。 - 例如:若mask为
r--
,即使某用户被赋予rwx
,实际有效权限为r--
。
fork后的文件操作
1. 文件描述符的继承与共享
- fork() 后的初始状态:
当父进程通过fork()
创建子进程时,子进程会继承父进程的所有打开的文件描述符。此时父子进程的 文件描述符表(指向内核文件表项)是完全复制的。 - 父子进程的 fd 数值相同(如父进程的 fd 3 对应子进程的 fd 3)。
-
这些 fd 指向相同的 内核文件表项(包括文件偏移量、打开模式等状态)。
-
共享文件偏移量:
如果父子进程操作同一个 fd(如同时读写),它们的操作会共享文件偏移量。
示例:
```c // 父进程打开文件,读取部分内容 int fd = open("file.txt", O_RDWR); read(fd, buf, 100); // 文件偏移量移动到 100
// fork() 后,子进程继承 fd if (fork() == 0) { // 子进程读取会从偏移量 100 继续 read(fd, buf, 50); // 偏移量变为 150 exit(0); } else { // 父进程读取也会从 150 继续 read(fd, buf, 50); // 偏移量变为 200 } ```
2. 独立操作的场景
- 关闭 fd 的独立性:
父子进程关闭自己的 fd 不会影响对方。
示例:
c
if (fork() == 0) {
close(fd); // 子进程关闭 fd,不影响父进程
exit(0);
} else {
sleep(1);
write(fd, "data", 4); // 父进程仍可写入
}
查看文件命令
ls
命令 | 说明 |
---|---|
ls |
列出当前目录的非隐藏文件(短格式) |
ls -a |
显示所有文件(包括以 . 开头的隐藏文件) |
ls -l |
长格式显示文件详细信息(权限、大小、时间等) |
ls -al |
组合 -a 和 -l ,显示所有文件的详细信息 |
ls -lh |
以易读格式显示文件大小(如 K , M , G ) |
ls -t |
按修改时间排序(最新文件在前) |
ls -r |
反向排序(配合 -t 、-S 等使用) |
ls -R |
递归列出子目录内容 |
ls -d */ |
仅显示当前目录的子目录(不展开内容) |
stat [选项]
显示文件或文件系统的详细元数据,包括:文件大小、inode 号、权限、所有者。 三种时间戳:访问时间(Access)、修改时间(Modify)、状态变更时间(Change)。
ln
文件系统实现
磁盘结构(on-disk structure)
-
Boot control block
-
Volume control block
-
directory structure
-
per-file file control block
内存结构(in-memory file system structure)
It reflects and extends on-disk structures
-
Mount table
-
system-wide open-file table: contains a copy of the FCB of each file and other info
-
per-process open-file table
-
I/O memory buffers
具体示例
假设有64 blocks,block size = 4KB. 预留56个block给数据存储,8个block存储meta data,具体如下: - Inodes: 假设inode大小为256 bytes,有5个block用于存储inode,那么4KB的block可存储16个inode, 5个块共可存储80个inodes,也就是80个file(directory) - Bitmap: 用来管理空闲空间,分为两种,各占一个block: - inode bitmap: 管理free inode - data region bitmap: 管理free data region - Superblock:存储元信息 - how many inodes/data blocks - where the inode table begins - where the data region begins - a magic number
每个inode都用一个数字表示,如果要读取inode number 32的inode:
inode区域地址:32*sizeof(inode) = 8KB 最终地址:8K + 4K(super block) + 8K(BITMAP)=20K