Linux 文件系统剖析

【转自】https://www.ibm.com/developerworks/cn/linux/l-linux-filesystem/

按照分层结构讨论 Linux 文件系统

在文件系统方面,Linux® 能够算得上操做系统中的 “瑞士军刀”。Linux 支持许多种文件系统,从日志型文件系统到集群文件系统和加密文件系统。对于使用标准的和比较奇特的文件系统以及开发文件系统来讲,Linux 是极好的平台。本文讨论 Linux 内核中的虚拟文件系统(VFS,有时候称为虚拟文件系统交换器),而后介绍将文件系统链接在一块儿的主要结构。php

基本的文件系统体系结构

Linux 文件系统体系结构是一个对复杂系统进行抽象化的有趣例子。经过使用一组通用的 API 函数,Linux 能够在许多种存储设备上支持许多种文件系统。例如,read 函数调用能够从指定的文件描述符读取必定数量的字节。read 函数不了解文件系统的类型,好比 ext3 或 NFS。它也不了解文件系统所在的存储媒体,好比 AT Attachment Packet Interface(ATAPI)磁盘、Serial-Attached SCSI(SAS)磁盘或 Serial Advanced Technology Attachment(SATA)磁盘。可是,当经过调用 read 函数读取一个文件时,数据会正常返回。本文讲解这个机制的实现方法并介绍 Linux 文件系统层的主要结构。node

 

什么是文件系统?

首先回答最多见的问题,“什么是文件系统”。文件系统是对一个存储设备上的数据和元数据进行组织的机制。因为定义如此宽泛,支持它的代码会颇有意思。正如前面提到的,有许多种文件系统和媒体。因为存在这么多类型,能够预料到 Linux 文件系统接口实现为分层的体系结构,从而将用户接口层、文件系统实现和操做存储设备的驱动程序分隔开。linux

挂装

在 Linux 中将一个文件系统与一个存储设备关联起来的过程称为挂装(mount)。使用 mount 命令将一个文件系统附着到当前文件系统层次结构中(根)。在执行挂装时,要提供文件系统类型、文件系统和一个挂装点。程序员

为了说明 Linux 文件系统层的功能(以及挂装的方法),咱们在当前文件系统的一个文件中建立一个文件系统。实现的方法是,首先用 dd 命令建立一个指定大小的文件(使用 /dev/zero 做为源进行文件复制)—— 换句话说,一个用零进行初始化的文件,见清单 1。数据库

清单 1. 建立一个通过初始化的文件
$ dd if=/dev/zero of=file.img bs=1k count=10000
10000+0 records in
10000+0 records out
$

如今有了一个 10MB 的 file.img 文件。使用 losetup 命令将一个循环设备与这个文件关联起来,让它看起来像一个块设备,而不是文件系统中的常规文件:windows

$ losetup /dev/loop0 file.img
$

这个文件如今做为一个块设备出现(由 /dev/loop0 表示)。而后用 mke2fs 在这个设备上建立一个文件系统。这个命令建立一个指定大小的新的 ext2 文件系统,见清单 2。缓存

清单 2. 用循环设备建立 ext2 文件系统
$ mke2fs -c /dev/loop0 10000
mke2fs 1.35 (28-Feb-2004)
max_blocks 1024000, rsv_groups = 1250, rsv_gdb = 39
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
2512 inodes, 10000 blocks
500 blocks (5.00%) reserved for the super user
...
$

使用 mount 命令将循环设备(/dev/loop0)所表示的 file.img 文件挂装到挂装点 /mnt/point1。注意,文件系统类型指定为 ext2。挂装以后,就能够将这个挂装点看成一个新的文件系统,好比使用 ls 命令,见清单 3。服务器

清单 3. 建立挂装点并经过循环设备挂装文件系统
$ mkdir /mnt/point1
$ mount -t ext2 /dev/loop0 /mnt/point1
$ ls /mnt/point1
lost+found
$

如清单 4 所示,还能够继续这个过程:在刚才挂装的文件系统中建立一个新文件,将它与一个循环设备关联起来,再在上面建立另外一个文件系统。网络

清单 4. 在循环文件系统中建立一个新的循环文件系统
$ dd if=/dev/zero of=/mnt/point1/file.img bs=1k count=1000
1000+0 records in
1000+0 records out
$ losetup /dev/loop1 /mnt/point1/file.img
$ mke2fs -c /dev/loop1 1000
mke2fs 1.35 (28-Feb-2004)
max_blocks 1024000, rsv_groups = 125, rsv_gdb = 3
Filesystem label=
...
$ mkdir /mnt/point2
$ mount -t ext2 /dev/loop1 /mnt/point2
$ ls /mnt/point2
lost+found
$ ls /mnt/point1
file.img lost+found
$

经过这个简单的演示很容易体会到 Linux 文件系统(和循环设备)是多么强大。能够按照相同的方法在文件上用循环设备建立加密的文件系统。能够在须要时使用循环设备临时挂装文件,这有助于保护数据。less

 

文件系统体系结构

既然已经看到了文件系统的构造方法,如今就看看 Linux 文件系统层的体系结构。本文从两个角度考察 Linux 文件系统。首先采用高层体系结构的角度。而后进行深层次讨论,介绍实现文件系统层的主要结构。

 

高层体系结构

尽管大多数文件系统代码在内核中(后面讨论的用户空间文件系统除外),可是图 1 所示的体系结构显示了用户空间和内核中与文件系统相关的主要组件之间的关系。

图 1. Linux 文件系统组件的体系结构
图 1. Linux 文件系统组件的体系结构

用户空间包含一些应用程序(例如,文件系统的使用者)和 GNU C 库(glibc),它们为文件系统调用(打开、读取、写和关闭)提供用户接口。系统调用接口的做用就像是交换器,它将系统调用从用户空间发送到内核空间中的适当端点。

VFS 是底层文件系统的主要接口。这个组件导出一组接口,而后将它们抽象到各个文件系统,各个文件系统的行为可能差别很大。有两个针对文件系统对象的缓存(inode 和 dentry)。它们缓存最近使用过的文件系统对象。

每一个文件系统实现(好比 ext二、JFS 等等)导出一组通用接口,供 VFS 使用。缓冲区缓存会缓存文件系统和相关块设备之间的请求。例如,对底层设备驱动程序的读写请求会经过缓冲区缓存来传递。这就容许在其中缓存请求,减小访问物理设备的次数,加快访问速度。以最近使用(LRU)列表的形式管理缓冲区缓存。注意,能够使用 sync 命令将缓冲区缓存中的请求发送到存储媒体(迫使全部未写的数据发送到设备驱动程序,进而发送到存储设备)

这就是 VFS 和文件系统组件的高层状况。如今,讨论实现这个子系统的主要结构。

主要结构

Linux 以一组通用对象的角度看待全部文件系统。这些对象是超级块(superblock)、inode、dentry 和文件。超级块在每一个文件系统的根上,超级块描述和维护文件系统的状态。文件系统中管理的每一个对象(文件或目录)在 Linux 中表示为一个 inode。inode 包含管理文件系统中的对象所需的全部元数据(包括能够在对象上执行的操做)。另外一组结构称为 dentry,它们用来实现名称和 inode 之间的映射,有一个目录缓存用来保存最近使用的 dentry。dentry 还维护目录和文件之间的关系,从而支持在文件系统中移动。最后,VFS 文件表示一个打开的文件(保存打开的文件的状态,好比写偏移量等等)。

虚拟文件系统层

VFS 做为文件系统接口的根层。VFS 记录当前支持的文件系统以及当前挂装的文件系统。

可使用一组注册函数在 Linux 中动态地添加或删除文件系统。内核保存当前支持的文件系统的列表,能够经过 /proc 文件系统在用户空间中查看这个列表。这个虚拟文件还显示当前与这些文件系统相关联的设备。在 Linux 中添加新文件系统的方法是调用 register_filesystem。这个函数的参数定义一个文件系统结构(file_system_type)的引用,这个结构定义文件系统的名称、一组属性和两个超级块函数。也能够注销文件系统。

在注册新的文件系统时,会把这个文件系统和它的相关信息添加到 file_systems 列表中(见图 2 和 linux/include/linux/mount.h)。这个列表定义能够支持的文件系统。在命令行上输入 cat /proc/filesystems,就能够查看这个列表。

图 2. 向内核注册的文件系统
图 2. 向内核注册的文件系统

VFS 中维护的另外一个结构是挂装的文件系统(见图 3)。这个结构提供当前挂装的文件系统(见 linux/include/linux/fs.h)。它连接下面讨论的超级块结构。

图 3. 挂装的文件系统列表
图 3. 挂装的文件系统列表

超级块

超级块结构表示一个文件系统。它包含管理文件系统所需的信息,包括文件系统名称(好比 ext2)、文件系统的大小和状态、块设备的引用和元数据信息(好比空闲列表等等)。超级块一般存储在存储媒体上,可是若是超级块不存在,也能够实时建立它。能够在 ./linux/include/linux/fs.h 中找到超级块结构(见图 4)。

图 4. 超级块结构和 inode 操做
图 4. 超级块结构和 inode 操做

超级块中的一个重要元素是超级块操做的定义。这个结构定义一组用来管理这个文件系统中的 inode 的函数。例如,能够用 alloc_inode 分配 inode,用 destroy_inode 删除 inode。能够用 read_inode 和 write_inode 读写 inode,用 sync_fs 执行文件系统同步。能够在 ./linux/include/linux/fs.h 中找到 super_operations 结构。每一个文件系统提供本身的 inode 方法,这些方法实现操做并向 VFS 层提供通用的抽象。

inode 和 dentry

inode 表示文件系统中的一个对象,它具备唯一标识符。各个文件系统提供将文件名映射为唯一 inode 标识符和 inode 引用的方法。图 5 显示 inode 结构的一部分以及两个相关结构。请特别注意 inode_operations 和 file_operations。这些结构表示能够在这个 inode 上执行的操做。inode_operations 定义直接在 inode 上执行的操做,而 file_operations 定义与文件和目录相关的方法(标准系统调用)。

图 5. inode 结构和相关联的操做
图 5. inode 结构和相关联的操做

inode 和目录缓存分别保存最近使用的 inode 和 dentry。注意,对于 inode 缓存中的每一个 inode,在目录缓存中都有一个对应的 dentry。能够在 ./linux/include/linux/fs.h 中找到 inode 和 dentry 结构。

缓冲区缓存

除了各个文件系统实现(能够在 ./linux/fs 中找到)以外,文件系统层的底部是缓冲区缓存。这个组件跟踪来自文件系统实现和物理设备(经过设备驱动程序)的读写请求。为了提升效率,Linux 对请求进行缓存,避免将全部请求发送到物理设备。缓存中缓存最近使用的缓冲区(页面),这些缓冲区能够快速提供给各个文件系统。

 

本文没有讨论 Linux 中可用的具体文件系统,可是值得在这里稍微提一下。Linux 支持许多种文件系统,包括 MINIX、MS-DOS 和 ext2 等老式文件系统。Linux 还支持 ext三、JFS 和 ReiserFS 等新的日志型文件系统。另外,Linux 支持加密文件系统(好比 CFS)和虚拟文件系统(好比 /proc)。

最后一种值得注意的文件系统是 Filesystem in Userspace(FUSE)。这种文件系统能够将文件系统请求经过 VFS 发送回用户空间。因此,若是您有兴趣建立本身的文件系统,那么经过使用 FUSE 进行开发是一种不错的方法。

 

结束语

尽管文件系统的实现并不复杂,但它是可伸缩和可扩展的体系结构的好例子。文件系统体系结构已经发展了许多年,并成功地支持了许多不一样类型的文件系统和许多目标存储设备类型。因为使用了基于插件的体系结构和多层的函数间接性,Linux 文件系统在近期的发展很值得关注。

 

=====================================================

Q:如何查看分区和目录及使用状况

–      fdisk查看硬盘分区表

–      df:查看分区使用状况

–      du: 查看文件占用空间状况

Q: 为何要分区,如何分区?

–      能够把不一样资料,分别放入不一样分区中管理,下降风险。

–      大硬盘搜索范围大,效率低

–      磁盘配合只能对分区作设定

–      /home /var /usr/local常常是单独分区,由于常常会操做,容易产生碎片

2.Mount挂载和NFS简介

挂载的概念 :当要使用某个设备时,例如要读取硬盘中的一个格式化好的分区、光盘或软件等设备时,必须先把这些设备对应到某个目录上,而这个目录就称为“挂载点(mount point)”,这样才能够读取这些设备,而这些对应的动做就是“挂载”。 将物理分区细节屏蔽掉。用户只有统一的逻辑概念。全部的东西都是文件。Mount命令能够实现挂载:

mount [-fnrsvw] [-t vfstype] [-o options] device dir

Q:全部的磁盘分区都必须被挂载上才能使用,那么咱们机器上的硬盘分区是如何被挂载的?

A:这主要是它利用了/etc/fstab文件。每次内核加载它知道从这里开始mount文件系统。每次系统启动会根据该文件定义自动挂载。若没有被自动挂载,分区将不能使用。 以下是个人/etc/fstab的定义,主要是根据装机的分区来的:

# <file system> <mount point>   <type>  <options>       <dump>  <pass>

proc            /proc           proc    defaults        0       0

#/dev/sda1被自动挂载到  /

UUID=cb1934d0-4b72-4bbf-9fad-885d2a8eeeb1 /               ext3    relatime,errors=remount-ro 0       1

# /dev/sda5 被自动挂载到分区/home

UUID=c40f813b-bb0e-463e-aa85-5092a17c9b94 /home           ext3    relatime        0       2

#/dev/sda7 被自动挂载到/work

UUID=0f918e7e-721a-41c6-af82-f92352a568af /work           ext3    relatime        0       2

#分区 /dev/sda6被自动挂载到swap

UUID=2f8bdd05-6f8e-4a6b-b166-12bb52591a1f none            swap    sw              0       0

 

Q:移动硬盘如何挂载?如何挂载一个新的分区?

移动硬盘有驱动模块会自动挂载,若是有个新硬盘,要先进行分区,并经过mount命令挂载到某个文件夹。若是要自动挂载则能够修改/etc/fstab文件.

NFS简介:NFS相信在不少地方都有普遍使用,是一个很是好的文件共享方式。咱们公司所使用的上传服务就是把文件上传到某台网络服务器上,中间就是经过NFS实现。

使用NFS客户端能够透明的地访问服务器端的文件。NFS也是经过mount来实现,底层是经过NFS通讯协议实现。基本原理:

 

 

图2:NFS基本原理

 

3.文件类型

Linux下面的文件类型主要有:

a)         普通文件:C语言元代码、SHELL脚本、二进制的可执行文件等。分为纯文本和二进制。

b)         目录文件:目录,存储文件的惟一地方。

c)         连接文件:指向同一个文件或目录的的文件。

d)         特殊文件:与系统外设相关的,一般在/dev下面。分为块设备和字符设备。

能够经过ls –l, file, stat几个命令来查看文件的类型等相关信息。

4.文件存储结构

 

Linux正统的文件系统(如ext二、ext3)一个文件由目录项、inode和数据块组成。

目录项:包括文件名和inode节点号。

Inode:又称文件索引节点,是文件基本信息的存放地和数据块指针存放地。

数据块:文件的具体内容存放地。

 

 

 

Linux正统的文件系统(如ext二、3等)将硬盘分区时会划分出目录块、inode Table区块和data block数据区域。一个文件由一个目录项、inode和数据区域块组成。Inode包含文件的属性(如读写属性、owner等,以及指向数据块的指针),数据区域块则是文件内容。当查看某个文件时,会先从inode table中查出文件属性及数据存放点,再从数据块中读取数据。

 

站在2w英尺视图,文件存储结构大概以下:

 

 

图3:文件存储结构2w英尺视图

 

 

其中目录项的结构以下(每一个文件的目录项存储在改文件所属目录的文件内容里):

 

 图4:目录项结构 

 

其中文件的inode结构以下(inode里所包含的文件信息能够经过stat filename查看获得):

 

 

图5:inode结构

 

以上只反映大致的结构,linux文件系统自己在不断发展。可是以上概念基本是不变的。且如ext二、ext三、ext4文件系统也存在很大差异,若是要了解能够查看专门的文件系统介绍。

5.软链接、硬连接

软连接和硬连接是咱们常见的两种概念:

硬链接:是给文件一个副本,同时创建二者之间的链接关系。修改其中一个,与其链接的文件同时被修改。若是删除其中[color=red]任意一个[/color]其他的文件将不受影响。

软链接:也叫符号链接,他只是对源文件在新的位置创建一个“快捷(借用一下wondows经常使用词)”,因此,当源文件删除时,符号链接的文件将成为无源之水->仅仅剩下个文件名了,固然删除这个链接,也不会影响到源文件,但对链接文件的使用、引用都是直接调用源文件的。

具体关系能够看下图:

 

 

 

图5:软连接和硬连接

 

 

从图上能够看出硬连接和软连接的区别:

1:硬连接原文件和新文件的inode编号一致。而软连接不同。

2:对原文件删除,会致使软连接不可用,而硬连接不受影响。

3:对原文件的修改,软、硬连接文件内容也同样的修改,由于都是指向同一个文件内容的。

 

6.文件目录管理命令

磁盘和文件空间

fdisk df du

文件目录与管理

cd pwd mkdir rmdir ls cp rm mv

查看文件内容

cat:
cat [file]
查看文件的内容。全程式concatenate的意思,将文件内容连续输出到屏幕上。第一行到最后一行显示。
tac:
tac [file]
和cat恰好相反 是从最后一行到第一行的方式查看。

cat有个比较很差的地方时当文件比较大时候没办法看清楚,这个时候能够用more或者Less命令。

more:
more [file]
若是使用grep或者find等命令时,能够配合使用more一页一页的查看。若是看到一半想退出,则敲入’q’便可退出。
less:
less [file]
less比more更有弹性,能够上下翻页。

若是只想读取文件的头几行或者文件的末尾几行,能够用head或tail.
head –n [file]:读取文件的前n行。
tail –n [file]:读取文件末尾n行。

以上命令都是用于查看字符文件,二进制文件出来的都是乱码,要看二进制文件的内容,能够用od命令,如查看一个MP3文件里面的内容:
od shijiemori.mp3

 

文件目录与权限

chmod chown chgrp umask

文件查找

which:
which [filename]
该命令用于查询经过PATH路径到该路径内查找可执行文件。
如:Which passwd:查找可执行文件passwd
whereis:
whereis [-bmsu] [keyword]
该命令用于把相关字的文件和目录都列出来。(Linux 会将文件都记录在一个文件数据库里面,该命令式从数据库去查询,因此速度比较快,Linux天天会更新该数据库)

locate:
locate [filename]
该命令用于把相关字的文件和目录都列出来。查找数据特别快,也是经过数据库方式来查询。可是数据库一周更新一次,因此可能有些存在数据查不到。能够去修改配置文件。

find:
find [path] [参数] [keyword]
该命令用于在指定路径下查找文件。不是经过数据来查询,因此速度会比较慢。  

7.常见目录解释

Linux各类发行版的目录结构基本一致,各个目录简单介绍以下:

 

目录

描述

/

根目录

/bin

作为基础系统所须要的最基础的命令就是放在这里。好比 ls、cp、mkdir等命令;功能和/usr/bin相似,这个目录中的文件都是可执行的,普通用户均可以使用的命令。

/boot

Linux的内核及引导系统程序所须要的文件,好比 vmlinuz initrd.img 文件都位于这个目录中。在通常状况下,GRUB或LILO系统引导管理器也位于这个目录;启动装载文件存放位置,如kernels,initrd,grub。通常是一个独立的分区。

/dev

一些必要的设备,声卡、磁盘等。还有如 /dev/null. /dev/console /dev/zero /dev/full 等。

/etc

系统的配置文件存放地. 一些服务器的配置文件也在这里;好比用户账号及密码配置文件;

/etc/opt:/opt对应的配置文件

/etc/X11:Xwindows系统配置文件

/etc/xml:XML配置文件

……

/home

用户工做目录,和我的配置文件,如我的环境变量等,全部的帐号分配一个工做目录。通常是一个独立的分区。

/lib

库文件存放地。bin和sbin须要的库文件。相似windows的DLL。

/media

可拆卸的媒介挂载点,如CD-ROMs、移动硬盘、U盘,系统默认会挂载到这里来。

/mnt

临时挂载文件系统。这个目录通常是用于存放挂载储存设备的挂载目录的,好比有cdrom 等目录。能够参看/etc/fstab的定义。

/opt

可选的应用程序包。

/proc

操做系统运行时,进程(正在运行中的程序)信息及内核信息(好比cpu、硬盘分区、内存信息等)存放在这里。/proc目录假装的文件系统proc的挂载目录,proc并非真正的文件系统,它的定义能够参见 /etc/fstab 。

/root

Root用户的工做目录

/sbin

和bin相似,是一些可执行文件,不过不是全部用户都须要的,通常是系统管理所须要使用获得的。

/tmp

系统的临时文件,通常系统重启不会被保存。

/usr

包含了系统用户工具和程序。

/usr/bin:非必须的普通用户可执行命令

/usr/include:标准头文件

 /usr/lib:/usr/bin/ 和 /usr/sbin/的库文件

 /usr/sbin:非必须的可执行文件

/usr/src:内核源码

/usr/X11R6:X Window System, Version 11, Release 6.

/srv

该目录存放一些服务启动以后须要提取的数据

 

============================================================

关于文件系统,相信你们都不陌生。身为攻城狮的咱们几乎每天都会与之打交道,可是细深剖一下,其中又有多少是咱们理解深度不够的呢。那么让咱们一块儿来看一下下面这一组Linux文件系统相关的问题吧:

一、机械磁盘随机读写时速度很是慢,操做系统是采用什么技巧来提升随机读写的性能的?

二、touch一个新的空文件占用磁盘空间吗? 占用的话占用多少?

三、新建一个空目录占用磁盘空间吗?占用多少?和新建一个文件相比,哪一个占用的更大?

四、你知道文件名是记录在磁盘的什么地方吗?

五、文件名最长多长?受什么制约?

六、文件名太长了会影响系统性能吗?为何会产生影响?

七、一个目录下最多能创建多少个文件?

八、新建一个内容大小1k的文件,实际会占用多大的磁盘空间?

九、向操做系统发起读取文件2Byte的命令,操做系统实际会读取多少呢?

十、咱们使用文件时要怎么样来能提升磁盘IO速度?

  若是你能想也不用想的就回答上来百分八十的问题,那么请关掉本篇文章吧。若是不能,并且你也像做者同样对有窥探操做系统隐私的嗜好,那么就请随我一块儿来探索文件系统的这些有趣的地方,相信理解了这些以后对咱们手中的工做会有很大的帮助。
 
1、磁盘构成及分区

一、磁盘物理结构

  仍是先从最基本的磁盘物理结构提及吧,注意本文只讨论机械磁盘,SSD不在本文讨论范围以内。咱们人类管理任何事物老是习惯先划分出必定的结构,再此规则的基础上进行管理。军队分军、师、旅、团和营。公司分事业群、部门、中心和小组。,而后。对于管理 磁盘,分磁盘面、磁头、磁道、柱面和扇区

    磁盘面:磁盘是由一叠磁盘面组成,见下左图。

    磁头(Heads):每一个磁头对应一个磁盘面,负责该磁盘面上的数据的读写。
    磁道(Track):每一个盘面会围绕圆心划分出多个同心圆圈,每一个圆圈叫作一个磁道。

    柱面(Cylinders):全部盘片上的同一位置的磁道组成的立体叫作一个柱面。

    扇区(Sector):以磁道为单位管理磁盘仍然太大,因此计算机前辈们又把每一个磁道划分出了多个扇区,见下右图
 
 
  本人爱上Linux的一个缘由就是只要你愿意下功夫,你就能把Linux的外衣一脱到底,知足你的一切欲望(请想歪的骚年去面壁)。Linux上能够经过 fdisk命令,来查看当前系统使用的磁盘的这些物理信息。
 
 
  以上是我本人的一台虚拟机的磁盘物理信息。能够看出个人磁盘有255个heads,也就是说共有255个盘面。3263个cylinders,也就是说每一个盘面上都有3263个磁道, 63sectors/track说的是每一个磁道上共有63个扇区。命令结果也给出了Sector size的值是512bytes。那咱们动笔算一下该磁盘的大小吧。

  255盘面  * 3263柱面 * 63扇区 * 每一个扇区512bytes = 26839088640byte。
结果是26.8G,和磁盘的总大小相符(至于fdisk给出的详细结果相差了约4M的大小,笔者也没有弄完全明白,有兴趣的读者能够继续研究)。

  另外查看了其它两台机器的磁盘状况,发现个有意思的事情。以下图,不管磁盘的容量大或者是小,其磁头数和每磁道扇区数都是不变的,只是磁道变多了而已。
 
 
二、分区
  分区是操做系统对磁盘进行管理的第一步,这也是咱们任何一个计算机使用者都很是熟悉的概念。例如Windows下的C、D、E、F盘。那么请思考一下,

    思考:前面的磁盘的详细物理结构已经有了,若是让你把整块磁盘分红C、D等分区,你会怎么分呢? 
    
  方案一: 255个盘面,C盘是0-100盘面, D盘是101-200个盘面,……

  方案二:3263个柱面,C盘0-1000个柱面,D盘1001-20001个柱面,……

  对于以上的两个方案,你会选择哪种呢??先说下磁盘IO时的过程。第一步,首先是磁头径向移动来寻找数据所在的磁道。这部分时间叫寻道时间。第二步,找到目标磁道后经过盘面旋转,将目标扇区移动到磁头的正下方。第三步,向目标扇区读取或者写入数据。到此为止,一次磁盘IO完成,故:

  单次磁盘IO时间 = 寻道时间 + 旋转延迟 + 存取时间。

  对于旋转延时,如今主流服务器上常用的是1W转/分钟的磁盘,每旋转一周所需的时间为60*1000/10000=6ms,故其旋转延迟为(0-6ms)。对于存取时间,通常耗时较短,为零点几ms。对于寻道时间,现代磁盘大概在3-15ms,其中寻道时间大小主要受磁头当前所在位置和目标磁道所在位置相对距离的影响。

  其实采用哪种,最主要看的是那种方式性能更快。由于同一分区下的数据常常会一块儿读取,假如采用第一种,那么这样磁头就须要在3000多个track间不停地跳来跳去,这样磁盘的寻道时间就会翻倍,磁盘性能就会降低。而对于方案二,假如对于磁盘C,只须要在磁头在1-1000个磁道间移动就能够了,大大下降了寻道时间。(实际上分区并非从0开始的,磁盘的第一个磁道对应的柱面会被用来安装引导加载程序以及磁盘分区表)。因此,方案二的分区方式能够下降磁盘IO时间中的寻道时间部分,因此全部的操做系统采用的都是方案二,没有用方案一的。

    在Linux下使用过fdisk进行分区的话能够注意到如下信息。
 
 
 
  这充分证实了操做系统是采用方案二的。

  回到开篇问题1,操做系统是采用什么技巧来下降随机读写的性能问题的呢?操做系统经过按磁道对应的柱面划分分区,来下降磁盘IO所花费的的寻道时间 ,进而提升磁盘的读写性能。

2、 目录与文件

一、引子   

  好了,磁盘基础都说完了,那咱们正式进入主题,开始咱们Linux文件系统相关的讨论吧。文件系统不就是目录和文件吗?这二位但是咱们熟悉的不能再熟悉的家伙了。可你确认它不是你的那位熟悉的陌生人么?我先来来建立个 空目录和空文件吧,查看结果以下图:
 
 

  咱们都知道第五列显示的是占用的空间大小,那么我来提个几个小小的问题吧。
(1)为何目录占用的空间是4096?


(2)为何空文件占用的空间倒是0?


(3)若是空文件真占用0byte空间,那么该文件的文件名、建立者以及权限-rw-rw-r—等文件夹相关的信息都存到哪儿去了?


二、我就不信空文件不占用空间


  为了解开这个谜底,须要借助df命令。输入df –i,

  Linux结果中红框位置处显示的是inodes的相关信息,若是你对inode的概念不熟悉,你能够暂时把它当成一个操做系统秘密管理的一个家伙,会占用空间就好了。接下来我touch一个空的文件后再次df -i。

  虽然前面操做系统告诉咱们,一个新建的空文件占用的空间是0。可是这个实验却证实操做系统“欺骗”了咱们,它消耗掉了一个inode。那么inode的节点大小是多少呢,使用dumpe2fs命令能够帮助咱们查看到这个东东的实际大小。

  在输出的结果中咱们能够找到下面这行:

  它告诉咱们每一个inode的大小是256Byte。固然这个大小每台机器都会不同,它其实是在系统格式化磁盘的时候决定的。


  好了,开篇第二个问题也有答案了。原来新建一个空的文件是会占用磁盘空间的,实际占用的是256Byte。哦,不,准确的说法应该是一个inode size,具体的值是在格式化时决定的。

  再说说新建空目录吧,前面说了新建空目录会占用4KB的磁盘空间。那么仅仅如此吗? 咱们一样在新建目录先后都使用df –i来监视系统inode的占用。

  原来目录也是会占用一个inode节点的,第三个问题也有了答案了,新建一个空目录会占用磁盘空间4KB + inode size。 哦,这个在你的系统上也不必定是4K,它实际上一个block size。一样在dumpe2fs下能够看到。


  只不过个人磁盘在格式化时采用的是4KB的大小,呵呵!

三、神秘的空目录的4KB

  前面的谜团解开了,能够做为攻城狮的我对另一个东西产生了好奇心。就是空目录占用的那4KB,这些空间是用来存什么的呢?好神秘呀。

  cd到咱们新建的目录下查看。

  咱们再新建两个空的文件,再查看下目录的空间占用状况。

  貌似,没有什么新发现。由于空文件不占用block,因此这里显示的仍然是目录占用的block,和以前大小没有变化。那么我继续使用php脚本建立100个文件名长度为32Byte的空文件。

  这时咱们发现目录占用的磁盘空间变大了,成了3个Block了。哈哈,这就解答了咱们开篇的第四个问题,文件名是存在目录占用的block中的。接下来我又还证实了每一个目录block中能保存的文件名个数是和文件名的长度有关的(好像有点废话的意思,不过亲手证实本身的猜测仍是有点小爽的)。我又另外新建了个空目录,建立了100个文件名长度为32*3个空文件,该临时目录占用的磁盘空间以下:

  你可能会问我为何文件名变成了3倍后,占用的block数目为何没有变成3倍。其实Linux文件系统关于文件的结构体中除了文件名之外,还有其它的一些字段的,文件名变长3倍不会致使结构体变大3倍的,这点能够参考Linux系统内核相关书籍。


  好了,到如今开篇问题6也有了答案了。文件名长了固然会对系统性能产生影响,由于这可能会致使更多的磁盘IO。不少程序员都喜欢将文件命名为有意义的长串,令人一看文件名就知道用途。固然我没说这样很差,可是若是你的文件数量至关大的时候,你就要考虑你的文件名是否致使你的目录block占用太多了。占用的空间却是小事,磁盘很便宜,可是你得考虑下在目录下查找文件时操做系统的感觉,操做系统可须要用你你提供的文件名进行字符串比较,并且运气很差的话须要将其名下全部block都搞一遍才行啊。(固然了,你的文件名长度不变态,并且数量没有达到十万数量级的话实际上这个开销也不会太大,可是这个开销你仍是知道的为好)

  至于开篇问题5,文件名最长多长。实际上Linux操做系统就是为了不程序员不节制地使用长文件名,强加了个限制,不得超过255byte


  另外,你们有没有经验,在目录下文件不少的时候,咱们使用ls命令时会很慢。如今你们知道缘由了吧,这时实际上操做系统在读取当前目录的全部block,若是block比较多的话,可能得须要屡次IO操做才能完成这个简单的ls命令。

  我在本身的电脑某个目录下建立了一100W个空文件,ls命令1分钟还没出结果,被我ctrl+c掉了。在本身的项目中可不要这么干,虽然操做系统能够cache住你的目录数据,使你下次调用时会块不少,但我仍是建议你单个目录下文件数目不要过万。不然你的程序在重启后首次运行时可能会出现性能不佳的状况。

  好了,回到开篇问题7,你有答案了吗?一个目录下最多能建多少个文件,这个最多实际上是受限于你目录所在分区的inode数量,你有100W个inode,你最多就能够新建100W个文件。可是,上面说了,单个目录下文件数量最好不要过万,不然会带来系统性能的问题。


四、文件的block


  再作个关于文件的实验。我新建了个空目录,并在其下新建了个文件,里面只写了一个空格数据,保存后du命令显示以下:

  这8K里有4K是目录的,也就能够算出操做系统为只包含一个空格的文件分配了4KB。其实文件的block比较简单的了,不像目录的block里会存不少文件系统的结构体,文件的block里只会保存文件的数据。上面这个实验代表,操做系统分配空间时是以block为最小单位。也就是说只要你的文件数据不为空,操做系统就至少会给你分配一个block来存储,直到你超过了4KB,操做系统再给你分配下一个block,就是这样。因此对于开篇问题8,新建一个内容大小为1k的文件,实际会占用1个block(通常为4k)和一个inode(通常为256byte)。


  其实文件系统在向磁盘发起IO请求的时候,也是以block size为单位的。哪怕你只向操做系统发起读取文件的2Byte,可是操做系统会一次性给你读取4KB回来。所以磁盘IO真的是很慢,并且咱们只要访问了这2Byte,确实颇有可能接下来继续访问这2byte后面的内容,这也就是程序局部性原理,因此操做系统索性一次性就多读取些回来了。呵呵,这就是开篇问题9的答案。
这就像咱们去逛超市,逛一次真的是很浪费时间,这可要比坑爹的磁盘IO也慢许多了。咱们总不会逛了一圈超市就买了一个苹果就回来了吧,咱们确定会多买些东西为家里之后的需求准备着,反正买一堆东西比买一个苹果也没多花多少时间,何乐为不为呢,就是这个道理。


  再说说开篇问题10,咱们攻城狮怎么样设计你的文件能提升一些IO速度呢?那就是若是你知道你的要新建的文件大概会占用多大的空间的话,好比1M。那么你新建文件时就顺便和操做系统说一下,让它帮你将文件的size预留下来。这样实际上操做系统时会尽量为你分配连续的block,这样你再读取这个文件时,磁头就省去不少寻道时间了,IO速度就显得快多了。   
 
3、写在后面的话


  前面咱们说的都是基于我本身的文件系统,情形是一个block size是4KB,一个inode size是256byte,包括我虚拟机上的inode数量才只有140多万个。这些值实际上不是固定的,你彻底能够在格式化你的硬盘的时候设置成其它的值。设置的原则就是看你的硬盘的容量,以及你的用途。


  若是你的文件都是大于4KB,甚至是几M,几G的文件,那么建议你的block仍是尽量的大一点吧,这样inode里就能少记几个地址。


  若是你的文件大部分都是1K如下的,那么确实使用4K的block会形成一点点浪费,若是你的老板对成本要求异常苛刻的话,你能够适当考虑把你的block设置得小一点。


  另外,要关注你的文件系统的inode。操做系统在查看目录和文件占用的磁盘空间信息时把inode节点的占用给隐藏起来了,其用意在于为用户提供一个白盒的环境,把数据占用的空间交给咱们来认知,而把inode信息隐藏起来为了下降咱们理解操做系统的难度。而实际上,咱们做为非普通用户的开发人员应该具有这个知情权。这个东东直接关系到你文件系统能建立文件数量。不然哪天等你发现线上机器磁盘还剩大把大把的空间,但就是inode使用光了,那时候就只有从新格式化或者迁移服务器了。这两个操做想一想都以为苦逼啊,仍是能避免就尽可能避免吧。       思考题:咱们你们有个经验就是目录下小文件太多的状况下,往其它地方拷贝的话,速度会很是的慢,咱们这时每每会把目录压缩一下再拷贝。如今你能说出这样作为何会快吗?

相关文章
相关标签/搜索