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

    1. chdir("/a");
    2. chdir("b");
    3. open("c", O_RDONLY);
    1. open("/a/b/c", O_RDONLY);

    上面代码将进程的当前目录更改为/a/b;下面代码既不引用也不更改进程的当前目录
    还有创建新文件和目录的系统调用:

    • mkdir创建一个新目录
    • open中若使用O_CREATE标志将会创建一个新的数据文件
    • mknod创建一个新的设备文件

    这个例子说明了这三点:

    1. mkdir("/dir");
    2. fd = open("/dir/file", O_CREATE | O_WRONLY);
    3. close(fd);
    4. mknod("/console", 1, 1);

    mknod创建一个引用设备的特殊文件。与设备文件相关联的是主设备号和次设备号(mknod的两个参数),它们唯一地标识了一个内核设备。当进程稍后打开设备文件时,内核将使用内核设备实现readwrite系统调用,而不是使用文件系统。
    一个文件的名字和文件本身是不同的;同一个底层文件(叫做inode,索引结点)可以有多个名字(叫做link,链接)。每个链接都由目录中的一个条目组成;该条目包含一个文件名和一个inode引用。Inode保存有关文件的元数据(用于解释或帮助理解信息的数据),包括其类型(文件/目录/设备)、长度、文件内容在磁盘上的位置以及指向文件的链接数。
    fstat系统调用从文件描述符所引用的inode中检索信息。它填充一个stat类型的结构体,struct statstat.h(kernel/stat.h)中定义为

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

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

    1. open("a", O_CREATE | O_WRONLY);
    2. link("a", "b");

    a读取或写入与从b读取或写入是相同的操作。每个inode由唯一的inode编号标识。在上面的代码序列之后,可以通过检查fstat的结果来确定a和b引用相同的底层内容:两者都将返回相同的inode号(ino),并且nlink计数将被设置为2。
    unlink系统调用从文件系统中删除一个名称。只有当文件的链接数为零且没有文件描述符引用时,文件的inode和包含其内容的磁盘空间才会被释放,因此添加

    1. unlink("a");

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

    1. fd = open("/tmp/xyz", O_CREATE | O_RDWR);
    2. unlink("/tmp/xyz");

    是创建没有名称的临时inode的惯用方法,该临时inode将在进程关闭fd或退出时被清理。
    Unix以用户级程序的形式提供了可从shell调用的文件实用程序,例如mkdirlnrm。这种设计允许任何人通过添加新的用户级程序来扩展命令行接口。事后看来,这个计划似乎是显而易见的,但是在Unix时代设计的其他系统经常将这样的命令构建到shell中(并将shell构建到内核中)
    一个例外是cd,它是内置在shell(user/sh.c:160)。cd必须更改shell本身的当前工作目录。如果cd作为常规命令运行,那么shell将分出一个子进程,子进程将运行cdcd将更改子进程的工作目录。父目录(即shell的)的工作目录不会改变。