# 1.4 文件系统

Xv6文件系统提供数据文件（包含未解释的字节数组）和目录（包含对数据文件和其他目录的命名引用）。这些目录形成一个树，从一个叫做根的特殊目录开始。像`/a/b/c`这样的路径是指在根目录`/`下名为`a`的目录中名为`b`的目录中名为`c`的文件或目录。不以`/`开始的路径相对于调用进程的当前工作目录进行计算，当前工作目录可以通过`chdir`系统调用进行更改。下面两个代码片段打开相同的文件(假设所有相关的目录都存在）

```c
chdir("/a");
chdir("b");
open("c", O_RDONLY);
open("/a/b/c", O_RDONLY);
```

上面代码将进程的当前目录更改为`/a/b`；下面代码既不引用也不更改进程的当前目录

还有创建新文件和目录的系统调用：

- `mkdir`创建一个新目录

- `open`中若使用`O_CREATE`标志将会创建一个新的数据文件

- `mknod`创建一个新的设备文件

这个例子说明了这三点:

```c
mkdir("/dir");
fd = open("/dir/file", O_CREATE | O_WRONLY);
close(fd);
mknod("/console", 1, 1);
```

`mknod`创建一个引用设备的特殊文件。与设备文件相关联的是主设备号和次设备号(`mknod`的两个参数)，它们唯一地标识了一个内核设备。当进程稍后打开设备文件时，内核将使用内核设备实现`read`和`write`系统调用，而不是使用文件系统。

一个文件的名字和文件本身是不同的;同一个底层文件（叫做inode，索引结点）可以有多个名字（叫做link，链接）。每个链接都由目录中的一个条目组成;该条目包含一个文件名和一个inode引用。Inode保存有关文件的元数据（用于解释或帮助理解信息的数据），包括其类型(文件/目录/设备)、长度、文件内容在磁盘上的位置以及指向文件的链接数。

`fstat`系统调用从文件描述符所引用的inode中检索信息。它填充一个`stat`类型的结构体，`struct stat`在***stat.h(kernel/stat.h)***中定义为

```c
#define T_DIR 1    // Directory
#define T_FILE 2   // File
#define T_DEVICE 3 // Device
struct stat {
    int dev;     // 文件系统的磁盘设备
    uint ino;    // Inode编号
    short type;  // 文件类型
    short nlink; // 指向文件的链接数
    uint64 size; // 文件字节数
};
```

`link`系统调用创建另一个文件名，该文件名指向与现有文件相同的inode。下面的代码片段创建了一个名字既为***a***又为***b***的新文件

```c
open("a", O_CREATE | O_WRONLY);
link("a", "b");
```

从***a***读取或写入与从***b***读取或写入是相同的操作。每个inode由唯一的inode编号标识。在上面的代码序列之后，可以通过检查`fstat`的结果来确定a和b引用相同的底层内容:两者都将返回相同的inode号(`ino`)，并且`nlink`计数将被设置为2。

`unlink`系统调用从文件系统中删除一个名称。只有当文件的链接数为零且没有文件描述符引用时，文件的inode和包含其内容的磁盘空间才会被释放，因此添加

```c
unlink("a");
```

最后一行代码序列中会使inode和文件内容可以作为b访问。此外

```c
fd = open("/tmp/xyz", O_CREATE | O_RDWR);
unlink("/tmp/xyz");
```

是创建没有名称的临时inode的惯用方法，该临时inode将在进程关闭fd或退出时被清理。

Unix以用户级程序的形式提供了可从shell调用的文件实用程序，例如`mkdir`、`ln`和`rm`。这种设计允许任何人通过添加新的用户级程序来扩展命令行接口。事后看来，这个计划似乎是显而易见的，但是在Unix时代设计的其他系统经常将这样的命令构建到shell中(并将shell构建到内核中)

一个例外是`cd`，它是内置在shell(***user/sh.c***:160)。`cd`必须更改shell本身的当前工作目录。如果`cd`作为常规命令运行，那么shell将分出一个子进程，子进程将运行`cd`，`cd`将更改子进程的工作目录。父目录(即shell的)的工作目录不会改变。