技术编程|低调的 Linux 文件系统家族( 四 )


授予访问权限
在 全局文件表(global file table) 中创建一个 条目(entry)
向软件提供条目的位置
文件描述符由唯一的非负整数组成 , 系统上每个打开的文件至少存在一个文件描述符 。 文件描述符最初在 Unix 中使用 , 并且被包括 Linux , macOS 和 BSD 在内的现代操作系统所使用 。
当一个进程成功访问一个打开的文件时 , 内核会返回一个文件描述符 , 这个文件描述符指向全局文件表的 entry 项 。 这个文件表项包含文件的 inode 信息 , 字节位移 , 访问限制等 。 例如下图所示

技术编程|低调的 Linux 文件系统家族
本文插图

默认情况下 , 前三个文件描述符为 STDIN(标准输入) 、 STDOUT(标准输出) 、 STDERR(标准错误)。
标准输入的文件描述符是 0, 在终端中 , 默认为用户的键盘输入
标准输出的文件描述符是 1, 在终端中 , 默认为用户的屏幕
与错误有关的默认数据流是 2 , 在终端中 , 默认为用户的屏幕 。
在简单聊了一下文件描述符后 , 我们继续回到文件系统调用的探讨 。
在文件系统调用中 , 开销最大的就是 read 和 write 了 。 read 和 write 都有三个参数
文件描述符 :告诉需要对哪一个打开文件进行读取和写入
缓冲区地址 :告诉数据需要从哪里读取和写入哪里
统计 :告诉需要传输多少字节
文件描述符 :告诉需要对哪一个打开文件进行读取和写入
缓冲区地址 :告诉数据需要从哪里读取和写入哪里
统计 :告诉需要传输多少字节
这就是所有的参数了 , 这个设计非常简单轻巧 。
虽然几乎所有程序都按顺序读取和写入文件 , 但是某些程序需要能够随机访问文件的任何部分 。 与每个文件相关联的是一个指针 , 该指针指示文件中的当前位置 。 顺序读取(或写入)时 , 它通常指向要读取(写入)的下一个字节 。 如果指针在读取 1024 个字节之前位于 4096 的位置 , 则它将在成功读取系统调用后自动移至 5120 的位置 。
Lseek 系统调用会更改指针位置的值 , 以便后续对 read 或 write 的调用可以在文件中的任何位置开始 , 甚至可以超出文件末尾 。
lseek = Lseek, 段首大写 。
?
lseek 避免叫做 seek 的原因就是 seek 已经在之前 16 位的计算机上用于搜素功能了 。
Lseek 有三个参数:第一个是文件的文件描述符 , 第二个是文件的位置;第三个告诉文件位置是相对于文件的开头 , 当前位置还是文件的结尾
lseek( intfildes, off_toffset, intwhence);
lseek 的返回值是更改文件指针后文件中的绝对位置 。 lseek 是唯一从来不会造成真正磁盘查找的系统调用 , 它只是更新当前的文件位置 , 这个文件位置就是内存中的数字 。
对于每个文件 , Linux 都会跟踪文件模式(常规 , 目录 , 特殊文件) , 大小 , 最后修改时间以及其他信息 。 程序能够通过 stat 系统调用看到这些信息 。 第一个参数就是文件名 , 第二个是指向要放置请求信息结构的指针 。 这些结构的属性如下图所示 。
存储文件的设备存储文件的设备i-node 编号文件模式(包括保护位信息)文件链接的数量文件所有者标识文件所属的组文件大小(字节)创建时间最后一个修改/访问时间
fstat 调用和 stat 相同 , 只有一点区别 , fstat 可以对打开文件进行操作 , 而 stat 只能对路径进行操作 。
pipe 文件系统调用被用来创建 shell 管道 。 它会创建一系列的 伪文件, 来缓冲和管道组件之间的数据 , 并且返回读取或者写入缓冲区的文件描述符 。 在管道中 , 像是如下操作
sort
sort 进程将会输出到文件描述符1 , 也就是标准输出 , 写入管道中 , 而 head 进程将从管道中读入 。 在这种方式中 , sort 只是从文件描述符 0 中读取并写入到文件描述符 1 (管道)中 , 甚至不知道它们已经被重定向了 。 如果没有重定向的话 , sort 会自动的从键盘读入并输出到屏幕中 。


推荐阅读