39.1 文件和目录

存储虚拟化形成了两个关键的抽象:文件目录

  • 文件就是一个线性字节数组,每个字节都可以读取或写入。每个文件都有一个与之关联的inode号
  • 目录包含一个(用户可读名字、低级名字)对的列表。比如目录中有一个低级别名称为“10”的文件,他的用户可读名称为“foo”。“foo”所在的目录因此会有条目(“foo”,“10”),将用户可读名称映射到低级名称

39.3 创建文件

创建文件,可以通过open系统调用并传入O_CREAT标志完成:

  1. int fd = open("foo", O_CREAT | O_WRONLY | O_TRUNC);

在本例中,程序创建文件(O_CREAT),只能写入该文件,因为以(O_WRONLY)这种方式打开,并且如果该文件已经存在,则首先将其截断为零字节大小,删除所有现有内容(O_TRUNC)。

open()返回文件描述符,只是一个整数,是每个进程私有的

39.4 读写文件

39.5 读取和写入,但不按顺序

使用lseek系统调用,读取或写入文件中特定的偏移量:

  1. off_t lseek(int fildes, odd_t offset, int whence);

第一个参数是文件描述符;第二个参数是偏移量,将文件偏移量定位到文件中的特定位置;第三个参数,明确指定了搜索的执行方式。

whence规则:
image.png

对于每个进程打开的文件,OS都会跟踪一个当前偏移量,这将决定下一次读取或写入开始的位置。

39.6 用 fsync()立即写入

write()系统调用:在将来的某个时刻将此数据写入持久存储。文件系统会将这些数据写在内存中缓冲一段时间,然后再发送到存储设备。

当进程调用fsync(_int fd)_时,文件系统强制将所有脏数据(即尚未写入的)写入磁盘来响应。

  1. int fd = open("foo", O_CREAT | O_WRONLY | O_TRUNC);
  2. assert(fd > -1);
  3. int rc = write(fd, buffer, size);
  4. assert(rc == size);
  5. rc = fsync(fd);
  6. assert(rc == 0);

39.7 文件重命名

在unix中使用mv指令。调用了rename系统调用,这个操作是原子的。

39.8 获取文件信息

使用stat()fstat()系统调用。它们将一个路径名(或文件描述符)添加到一个文件中,并填充一个stat结构:
image.png

39.9 删除文件

使用rm指令,它调用了unlink()系统调用。

39.10 创建目录

可以使用一组系统调用创建、读取和删除目录,但是永远不能直接写入目录,因为目录的格式被视为文件系统元数据,所以只能间接更新目录

使用mkdir指令通过mkdir()系统调用创建一个目录。新创建的目录,有两个条目:一个引用自身的条目,一个引用其父目录的条目,前者是“.”,后者是“..”。

39.11 读取目录

使用opendir()readdir()closedir()三个系统调用就可以完成目录的读取。

  1. int main(void){
  2. DIR *dp = opendir("."); //DIR定义在dirent.h头文件中!
  3. assert(dp!=NULL);
  4. struct dirent *d;
  5. while((d=readdir(dp))!=NULL){
  6. printf("%d %s\n",(int)d->d_ino,d->d_name);
  7. }
  8. closedir(dp);
  9. return 0;
  10. }

Hint:DIR定义在dirent.h头文件中。

下面的声明定义在 struct dirent中:

  1. struct dirent
  2. {
  3. char d_name[256];
  4. ino_t d_ino;
  5. off_t d_off;
  6. unsigned short d_reclen;
  7. unsigned char d_type;
  8. };

39.12 删除目录

使用rmdir指令通过rmdir()系统调用来完成目录的删除。但是要求目录在被删除之前是空的(只包含自身和父目录条目)。如果删除一个非空目录,调用会失败。

39.13 硬链接

在文件系统中创建条目的一个新方法是通过link()系统调用,两个参数:一个旧路径名,一个新路径名。

当将一个新的文件名链接到一个旧的文件名时,实际上创建了另一种引用同一个文件的方法。

命令行程序ln创建了file的硬链接:

  1. prompt> echo hello > file
  2. prompt> cat file
  3. hello
  4. prompt> ln file file2
  5. prompt> cat file2
  6. hello

link只是在创建链接的目录中创建了另一个名称,并将其指向原有文件相同的inode号,该文件不以任何方式复制。其实质就是:对同一个inode 号创建新的引用。都只是指向文件底层元数据的链接。

创建一个文件时,系统做了两件事:

  • 首先构建一个结构,跟踪所有关于文件的信息。
  • 然后将人类可读的名称链接到该文件,并将该文件放入目录中。

因此为了从文件系统中删除一个文件,调用的是unlink()系统调用。

但是在上面的例子中,如果删除了file和file2中的任意一个,都依然可以通过剩下的那一个访问该文件。因为当文件系统取消链接文件时,会检查inode号中的引用计数,然后再减少引用计数。引用计数允许文件系统跟踪有多少不同的文件名已链接到这个inode。当引用计数达到0时,文件系统才会释放inode和相关数据块。

39.14 符号链接(软链接)

有时被称为软链接

硬链接有点局限:不能创建目录的硬链接(因为可能会在目录树中创建一个环);不能硬链接其他磁盘分区中的文件(因为inode是在特定文件系统中唯一的,而不是跨文件系统的)

使用命令行程序ln -s创建软链接:

  1. prompt> echo hello > file
  2. prompt> ln -s file file2
  3. prompt> cat file2
  4. hello
  • 符号链接本身是一个不同类型的文件

由于创建符号链接的方式,有可能会造成悬空引用,如果删除名为file的原始文件,会导致符号链接指向不再存在的路径名。