39.1 文件和目录
存储虚拟化形成了两个关键的抽象:文件和目录。
- 文件就是一个线性字节数组,每个字节都可以读取或写入。每个文件都有一个与之关联的inode号。
- 目录包含一个(用户可读名字、低级名字)对的列表。比如目录中有一个低级别名称为“10”的文件,他的用户可读名称为“foo”。“foo”所在的目录因此会有条目(“foo”,“10”),将用户可读名称映射到低级名称。
39.3 创建文件
创建文件,可以通过open系统调用并传入O_CREAT标志完成:
int fd = open("foo", O_CREAT | O_WRONLY | O_TRUNC);
在本例中,程序创建文件(O_CREAT),只能写入该文件,因为以(O_WRONLY)这种方式打开,并且如果该文件已经存在,则首先将其截断为零字节大小,删除所有现有内容(O_TRUNC)。
open()返回文件描述符,只是一个整数,是每个进程私有的。
39.4 读写文件
39.5 读取和写入,但不按顺序
使用lseek系统调用,读取或写入文件中特定的偏移量:
off_t lseek(int fildes, odd_t offset, int whence);
第一个参数是文件描述符;第二个参数是偏移量,将文件偏移量定位到文件中的特定位置;第三个参数,明确指定了搜索的执行方式。
whence规则:
对于每个进程打开的文件,OS都会跟踪一个当前偏移量,这将决定下一次读取或写入开始的位置。
39.6 用 fsync()立即写入
write()系统调用:在将来的某个时刻将此数据写入持久存储。文件系统会将这些数据写在内存中缓冲一段时间,然后再发送到存储设备。
当进程调用fsync(_int fd)_时,文件系统强制将所有脏数据(即尚未写入的)写入磁盘来响应。
int fd = open("foo", O_CREAT | O_WRONLY | O_TRUNC);
assert(fd > -1);
int rc = write(fd, buffer, size);
assert(rc == size);
rc = fsync(fd);
assert(rc == 0);
39.7 文件重命名
在unix中使用mv
指令。调用了rename
系统调用,这个操作是原子的。
39.8 获取文件信息
使用stat()
和fstat()
系统调用。它们将一个路径名(或文件描述符)添加到一个文件中,并填充一个stat结构:
39.9 删除文件
使用rm
指令,它调用了unlink()
系统调用。
39.10 创建目录
可以使用一组系统调用创建、读取和删除目录,但是永远不能直接写入目录,因为目录的格式被视为文件系统元数据,所以只能间接更新目录。
使用mkdir
指令通过mkdir()
系统调用创建一个目录。新创建的目录,有两个条目:一个引用自身的条目,一个引用其父目录的条目,前者是“.”,后者是“..”。
39.11 读取目录
使用opendir()
、readdir()
和closedir()
三个系统调用就可以完成目录的读取。
int main(void){
DIR *dp = opendir("."); //DIR定义在dirent.h头文件中!
assert(dp!=NULL);
struct dirent *d;
while((d=readdir(dp))!=NULL){
printf("%d %s\n",(int)d->d_ino,d->d_name);
}
closedir(dp);
return 0;
}
Hint:DIR定义在dirent.h头文件中。
下面的声明定义在 struct dirent中:
struct dirent
{
char d_name[256];
ino_t d_ino;
off_t d_off;
unsigned short d_reclen;
unsigned char d_type;
};
39.12 删除目录
使用rmdir
指令通过rmdir()
系统调用来完成目录的删除。但是要求目录在被删除之前是空的(只包含自身和父目录条目)。如果删除一个非空目录,调用会失败。
39.13 硬链接
在文件系统中创建条目的一个新方法是通过link()
系统调用,两个参数:一个旧路径名,一个新路径名。
当将一个新的文件名链接到一个旧的文件名时,实际上创建了另一种引用同一个文件的方法。
命令行程序ln
创建了file的硬链接:
prompt> echo hello > file
prompt> cat file
hello
prompt> ln file file2
prompt> cat file2
hello
link
只是在创建链接的目录中创建了另一个名称,并将其指向原有文件相同的inode号,该文件不以任何方式复制。其实质就是:对同一个inode 号创建新的引用。都只是指向文件底层元数据的链接。
创建一个文件时,系统做了两件事:
- 首先构建一个结构,跟踪所有关于文件的信息。
- 然后将人类可读的名称链接到该文件,并将该文件放入目录中。
因此为了从文件系统中删除一个文件,调用的是unlink()
系统调用。
但是在上面的例子中,如果删除了file和file2中的任意一个,都依然可以通过剩下的那一个访问该文件。因为当文件系统取消链接文件时,会检查inode号中的引用计数,然后再减少引用计数。引用计数允许文件系统跟踪有多少不同的文件名已链接到这个inode。当引用计数达到0时,文件系统才会释放inode和相关数据块。
39.14 符号链接(软链接)
有时被称为软链接。
硬链接有点局限:不能创建目录的硬链接(因为可能会在目录树中创建一个环);不能硬链接其他磁盘分区中的文件(因为inode是在特定文件系统中唯一的,而不是跨文件系统的)
使用命令行程序ln -s
创建软链接:
prompt> echo hello > file
prompt> ln -s file file2
prompt> cat file2
hello
- 符号链接本身是一个不同类型的文件
由于创建符号链接的方式,有可能会造成悬空引用,如果删除名为file的原始文件,会导致符号链接指向不再存在的路径名。