unix支持哪些原始文件系统操作_文件系统

文件系统很重要,因为它们允许您在计算机关闭,崩溃或内存损坏后保留数据。文件系统使用起来很昂贵。写入文件系统(FS)涉及写入磁带和从磁带读取。

如今我们的大多数文件都存储在磁盘上 - 尽管不是全部!而且,磁盘至少比内存慢一个数量级。

先来了解一些术语。一个文件系统,因为我们将定义更加具体地过去了,是什么,满足文件系统的API。文件系统由存储介质支持,例如硬盘驱动器,固态驱动器,RAM等。磁盘是硬盘驱动器(HDD),其包括旋转的金属盘和可以将盘片切割到的盘头。编码1或0,或asolid-state drive (SSD)可以翻转芯片或独立驱动器上的某些NAND门以存储1或0.截至2019年,SSD比标准HDD快一个数量级。这些是文件系统的典型背景。文件系统是在这个支持的基础上实现的,这意味着我们可以在商用硬盘上实现类似EXT,MinixFS,NTFS,FAT32等的东西。这个文件系统告诉操作系统如何组织1和0来存储文件信息以及目录信息,但稍后会详细介绍。为了避免迂腐,我们会说像EXT或NTFS这样的文件系统直接实现文件系统API(打开,关闭等)。通常,操作系统会添加一层抽象,并要求操作系统满足其API(想想函数linux_open,linux_close等)。这两个好处是可以为多个操作系统API实现一个文件系统,并且添加新的OS文件系统调用,不需要所有底层文件系统来更改其API。例如,在Linux的下一次迭代中,如果有新的系统调用来创建文件的备份,则操作系统可以使用内部API实现该操作,而不是要求所有文件系统驱动程序更改其代码。

最后一段背景是重要的。在本章中,我们将参考符合ISO标准的KiB或Kibibyte中的文件大小。* iB系列是两个存储功率的缩写。这意味着以下内容:

字首

字节值

KIB

1024B

MIB

1024 * 1024 B.

GIB

10243 B.

Kibibyte在工业界的换算标准

标准符号前缀表示以下内容:

字首

字节值

KB

1000B

MB

1000 * 1000 B

GB

10003 B.

为了保持一致性,我们将在本书和网络章节中执行此操作,不要混淆任何人。令人困惑的是,在现实世界中,有一种不同的惯例。该惯例是,当在操作系统中显示文件时,KB与KiB相同。当我们谈论计算机网络时,CD,其他存储KB与KiB不同并且是上面的ISO / Metric定义。这是一个历史怪癖,是由网络开发人员和内存/硬件存储开发人员之间的冲突带来的。硬存储和内存开发人员发现,如果有一个位可能采用两种状态之一,那么调用Kilo-前缀1024是很自然的,因为它大约是1000.网络开发人员必须处理位,实时信号处理和各种其他因素,所以他们采用已经接受的惯例,Kilo-意味着1000的东西(“国际”,#ref-iec)。你需要知道的是,如果你看到野外的KB,它可能是1024基于上下文。如果在这个类中有任何时间你看到KB或任何一个系列引用文件系统问题,你可以安全地推断它们指的是1024作为基本单元。虽然当您推送生产代码时,请务必询问差异!

什么是文件系统?

您可能遇到过旧的UNIX格言,“一切都是文件”。在大多数UNIX系统中,文件操作提供了一个抽象许多不同操作的接口。网络套接字,硬件设备和磁盘上的数据都由类似文件的对象表示。类文件对象必须遵循以下约定:

  1. 它必须呈现给文件系统。
  2. 它必须支持常见的文件系统操作,例如open, read,write。至少需要打开和关闭。

文件系统是文件接口的实现。在本章中,我们将探讨文件系统提供的各种回调,一些典型功能和相关的实现细节。在本课程中,我们将主要讨论用于允许用户访问磁盘上数据的文件系统,这些数据是现代计算机不可或缺的。

以下是文件系统的一些常见功能:

  1. 它们处理存储本地文件和处理允许内核和用户空间之间安全通信的特殊设备。
  2. 它们处理故障,可伸缩性,索引,加密,压缩和性能。
  3. 它们处理包含数据的文件与数据存储在磁盘,分区和受保护的确切位置之间的抽象。

在深入研究文件系统的细节之前,让我们先看一些例子。为了澄清,安装点只是将目录映射到内核中表示的文件系统。

  1. ext4 通常安装在Linux系统上/上,这是通常提供磁盘访问的文件系统。
  2. procfs 通常安装在/ proc,提供信息和控制流程。
  3. sysfs 通常安装在/ sys,一个更现代的/ proc版本,它还允许控制各种其他硬件,如网络套接字。
  4. tmpfs 在某些系统中安装在/ tmp,内存文件系统用于保存临时文件。
  5. sshfs这会在整个ssh协议中同步文件。

它告诉您基于文件系统的系统调用解析的内容。例如,在我们的情况下/由ext4文件系统解析,但即使它包含 作为子系统, /proc/2也由procfs系统解析/。

您可能已经注意到,某些文件系统提供了一个非“文件”的界面。诸如文件系统procfs通常被称为虚拟文件系统,因为它们不像传统文件系统那样提供数据访问。从技术上讲,内核中的所有文件系统都由虚拟文件系统表示,但我们将虚拟文件系统区分为实际上不在硬盘上存储任何内容的文件系统。

文件API

文件系统必须为各种操作提供回调函数。其中一些列表如下:

  • open 打开IO文件
  • read 读取文件的内容
  • write 写入文件
  • close 关闭文件并释放相关资源
  • chmod 修改文件的权限
  • ioctl 与终端等字符设备的设备参数进行交互

并非每个文件系统都支持所有可能的回调函数。例如,许多文件系统省略ioctl或link。许多文件系统并不seekable意味着它们专门提供顺序访问。程序无法移动到文件中的任意点。这类似于seekable streams。在本章中,我们不会检查每个文件系统回调。如果您想了解有关此接口的更多信息,请尝试在用户空间级别(FUSE)查看文件系统的文档。

将数据存储在磁盘上

要了解文件系统如何与磁盘上的数据进行交互,我们将使用三个关键术语。

  1. disk block 磁盘块是磁盘的一部分,用于存储文件或目录的内容。
  2. inodeinode 文件或目录。这意味着inode包含有关该文件的元数据以及指向磁盘块的指针,以便实际可以写入或读取该文件。
  3. superblock超级块包含有关inode和磁盘块的元数据。示例超级块可以存储每个磁盘块的填充程度,正在使用的索引节点等等。现代文件系统实际上可以包含多个超级块和一种超级超级块,用于跟踪哪些扇区由哪些超级块控制。这往往有助于碎片化。

这可能看起来势不可挡,但到本章结束时,我们将能够理解文件系统的每个部分。

为了推理某种形式存储的数据 - 旋转磁盘,固态驱动器,磁带 - 通常首先将存储介质视为的集合。块可以被认为是磁盘上的连续区域。虽然它的大小有时由底层硬件的某些属性决定,但它更常根据给定系统的内存页面大小来确定,因此来自磁盘的数据可以缓存在内存中以便更快地访问 - 这很重要许多文件系统的功能。

文件系统有一个特殊的块,表示为超级块,它存储有关文件系统的元数据,例如日志(记录对文件系统的更改),inode表,磁盘上第一个inode的位置等。重要的是关于a超级块是它位于磁盘上的已知位置。如果没有,您的计算机可能无法启动!考虑一个编程到主板上的简单ROM。如果您的处理器无法告诉主板开始读取和解密磁盘块以启动引导序列,那么您就不走运了。

inode是我们文件系统最重要的结构,因为它代表一个文件。在我们深入探讨之前,让我们列出拥有可用文件所需的关键信息。

  • 名称
  • 文件大小
  • 创建时间,上次修改,上次访问
  • 权限
  • 文件路径
  • 校验
  • 文件数据

文件内容

来自 http://en.wikipedia.org/wiki/Inode:

在Unix风格的文件系统中,索引节点(非正式地称为inode)是用于表示文件系统对象的数据结构,文件系统对象可以是各种事物,包括文件或目录。每个inode都存储文件系统对象数据的属性和磁盘块位置。文件系统对象属性可以包括操纵元数据(例如,改变,访问,修改时间),以及所有者和许可数据(例如,组ID,用户ID,许可)。

超级块可以存储一个inode数组,每个inode存储直接的,并且可能存储几种到磁盘块的间接指针。由于inode存储在超级块中,因此大多数文件系统对可存在的inode数量有限制。由于每个inode对应一个文件,因此这也是文件系统可以拥有的文件数量的限制。试图通过在其他位置存储inode来克服这个问题,这大大增加了文件系统的复杂性。尝试为inode表重新分配空间也是不可行的,因为在inode数组结束之后的每个字节都必须被移位,这是一个非常昂贵的操作。这并不是说根本没有任何解决方案,尽管通常不需要增加inode的数量,因为inode的数量通常足够高。

一个好的想法:忘记文件的名称。'inode'是文件。

通常将文件名视为“实际”文件。不是!相反,将inode视为文件。inode保存元信息(最后访问,所有权,大小)并指向用于保存文件内容的磁盘块。但是,inode通常不存储文件名。文件名通常只存储在目录中(见下文)。

例如,要读取文件的前几个字节,请按照指向第一个直接块的第一个直接块指针读取前几个字节。写作遵循相同的过程。如果程序想要读取整个文件,请继续读取直接块,直到您读取了几个等于文件大小的字节为止。如果文件的总大小小于直接块的数量乘以块的大小,则未使用的块指针将是未定义的。类似地,如果文件的大小不是块大小的倍数,则超过最后一个块中最后一个字节末尾的数据将是垃圾。

如果文件大于其直接块可寻址的最大空间,该怎么办?对此,我们提出一个座右铭如下。

“计算机科学中的所有问题都可以通过另一层次的间接解决。” - David Wheeler

为了解决这个问题,我们介绍一下indirect blocks。单个间接块是存储指向更多数据块的指针的块。类似地,双重间接块存储指向单个间接块的指针,并且该概念可以推广到任意级别的间接。这是一个重要的概念,因为inode存储在超级块中,或者在具有恒定空间量的众所周知的位置中的一些其他结构,间接允许inode可以跟踪的空间量的指数增加。

作为一个有效的例子,假设我们将磁盘分成4KiB块,我们想要解决最多2 32块。最大磁盘大小为 4KB * 232= 16 TB。磁盘块可以存储4KB。需要四个字节宽的指针,因为我们想要寻址32位的块。每个指针指的是4KiB磁盘块,因此您最多可以参考1024*4K B = 4 MB.对于相同的磁盘配置,双间接块存储1024个指向1024个间接表的指针。因此,一个双间接块可以寻址高达4GB数据的。类似地,三重间接块可以指代最多4TiB的数据。由于间接级别的增加,这是块之间读取的三倍慢。实际的块内读取时间不会改变。

目录实施

目录是名称到inode编号的映射。它通常是一个普通文件,但在其inode中设置了一些特殊位,并为其内容设置了特定结构。POSIX提供了一个小组的函数来读取每个条目的文件名和inode号,我们将在本章后面深入讨论。

让我们考虑一下实际文件系统中的目录。从理论上讲,它们是文件。磁盘块将包含目录条目目标。这意味着我们的磁盘块看起来像这样

| inode_num | name | | ----------- | ------ |

| 2043567 | hi.txt | | ... |

每个目录条目可以是固定大小,也可以是可变长度的C字符串。它取决于特定文件系统在较低级别实现它的方式。要在POSIX系统上查看文件名到inode编号的映射,请使用ls该-i选项

# ls -i

12983989 dirlist.c 12984068 sandwich.c

稍后您可以看到这是一个强大的抽象。可以使文件在目录中具有多个不同的名称,或者存在于多个目录中。

UNIX目录约定

在标准UNIX文件系统中,以下条目专门添加到读取目录的请求中。

  1. . 代表当前目录
  2. .. 表示父目录
  3. ~ 通常是主目录的名称

违反直觉的,...可能是文件的名称,而不是祖父母目录。只有当前目录和父目录具有涉及.(即.和..)的特殊别名。但是,... 可能是磁盘上文件或目录的名称(您可以尝试使用mkdir ...)。令人困惑的是,shell 在扩展shell命令时会将其zsh 解释...为祖父目录(如果存在)的便捷快捷方式。

关于名称相关约定的其他事实:

  1. 以'。'开头的文件 磁盘上的(句号)通常被认为是“隐藏的”,并且将被ls 没有附加标志(-a)的程序省略。这不是文件系统的功能,程序可能会选择忽略它。
  2. 某些文件也可能以NULL字节开头。这些通常是 抽象的UNIX套接字,用于防止文件系统混乱,因为它们将被任何未预料到的程序有效地隐藏。但是,它们将通过详细说明套接字信息的工具列出,因此这不是提供安全性的功能。
  3. 如果您想惹恼您的邻居,请使用终端铃声字符创建一个文件。每次列出文件时(例如,通过调用'ls'),都会听到一声铃声。

目录API

虽然通常使用open打开文件然后read或write在调用close释放资源之前与文件交互来完成与C中的文件交互,但目录具有特殊调用,例如opendir,closedir和readdir。没有功能,writedir因为通常意味着创建文件或链接。该程序将使用类似open或mkdir。

为了探索这些功能,让我们编写一个程序来搜索特定文件的目录内容。下面的代码有一个错误,试着发现它!

int exists(char *directory, char *name) {

struct dirent *dp;

DIR *dirp = opendir(directory);

while ((dp = readdir(dirp)) != NULL) {

puts(dp->d_name);

if (!strcmp(dp->d_name, name)) {

return 1; /* Found */

}

}

closedir(dirp);

return 0; /* Not Found */

}

你找到了这个bug吗?它泄漏了资源!如果找到匹配的文件名,那么'closedir'永远不会被称为早期返回的一部分。打开任何文件描述符,并且永远不会释放由opendir分配的任何内存。这意味着最终进程将耗尽资源,open或者opendir调用将失败。

解决方法是确保我们在每个可能的代码路径中释放资源。

在上面的代码中,这意味着closedir之前调用return 1。忘记释放资源是一种常见的C编程错误,因为C语言中不支持确保始终使用所有代码路径释放资源。

给定一个打开的目录,在调用之后fork(),(XOR),父或子可以使用readdir(),rewinddir()或seekdir()。如果父项和子项都使用上述内容,则行为未定义。

有两个主要问题和一个考虑因素。该readdir函数返回“。”(当前目录)和“..”(父目录)。另一种是程序需要明确地从搜索中排除子目录,否则搜索可能需要很长时间。

对于许多应用程序,在递归搜索子目录之前首先检查当前目录是合理的。这可以通过将结果存储在链表中,或重置目录结构以从头重新开始来实现。

以下代码尝试以递归方式列出目录中的所有文件。作为练习,尝试识别它引入的错误。

void dirlist(char *path) {

struct dirent *dp;

DIR *dirp = opendir(path);

while ((dp = readdir(dirp)) != NULL) {

char newpath[strlen(path) + strlen(dp->d_name) + 1];

sprintf(newpath,"%s/%s", newpath, dp->d_name);

printf("%sn", dp->d_name);

dirlist(newpath);

}

}

int main(int argc, char **argv) {

dirlist(argv[1]);

return 0;

}

你找到了所有5个错误吗?

// Check opendir result (perhaps user gave us a path that can not be opened as a directory

if (!dirp) { perror("Could not open directory"); return; }

// +2 as we need space for the / and the terminating 0

char newpath[strlen(path) + strlen(dp->d_name) + 2];

// Correct parameter

sprintf(newpath,"%s/%s", path, dp->d_name);

// Perform stat test (and verify) before recursing

if (0 == stat(newpath,&s) && S_ISDIR(s.st_mode)) dirlist(newpath)

// Resource leak: the directory file handle is not closed after the while loop

closedir(dirp);

最后一点需要注意。readdir不是线程安全的!您不应该使用该函数的可重入版本。在进程中同步文件系统很重要,因此请使用带lock的readdir。

有关 详细信息,请参阅 https://linux.die.net/man/3/readdir。

链接

链接迫使我们将文件系统建模为树而不是图形。

虽然将文件系统建模为树将意味着每个in​​ode都具有唯一的父目录,但链接允许inode将自己呈现为多个位置的文件,可能具有不同的名称,从而导致具有多个父目录的inode。有两种链接:

  1. Hard Links硬链接只是一个目录中的一个条目,它将一些名称分配给一个inode号,该号码已经具有不同的名称并映射到同一目录或不同的目录中。如果我们已经在文件系统上有文件,我们可以使用以下‘ln’命令创建指向同一inode的另一个链接:
  2. $ ln file1.txt blip.txt

但是,blip.txt 同一个文件。如果我们编辑blip,我正在编辑与'file1.txt!'相同的文件。我们可以通过显示两个文件名引用相同的inode来证明这一点。

$ ls -i file1.txt blip.txt

134235 file1.txt

134235 blip.txt

等效的C调用是 link

// Function Prototype

int link(const char *path1, const char *path2);

link("file1.txt", "blip.txt");

为简单起见,上面的示例在同一目录中创建了硬链接。可以在同一文件系统内的任何位置创建硬链接。

  1. Soft Links第二种链接称为软链接,符号链接或符号链接。符号链接是不同的,因为它是一个具有特殊位设置的文件,并存储到另一个文件的路径。很简单,没有特殊位,它只不过是一个内部有文件路径的文本文件。注意当人们通常谈论链接而没有指定硬或软时,他们指的是硬链接。

要在shell中创建符号链接,请使用ln -s。要将链接的内容作为文件读取,请使用readlink。这些都在下面说明。

$ ln -s file1.txt file2.txt

$ ls -i file1.txt blip.txt

134235 file1.txt

134236 file2.txt

134235 blip.txt

$ cat file1.txt

file1!

$ cat file2.txt

file1!

$ cat blip.txt

file1!

$ echo edited file2 >> file2.txt # >> is bash syntax for append to file

$ cat file1.txt

file1!

edited file2

$ cat file2.txt

I'm file1!

edited file2

$ cat blip.txt

file1!

edited file2

$ readlink myfile.txt

file2.txt

请注意,file2.txt并file1.txt有不同的inode编号,不像硬链接blip.txt。

有一个C库调用来创建符合链接的符号链接。

symlink(const char *target, const char *symlink);

符号链接的一些优点是

    • 可以引用尚不存在的文件
    • 与硬链接不同,可以引用目录以及常规文件
    • 可以引用当前文件系统之外的文件(和目录)

但是,符号链接有一个关键的缺点,它们比常规文件和目录慢。读取链接的内容时,必须将它们解释为目标文件的新路径,从而导致打开和读取的额外调用,因为必须打开和读取实际文件。另一个缺点是POSIX禁止硬链接目录,其中允许软链接。该ln 命令仅允许root执行此操作,并且仅在您提供-d选项时才允许 。但是,即使root也可能无法执行此操作,因为大多数文件系统都会阻止它!

文件系统的完整性假定目录结构是可从根目录访问的非循环树。如果允许目录链接,则强制执行或验证此约束会变得昂贵。打破这些假设可能会使文件完整性工具无法修复文件系统。递归搜索可能永远不会终止,目录可以有多个父级,但“..”只能引用单个父级。总而言之,一个坏主意。软链接仅被忽略,这就是我们可以使用它们来引用目录的原因。

使用rm或删除文件时unlink,将从目录中删除inode引用。但是,仍可以从其他目录引用inode。要确定是否仍需要文件内容,每个inode都会保留一个引用计数,只要创建或销毁新链接,该引用计数就会更新。此计数仅跟踪硬链接,允许符号链接引用不存在的文件,因此无关紧要。

硬链接的示例使用是在不同时间点有效地创建文件系统的多个档案。归档区域具有特定文件的副本后,未来的归档可以重新使用这些归档文件,而不是创建重复文件。这称为增量备份。Apple的“Time Machine”软件就是这样做的。

寻址

现在我们已经定义了并且已经讨论了目录,我们遇到了路径的概念。路径是一系列目录,它们在图形中为文件系统提供一个“路径”。但是,有一些细微差别。可以有一个名为的路径a/b/../c/./。由于..和.是目录中的特殊条目,因此这是实际引用的有效路径a/c。大多数文件系统函数都允许传入未压缩的路径.C库提供了realpath压缩路径或获取绝对路径的功能。要手动简化,请记住这..意味着'父文件夹',这.意味着'当前文件夹'。下面是一个示例,说明了a/b/../c/.通过cd在shell中使用来导航文件系统的简化。

  1. cd a (在一个)
  2. cd b (在a / b)
  3. cd .. (在a中,因为..代表'父文件夹')
  4. cd c (在/ c中)
  5. cd . (在a / c中,因为。表示'当前文件夹')

因此,该路径可以简化为a/c。

元数据

我们如何区分常规文件和目录?单单就此而言,文件也可能包含许多其他属性。我们区分文件类型 - 与文件扩展名不同,即png,svg,pdf - 使用inode中的字段。系统如何知道文件的类型?

此信息存储在inode中。要访问它,请使用stat调用。例如,要找出上次访问我的'notes.txt'文件的时间。

struct stat s;

stat("notes.txt", &s);

printf("Last accessed %s", ctime(&s.st_atime));

实际上有三个版本stat;

int stat(const char *path, struct stat *buf);

int fstat(int fd, struct stat *buf);

int lstat(const char *path, struct stat *buf);

例如,fstat如果文件元数据已经具有与该文件关联的文件描述符,则程序可以用于了解文件元数据。

FILE *file = fopen("notes.txt", "r");

int fd = fileno(file); /* Just for fun - extract the file descriptor from a C FILE struct */

struct stat s;

fstat(fd, & s);

printf("Last accessed %s", ctime(&s.st_atime));

lstat几乎相同stat但处理符号链接的方式不同。从stat手册页。

lstat()与stat()相同,只是如果pathname是符号链接,则它返回有关链接本身的信息,而不是它引用的文件。

stat函数使用struct stat:

struct stat {

dev_t st_dev; /* ID of device containing file */

ino_t st_ino; /* Inode number */

mode_t st_mode; /* File type and mode */

nlink_t st_nlink; /* Number of hard links */

uid_t st_uid; /* User ID of owner */

gid_t st_gid; /* Group ID of owner */

dev_t st_rdev; /* Device ID (if special file) */

off_t st_size; /* Total size, in bytes */

blksize_t st_blksize; /* Block size for filesystem I/O */

blkcnt_t st_blocks; /* Number of 512B blocks allocated */

struct timespec st_atim; /* Time of last access */

struct timespec st_mtim; /* Time of last modification */

struct timespec st_ctim; /* Time of last status change */

};

该st_mode字段可用于区分常规文件和目录。要完成此任务,请使用宏,S_ISDIR和S_ISREG。

struct stat s;

if (0 == stat(name, &s)) {

printf("%s ", name);

if (S_ISDIR( s.st_mode)) puts("is a directory");

if (S_ISREG( s.st_mode)) puts("is a regular file");

} else {

perror("stat failed - are you sure we can read this file's metadata?");

}

权限和位

权限是UNIX系统在文件系统中提供安全性的方式的关键部分。您可能已经注意到该st_mode字段struct stat包含的不仅仅是文件类型。它还包含模式、详细说明用户可以和不能对给定文件执行的操作等等。任何文件通常有三组权限。用户其他人的权限(每个用户都超出前两个类别)。对于这三个类别中的每一个,我们需要跟踪用户是否被允许读取文件,写入文件以及执行文件。由于有三个类别和三个权限,因此权限通常表示为3位八进制数。对于每个数字,最低有效字节对应于读取权限,中间一个对应于写入权限,最后一个字节对应于执行权限。它们始终显示为用户其他UGO)。以下是一些常见的例子。以下是位约定:

  1. r 意味着一组人可以阅读
  2. w 意味着一组人可以写
  3. x 意味着一组人可以执行

八进制代码

用户

其他

755

rwx

r-x

r-x

644

rw-

r–

r–

值得注意的是,这些rwx位对目录的含义略有不同。对一个允许程序在其中创建或删除新文件或目录的目录的写访问权限。您可以将此视为具有对目录条目(dirent)映射的写访问权。对目录的读访问将允许程序列出目录的内容。这是对目录条目(dirent)映射的读访问。执行将允许程序使用cd进入目录。如果没有执行位,任何创建或删除文件或目录的尝试都将失败,因为您无法访问它们。但是,您可以列出目录的内容。

有几个命令行实用程序用于与文件的模式进行交互。mknod更改文件的类型。chmod获取一个数字和一个文件并更改权限位。但是,在我们详细讨论chmod之前,我们还必须了解用户ID(uid)和组id(gid)。

用户ID /组ID

UNIX系统中的每个用户都有一个用户ID。这是一个可以识别用户的唯一编号。类似地,可以将用户添加到称为组的集合中,并且每个组也具有唯一的标识号。组在UNIX系统上具有多种用途。可以为它们分配功能 - 一种描述用户对系统的控制级别的方法。例如,您可能遇到的sudoers 组是组,一组受信任的用户,可以使用该命令sudo 暂时获得更高的权限。我们将更多地讨论sudo 本章的工作原理。每个文件在创建时都是文件的创建者。uid可以在调用的st_mode文件中找到此所有者的用户ID() 。同样,组ID(struct statstatgid)也设置好了。

每个过程都可以确定它的uid和gid用getuid和 getgid。当一个进程试图打开一个文件与特定的模式,它 uid和gid与比较uid和gid文件。如果uids匹配,则进程的打开文件的请求将与文件权限的用户字段上的位进行比较。如果gids匹配,则将进程的请求与权限的组字段进行比较。如果没有ID匹配,则将应用其他字段。

读取/更改文件权限

在我们讨论如何更改权限位之前,我们应该能够阅读它们。在C中,stat可以使用库调用族。要从命令行读取权限位,请使用ls -l。请注意,权限将以“trwxrwxrwx”格式输出。第一个字符表示文件类型的类型。第一个字符的可能值包括但不限于。

  1. ( - )常规文件
  2. (d)目录
  3. (c)字符设备文件
  4. (l)象征性联系
  5. (p)命名管道(也称为FIFO)
  6. (b)阻止装置
  7. (s)插座

或者,使用程序stat,该程序显示可以从stat库调用中检索的所有信息。

要更改权限位,需要进行系统调用int chmod(const char *path, mode_t mode);。为了简化我们的示例,我们将使用与chmod“更改模式” 相同的命令行实用程序。有两种常用的方法,使用chmod八进制值或符号字符串。

$ chmod 644 file1

$ chmod 755 file2

$ chmod 700 file3

$ chmod ugo-w file4

$ chmod o-rx file4

base-8('octal')数字描述了每个角色的权限:拥有该文件的用户,该组以及其他所有人。八进制数是给予三种权限类型的三个值的总和:read(4),write(2),execute(1)

例: chmod 755 myfile

  1. r + w + x =数字*用户有4 + 2 + 1,完全许可
  2. group具有4 + 0 + 1,读取和执行权限
  3. 所有用户都有4 + 0 + 1,读取和执行权限

'setuid'位

您可能已经注意到可能已设置具有执行权限的文件。这位是setuid位。它表示在运行时,程序会将用户的uid设置为文件所有者的uid。类似地,有setgid一点将执行者的gid设置为所有者的gid。带有setuidset 的程序的规范示例是sudo。

sudo通常是root用户拥有的程序 - 具有所有功能的用户。通过使用sudo,否则非特权用户可以访问系统的大多数部分。这对于运行可能需要提升权限的程序非常有用,例如chown用于更改文件的所有权,或者mount用于挂载或卸载文件系统(我们将在本章后面讨论这个操作)。这里有些例子:

$ sudo mount /dev/sda2 /stuff/mydisk

$ sudo adduser fred

$ ls -l /usr/bin/sudo

-r-s--x--x 1 root wheel 327920 Oct 24 09:04 /usr/bin/sudo

使用setuid位执行进程时,仍然可以确定用户的原始uid getuid。该setuid位的实际操作 是设置euid可以确定的有效用户ID()geteuid。下面的行动getuid和geteuid描述。

  • getuid 返回真实用户ID(如果以root身份登录则为零)
  • geteuid 返回有效用户ID(如果以root身份运行,则为零,例如,由于程序上设置了setuid标志)

这些函数可以允许用户通过检查geteuid或更进一步编写只能由特权用户运行的程序,并确保可以运行代码的唯一用户是root用户getuid。

'粘'位

我们今天使用它们的粘性位与最初的介绍有不同的用途。可以在可执行文件上设置粘性位,即使在程序执行结束后,程序的文本段也可以保持交换状态。这使得后续执行同一程序的速度更快。今天,不再支持此行为,并且粘滞位仅在目录上设置时具有含义,

当目录的粘滞位仅设置文件的所有者时,目录的所有者和root用户可以重命名或删除该文件。当多个用户具有对公共目录的写访问权时,这非常有用。粘滞位的常见用途是用于/tmp 可以存储许多用户文件的共享和可写目录,但是用户不应该能够访问属于其他用户的文件。

要设置粘滞位,请使用chmod +t。

aneesh$ mkdir sticky

aneesh$ chmod +t sticky

aneesh$ ls -l

drwxr-xr-x 7 aneesh aneesh 4096 Nov 1 14:19 .

drwxr-xr-x 53 aneesh aneesh 4096 Nov 1 14:19 ..

drwxr-xr-t 2 aneesh aneesh 4096 Nov 1 14:19 sticky

aneesh$ su newuser

newuser$ rm -rf sticky

rm: cannot remove 'sticky': Permission denied

newuser$ exit

aneesh$ rm -rf sticky

aneesh$ ls -l

drwxr-xr-x 7 aneesh aneesh 4096 Nov 1 14:19 .

drwxr-xr-x 53 aneesh aneesh 4096 Nov 1 14:19 ..

请注意,在上面的示例中,用户名前置于提示符,该命令su用于切换用户。

虚拟文件系统和其他文件系统

POSIX系统,例如Linux和Mac OS X(基于BSD),包括作为文件​​系统一部分安装(可用)的几个虚拟文件系统。这些虚拟文件系统内的文件可以动态生成或存储在ram中。Linux提供3个主要的虚拟文件系统。

设备

用例

/dev

物理和虚拟设备列表(例如网卡,cdrom,随机数生成器

/proc

每个进程和(按传统)系统信息集使用的资源列表

/sys

有组织的内部内核实体列表

虚拟文件系统列表

如果我们想要连续的 ‘0’流,我们就可以运行cat /dev/zero。

另一个例子是文件/dev/null,一个存储您永远不需要阅读的位的好地方。发送的字节/dev/null/永远不会被存储和丢弃。常见的用途/dev/null是丢弃标准输出。例如,

$ ls . >/dev/null

管理文件和文件系统

鉴于文件系统可以使用的大量操作,让我们探索一些可用于管理文件和文件系统的工具和技术。

一个例子是创建一个安全目录。假设您在/ tmp中创建了自己的目录,然后设置权限,以便只有您可以使用该目录(见下文)。这样安全吗?

$ mkdir /tmp/mystuff

$ chmod 700 /tmp/mystuff

创建目录和更改权限之间有一个机会窗口。这导致了一些基于竞争条件的漏洞。

另一个用户用mystuff硬链接替换第二个用户拥有的现有文件或目录,然后他们就能够读取和控制mystuff目录的内容。哦不 - 我们的秘密不再是独享的了!

然而,在该具体示例中,/tmp目录具有粘滞位设置,因此只有所有者可以删除mystuff目录,并且上述简单攻击情形是不可能的。这并不意味着创建目录然后将目录设为私有是安全的!更好的版本是从一开始就以原子方式创建具有正确权限的目录。

$ mkdir -m 700 /tmp/mystuff

获取随机数据

/dev/random是一个包含随机数生成器的文件,其中熵由环境噪声确定。随机将阻止/等待,直到从环境中收集到足够的熵。

/dev/urandom 就像是随机的,但不同之处在于它允许重复(较低的熵阈值),因此不会阻塞。

可以将这两者视为程序可以读取的字符流,而不是具有开始和结束的文件。要触及一个误解,大部分时间都应该使用/dev/urandom。唯一的具体用例/dev/random是,在启动时需要加密安全数据,系统应该阻止。否则,有以下原因。

  1. 根据经验,它们都产生看起来随机的数字。
  2. /dev/random可能会在不方便的时候阻止。如果编程服务具有高可伸缩性并依赖/dev/random,则攻击者可以可靠地耗尽熵池并导致服务阻塞。
  3. 手动页面作者构成假设攻击,攻击者耗尽熵池并猜测播种位,但该攻击尚未实施。
  4. 有些操作系统/dev/random不像MacOS那样真实。
  5. 安全专家将在本文https://www.2uo.de/myths-about-urandom上讨论计算安全与信息理论安全性 。大多数加密在计算上是安全的,这意味着 /dev/urandom也是如此。

复制文件

使用通用dd命令。例如,以下命令将1 MiB数据从文件复制/dev/urandom到文件/dev/null。数据被复制为1024块块大小为1024字节的块。

$ dd if=/dev/urandom of=/dev/null bs=1k count=1024

上例中的输入和输出文件都是虚拟的 - 它们不存在于磁盘上。这意味着传输速度不受硬件电源的影响。

dd 也常用于制作磁盘或整个文件系统的副本,以创建可以刻录到其他磁盘或将数据分发给其他用户的映像。

更新修改时间

该touch可执行文件创建一个文件,如果它是不存在的,并更新文件的最后修改时间为当前时间。例如,我们可以使用当前时间创建一个新的私有文件:

$ umask 077 # all future new files will mask out all r,w,x bits for group and other access

$ touch file123 # create a file if it non-existant, and update its modified time

$ stat file123

File: `file123'

Size: 0 Blocks: 0 IO Block: 65536 regular empty file

Device: 21h/33d Inode: 226148 Links: 1

Access: (0600/-rw-------) Uid: (395606/ angrave) Gid: (61019/ ews)

Access: 2014-11-12 13:42:06.000000000 -0600

Modify: 2014-11-12 13:42:06.001787000 -0600

Change: 2014-11-12 13:42:06.001787000 -0600

触摸的一个示例用法是强制make在修改makefile中的编译器选项后重新编译未更改的文件。请记住make是'lazy' - 它会将源文件的修改时间与相应的输出文件进行比较,以查看是否需要重新编译该文件。

$ touch myprogram.c # force my source file to be recompiled

$ make

管理文件系统

要管理计算机上的文件系统,请使用mount。使用不带任何选项的mount会生成已安装文件系统的列表(每行一个文件系统),包括联网,虚拟和本地(基于旋转磁盘/ SSD的)文件系统。这是mount的典型输出

$ mount

/dev/mapper/cs241--server_sys-root on / type ext4 (rw)

proc on /proc type proc (rw)

sysfs on /sys type sysfs (rw)

devpts on /dev/pts type devpts (rw,gid=5,mode=620)

tmpfs on /dev/shm type tmpfs (rw,rootcontext="system_u:object_r:tmpfs_t:s0")

/dev/sda1 on /boot type ext3 (rw)

/dev/mapper/cs241--server_sys-srv on /srv type ext4 (rw)

/dev/mapper/cs241--server_sys-tmp on /tmp type ext4 (rw)

/dev/mapper/cs241--server_sys-var on /var type ext4 (rw)rw,bind)

/srv/software/Mathematica-8.0 on /software/Mathematica-8.0 type none (rw,bind)

engr-ews-homes.engr.illinois.edu:/fs1-homes/angrave/linux on /home/angrave type nfs (rw,soft,intr,tcp,noacl,acregmin=30,vers=3,sec=sys,sloppy,addr=128.174.252.102)

请注意,每行包括文件系统和挂载点的文件系统类型源。要减少此输出,我们可以将其输入grep并仅查看与正则表达式匹配的行。

>mount | grep proc # only see lines that contain 'proc'

proc on /proc type proc (rw)

none on /proc/sys/fs/binfmt_misc type binfmt_misc (rw)

内存映射IO

虽然我们传统上认为从文件读取和写入是通过使用read和write调用发生的操作,但有一种替代方法,即使用将文件映射到内存中mmap。mmap也可以用于IPC,您可以mmap在IPC章节中看到更多关于启用共享内存的系统调用。在本章中,我们将简要探讨mmap作为文件系统操作。

mmap获取文件并将其内容映射到内存中。这允许用户将整个文件视为内存中的缓冲区,以便在编程时更容易语义,并避免必须明确地将文件读取为离散块。

并非所有文件系统都支持使用mmapI/O。那些确实有不同的行为。有些将简单地实现mmap为包装 read和write。其他人将利用内核的页面缓存添加额外的优化。当然,这种优化也可以用于实现read和write使用,因此通常使用mmap具有相同的性能。

mmap用于执行某些操作,例如将库和进程加载到内存中。如果许多程序只需要对同一文件的读访问权限,则可以在多个进程之间共享相同的物理内存。这用于C标准库等常用库。

将文件映射到内存的过程如下。

  1. mmap需要一个文件描述符,所以我们首先需要open该文件
  2. 我们寻求所需的大小并写入一个字节以确保文件足够长
  3. 完成后调用munmap从内存中取消映射文件。

这是一个简单的例子。

#include <stdio.h>

#include <stdlib.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <sys/mman.h>

#include <fcntl.h>

#include <unistd.h>

#include <errno.h>

#include <string.h>

int fail(char *filename, int linenumber) {

fprintf(stderr, "%s:%d %sn", filename, linenumber, strerror(errno));

exit(1);

return 0; /*Make compiler happy */

}

#define QUIT fail(__FILE__, __LINE__ )

int main() {

// We want a file big enough to hold 10 integers

int size = sizeof(int) * 10;

int fd = open("data", O_RDWR | O_CREAT | O_TRUNC, 0600); //6 = read+write for me!

lseek(fd, size, SEEK_SET);

write(fd, "A", 1);

void *addr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

printf("Mapped at %pn", addr);

if (addr == (void*) -1 ) QUIT;

int *array = addr;

array[0] = 0x12345678;

array[1] = 0xdeadc0de;

munmap(addr,size);

return 0;

}

细心的读者可能会注意到我们的整数是用最不重要的字节格式编写的,因为这是我们运行这个例子的CPU的字节序。我们还分配了一个字节太多的文件!该PROT_READ | PROT_WRITE选项指定虚拟内存保护。可以将选项PROT_EXEC(此处未使用)设置为允许CPU在内存中执行指令。

可靠的单磁盘文件系统

大多数文件系统会在物理内存中缓存大量磁盘数据。在这方面,Linux是极端的。所有未使用的内存都用作巨型磁盘缓存。磁盘缓存会对整体系统性能产生重大影响,因为磁盘I / O速度很慢。对于旋转磁盘上的随机访问请求尤其如此,其中磁盘读写延迟由将读写磁盘头移动到正确位置所需的查找时间决定。

为了提高效率,内核会缓存最近使用过的磁盘块。对于写作,我们必须在性能和可靠性之间进行权衡。磁盘写入也可以被缓存(“回写缓存”),其中修改的磁盘块存储在内存中直到被驱逐。或者,可以使用“直写高速缓存”策略,其中磁盘写入立即发送到磁盘。后者更安全,因为文件系统修改被快速存储到持久性媒体,但比回写高速缓存慢。如果缓存写入,则可以根据每个磁盘块的物理位置来延迟和有效地调度它们。请注意,这是一个简化的描述,因为固态驱动器(SSD)可以用作辅助回写缓存。

在读取或写入顺序数据时,固态磁盘(SSD)和旋转磁盘都具有改进的性能。因此,操作系统通常可以使用预读策略来分摊读取请求成本并为每个请求请求几个连续的磁盘块。通过在用户应用程序需要下一个磁盘块之前发出下一个磁盘块的I / O请求,可以减少表观磁盘I / O延迟。

如果您的数据很重要并且需要强制写入磁盘,请调用 sync以请求将文件系统的更改写入(刷新)到磁盘。但是,操作系统可能会忽略此请求。即使数据是从内核缓冲区中逐出的,磁盘固件也可能使用内部磁盘缓存,或者可能尚未完成更改物理介质。注意,您还可以请求将与特定文件描述符关联的所有更改都刷新到磁盘使用fsync(int fd)。

如果操作系统在操作过程中出现故障,大多数现代文件系统会执行称为日记功能的操作来解决此问题。文件系统在完成一个可能很昂贵的操作之前所做的是它在日志中写下它将要做什么。在崩溃或失败的情况下,可以单步执行日志并查看哪些文件已损坏并修复它们。这是一种在存在关键数据且没有明显备份的情况下抢救硬盘的方法。

尽管您的计算机不太可能,但数据中心编程意味着磁盘每隔几秒钟就会失败。使用“平均故障时间(MTTF)”测量磁盘故障。对于大型阵列,平均故障时间可能非常短。如果MTTF(单个磁盘)= 30,000小时,那么MTTF(1000个磁盘)= 30000/1000 = 30个小时或大约一天半!这也是假设磁盘之间的故障是独立的,而它们通常不是。

RAID - 廉价磁盘冗余阵列

防止数据丢失的一种方法是将数据存储两次!这是“RAID-1”磁盘阵列的主要原理。通过复制写入磁盘并写入另一个备份磁盘,只有两个数据副本。如果一个磁盘发生故障,另一个磁盘将作为唯一的副本,直到可以重新克隆。读取数据的速度更快,因为可以从任一磁盘请求数据,但写入速度可能慢两倍,因为现在需要为每个磁盘块写入发出两个写入命令。与使用单个磁盘相比,每个字节的存储成本增加了一倍。

另一种常见的RAID方案是RAID-0,这意味着文件可以在两个磁盘之间拆分,但如果任何磁盘发生故障,则文件将无法恢复。这样可以减少写入时间,因为文件的一部分可以写入硬盘,另一部分写入硬盘2。

组合这些系统也很常见。如果您有很多硬盘,请考虑使用RAID-10。这是您有两个RAID-1系统的地方,但这些系统相互连接在RAID-0中。这意味着您可以从减速速度获得大致相同的速度,但现在任何一个磁盘都可能出现故障,您可以恢复该磁盘。如果来自对方raid分区的两个磁盘发生故障,则有可能您可以恢复,尽管我们大多数时间都无法恢复。

更高级别的RAID

RAID-3使用奇偶校验码而不是镜像数据。对于写入的每个N位,我们将写一个额外的位,'奇偶校验位',确保写入的总数为偶数。奇偶校验位写入另一个磁盘。如果包含奇偶校验磁盘的任何磁盘丢失,则仍可使用其他磁盘的内容计算其内容。

RAID-3的一个缺点是,无论何时写入磁盘块,都将始终写入奇偶校验块。这意味着单独的磁盘实际上存在瓶颈。实际上,这更有可能导致故障,因为一个磁盘在100%的时间内被使用,一旦磁盘出现故障,其他磁盘就更容易出现故障。

单个磁盘故障是可恢复的,因为有足够的数据可以从其余磁盘重建阵列。当两个磁盘不可用时将发生数据丢失,因为不再有足够的数据来重建阵列。我们可以根据修复时间计算出两个磁盘故障的概率,这会影响插入新磁盘的时间和重建整个数组内容所需的时间。

MTTF = mean time to failure

MTTR = mean time to repair

N = number of original disks

p = MTTR / (MTTF-one-disk / (N-1))

使用典型数字(MTTR = 1天,MTTF = 1000天,N-1 = 9,p = 0.009)

在重建过程中有另外一个驱动器失败的可能性为1%(此时你最好还是希望你仍然可以获得原始数据的可访问备份。实际上,在修复过程中第二次失败的概率可能更高因为重建阵列是I / O密集型的(并且在正常的I / O请求活动之上)。这种更高的I / O负载也会对磁盘阵列造成压力。

RAID-5类似于RAID-3,只是将检查块(奇偶校验信息)分配给不同块的不同磁盘。检查块通过磁盘阵列“旋转”。RAID-5提供比RAID-3更好的读写性能,因为不再存在单奇偶校验磁盘的瓶颈。一个缺点是您需要更多磁盘才能进行此设置,并且需要使用更复杂的算法。

bug很常见。Google报告称每年有2-10%的磁盘发生故障。在单个仓库中乘以60,000多个磁盘。服务必须在单个磁盘,服务器机架或整个数据中心故障中存在。

简单的文件系统模型

软件开发人员需要始终实现文件系统。如果您对此感到惊讶,我们建议您查看Hadoop,GlusterFS,Qumulo等。截至2018年,文件系统是研究的热门领域,因为人们已经意识到我们设计的软件模型没有充分利用我们目前的硬件。此外,我们用于存储信息的硬件一直在变得越来越好。因此,您可能有一天会自己设计一个文件系统。在本节中,我们将介绍一个假文件系统,并“浏览”一些有关工作方式的示例。

那么,我们的假设文件系统是什么样的?我们将基于minixfs一个简单的文件系统,它恰好是Linux运行的第一个文件系统。它按顺序排列在磁盘上,第一部分是超级块。超级块存储有关整个文件系统的重要元数据。由于我们希望能够在知道磁盘上的数据之前读取此块,因此需要在一个众所周知的位置,因此磁盘的启动是一个不错的选择。在超级块之后,我们将保留正在使用哪些inode的映射。如果第n个inode -0,则设置第n位0正在使用inode根目录。类似地,我们存储一个记录使用哪些数据块的地图。最后,我们有一个inode数组,后跟磁盘的其余部分 - 隐式分区为数据块。从磁盘的硬件组件的角度来看,一个数据块可以与下一个数据块相同。将磁盘视为数据块数组只是我们所做的事情,因此我们可以通过这种方式来描述文件在磁盘上的位置。

下面,我们有一个示例,说明描述文件的inode可能看起来如何。请注意,为简单起见,我们绘制了箭头,将inode中的数据块编号映射到磁盘上的位置。这些指针不是指针到数组中的指针。

83e0541cdd47993ea9888c174ad28494.png

我们假设数据块是4 KiB。

请注意,在请求附加数据块之前,文件将完全填满其每个数据块。我们将此属性称为压缩文件。上面提供的文件很有趣,因为它使用了所有直接块,其间接块的条目之一和部分使用另一个间接块。

以下小节均将参考上述文件。

执行读取

在我们的文件系统中执行读取往往非常简单,因为我们的文件很紧凑。假设我们想要阅读整个特定文件。我们开始做的是转到inode的直接结构并找到第一个直接数据块编号。在我们的例子中,它是#7。然后我们从所有数据块的开头找到第7个数据块。然后我们读取所有这些字节。我们对所有直接节点都做同样的事情。我们怎么办?我们转到间接块并读取间接块。我们知道间接块的每4个字节要么是一个前哨节点(-1),要么是另一个节点的数量数据块。在我们的特定示例中,前四个字节计算为整数5,这意味着我们的数据从头开始在第5个数据块上继续。我们对数据块#4做同样的事情,我们之后停止,因为我们超过了inode的大小;

执行写作

执行写入分为两类,写入文件和写入目录。

写入文件

首先,我们将关注文件并假设我们正在向6写入一个字节6我们档案中的KiB。要在特定偏移量的文件上执行写入,首先必须转到数据块的文件系统将从该偏移量开始。对于这个特定的例子,我们必须转到第二个或索引的数字1 inode来执行我们的写操作。我们将再次从inode获取此数字,转到数据块的根目录,转到5五数据块并在2处执行我们的写操作 2KiB偏离此块,因为我们跳过了块7中文件的前四个kibibytes。我们执行写操作并继续我们的快乐方式。

有些问题需要考虑。

  • 程序如何跨数据块边界执行写操作?
  • 添加偏移量后,程序如何执行写入会延长文件的长度?
  • 程序如何执行写入,其中偏移量大于原始文件的长度?

写入目录

执行对目录的写入意味着需要将inode添加到目录中。如果我们假装上面的例子是一个目录。我们知道我们一次最多只能添加一个目录条目。这意味着我们必须为数据块中的一个目录条目留出足够的空间。幸运的是,我们拥有的最后一个数据块有足够的可用空间。这意味着我们需要像上面那样找到最后一个数据块的编号,转到数据结束的位置,然后编写一个目录条目。不要忘记更新目录的大小,以便下一个创建不会覆盖您的文件!

添加删除

如果inode是文件,则通过将其标记为无效(可能使其指向inode -1)并在读取中跳过它来删除父目录中的目录条目。文件系统减少了inode的硬链接数,如果计数达到零,则释放inode映射中的inode并释放所有关联的数据块,以便文件系统回收它们。在许多操作系统中,inode中的几个字段会被覆盖。

如果inode是目录,则文件系统检查它是否为空。如果没有,那么内核很可能会标记错误。

额外:现代文件系统

虽然多年来大多数文件系统的API在POSIX上保持不变,但实际的文件系统本身提供了许多重要方面。

  • 数据的完整性。文件系统使用日记和有时校验和来确保写入的数据有效。Journalling是一个简单的发明,文件系统在日志中写入操作。如果文件系统在操作完成之前崩溃,则可以在使用部分日志再次启动时恢复操作。
  • 缓存。Linux在缓存文件系统操作(如查找inode)方面做得很好。这使磁盘操作看起来几乎是即时的。如果你想看到一个慢速系统,请查看带有FAT / NTFS的Windows。磁盘操作需要由应用程序缓存,否则它将通过CPU刻录。
  • 速度。在旋转磁盘机器上,朝向金属盘末端的数据将旋转得更快(角速度离中心更远)。程序用于减少在视频编辑软件中加载大型文件(如电影)的时间。SSD没有这个问题,因为没有旋转磁盘,但是它们会将其空间的一部分分开,用作fiels的“交换空间”。
  • 并行。具有多个磁头(用于物理硬盘)或多个控制器(用于SSD)的文件系统可以通过将PCIe插槽与数据复用来利用并行性,始终尽可能地向应用程序提供一些数据。
  • 加密。可以使用一个或多个密钥加密数据。一个很好的例子是Apple的APFS文件系统。
  • 冗余。有时可以将数据复制到块以确保数据始终可用。
  • 高效的备份。我们中的许多人都有数据,由于某种原因我们无法存储在云端。当文件系统被用作备份介质或作为备份源时,它能够有效地计算已更改的内容,压缩文件以及在外部驱动器之间进行同步,这很有用。
  • 集成和可引导性。文件系统需要具有适应性才能进行位翻转。大多数读者将其操作系统安装在与用于执行不同操作的文件系统相同的位置。文件系统需要确保杂散读取或写入不会破坏引导扇区 - 这意味着您的计算机无法再次启动。
  • 碎片。就像内存分配器一样,为文件分配空间会导致内部和外部碎片。当单个文件的磁盘块彼此相邻时,会发生相同的缓存优势。文件系统需要在低,高和可能的碎片使用情况下表现良好。
  • 分散式。有时,文件系统应该是单机容错的。Hadoop和其他分布式文件系统允许您这样做。

额外:尖端文件系统

现在有一些文件系统硬件真正具有前沿性。我们想简单介绍一下的是AMD的StoreMI。我们并不是在尝试销售AMD芯片组,但StoreMI的功能集值得一提。

StoreMI是一个硬件微控制器,可分析操作系统如何访问文件以及如何移动文件/块以加快加载时间。通常的用法可以设想为具有快速但小容量的SSD和较慢的大容量HDD。为了使所有文件看起来都在SSD上,StoreMI匹配文件访问模式。如果您正在启动Windows,Windows通常会以相同的顺序访问许多文件。StoreMI注意到这一点,当微控制器注意到它正在启动时,它会在操作系统请求之前将文件从HDD驱动器移动到SSD。当操作系统需要时,它们已经在SSD上。StoreMI也可以与其他应用程序一起使用。该技术仍有许多不足之处,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值