1.4 文件系统
Xv6文件系统提供数据文件(包含未解释的字节数组)和目录(包含对数据文件和其他目录的命名引用)。这些目录形成一个树,从一个叫做根的特殊目录开始。像/a/b/c
这样的路径是指在根目录/
下名为a
的目录中名为b
的目录中名为c
的文件或目录。不以/
开始的路径相对于调用进程的当前工作目录进行计算,当前工作目录可以通过chdir
系统调用进行更改。下面两个代码片段打开相同的文件(假设所有相关的目录都存在)
chdir("/a");
chdir("b");
open("c", O_RDONLY);
open("/a/b/c", O_RDONLY);
上面代码将进程的当前目录更改为/a/b
;下面代码既不引用也不更改进程的当前目录
还有创建新文件和目录的系统调用:
mkdir
创建一个新目录open
中若使用O_CREATE
标志将会创建一个新的数据文件mknod
创建一个新的设备文件
这个例子说明了这三点:
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)中定义为
#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的新文件
open("a", O_CREATE | O_WRONLY);
link("a", "b");
从a读取或写入与从b读取或写入是相同的操作。每个inode由唯一的inode编号标识。在上面的代码序列之后,可以通过检查fstat
的结果来确定a和b引用相同的底层内容:两者都将返回相同的inode号(ino
),并且nlink
计数将被设置为2。
unlink
系统调用从文件系统中删除一个名称。只有当文件的链接数为零且没有文件描述符引用时,文件的inode和包含其内容的磁盘空间才会被释放,因此添加
unlink("a");
最后一行代码序列中会使inode和文件内容可以作为b访问。此外
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的)的工作目录不会改变。