文件系统,隐匿在 Linux 背后的机制( 三 )


Linux 文件系统调用许多系统调用都会和文件与文件系统有关 。 我们首先先看一下对单个文件的系统调用 , 然后再来看一下对整个目录和文件的系统调用 。
为了创建一个新的文件 , 会使用到 creat 方法 , 注意没有 e 。
这里说一个小插曲 , 曾经有人问 UNIX 创始人 Ken Thompson , 如果有机会重新写 UNIX, 你会怎么办 , 他回答自己要把 creat 改成 create, 哈哈哈哈 。
这个系统调用的两个参数是文件名和保护模式
fd = creat("aaa",mode);这段命令会创建一个名为 aaa 的文件 , 并根据 mode 设置文件的保护位 。 这些位决定了哪个用户可能访问文件、如何访问 。
creat 系统调用不仅仅创建了一个名为 aaa 的文件 , 还会打开这个文件 。 为了允许后续的系统调用访问这个文件 , 这个 creat 系统调用会返回一个 非负整数 ,这个就叫做 文件描述符(file descriptor) , 也就是上面的 fd 。
如果在已经存在的文件上调用了 creat 系统调用 , 那么该文件中的内容会被清除 , 从 0 开始 。 通过设置合适的参数 , open 系统调用也能够创建文件 。
下面让我们看一看主要的系统调用 , 如下表所示
文件系统,隐匿在 Linux 背后的机制文章插图
为了对一个文件进行读写的前提是先需要打开文件 , 必须使用 creat 或者 open 打开 , 参数是打开文件的方式 , 是只读、可读写还是只写 。 open 系统调用也会返回文件描述符 。 打开文件后 , 需要使用 close 系统调用进行关闭 。 close 和 open 返回的 fd 总是未被使用的最小数量 。
什么是文件描述符?文件描述符就是一个数字 , 这个数字标示了计算机操作系统中打开的文件 。 它描述了数据资源 , 以及访问资源的方式 。
当程序要求打开一个文件时 , 内核会进行如下操作

  • 授予访问权限
  • 在全局文件表(global file table)中创建一个条目(entry)
  • 向软件提供条目的位置
文件描述符由唯一的非负整数组成 , 系统上每个打开的文件至少存在一个文件描述符 。 文件描述符最初在 Unix 中使用 , 并且被包括 Linux , macOS 和 BSD 在内的现代操作系统所使用 。
当一个进程成功访问一个打开的文件时 , 内核会返回一个文件描述符 , 这个文件描述符指向全局文件表的 entry 项 。 这个文件表项包含文件的 inode 信息 , 字节位移 , 访问限制等 。 例如下图所示
文件系统,隐匿在 Linux 背后的机制文章插图
默认情况下 , 前三个文件描述符为 STDIN(标准输入)、STDOUT(标准输出)、STDERR(标准错误) 。
标准输入的文件描述符是 0, 在终端中 , 默认为用户的键盘输入
标准输出的文件描述符是 1, 在终端中 , 默认为用户的屏幕
与错误有关的默认数据流是 2 , 在终端中 , 默认为用户的屏幕 。
在简单聊了一下文件描述符后 , 我们继续回到文件系统调用的探讨 。
在文件系统调用中 , 开销最大的就是 read 和 write 了 。 read 和 write 都有三个参数
  • 文件描述符:告诉需要对哪一个打开文件进行读取和写入
  • 缓冲区地址:告诉数据需要从哪里读取和写入哪里
  • 统计:告诉需要传输多少字节
这就是所有的参数了 , 这个设计非常简单轻巧 。
虽然几乎所有程序都按顺序读取和写入文件 , 但是某些程序需要能够随机访问文件的任何部分 。 与每个文件相关联的是一个指针 , 该指针指示文件中的当前位置 。 顺序读取(或写入)时 , 它通常指向要读取(写入)的下一个字节 。 如果指针在读取 1024 个字节之前位于 4096 的位置 , 则它将在成功读取系统调用后自动移至 5120 的位置 。


推荐阅读