本文主要是基于百度文库的《Linux2.4.30内核文件系统学习(多图).doc》和360doc的《Linux内核虚拟文件系统》修改而来,当然还参考了其他的一些文档,在此就不一一列出了。本来在看到这些文章后,都没有勇气再写点文件系统方面的东西了,这些文章实在太精彩了。最后还是鼓足勇气决定把整理的资料增加了一点自己的理解写下来,主要目的是让各位高手看看我的理解是否正确,另外就是备忘。
1、如何描述一个文件
我们先看看一个文件在内存和磁盘上是如何描述的。每个文件至少要有一个数据结构存放该文件的信息,包括uid、gid、flag、文件长度、文件内容存放位置的数据结构等。在Linux中这个数据结构被称为inode,本来inode中也应该包括文件名称等信息,但是由于符号链接的存在,导致一个文件可能存在多个文件名称,因此把和文件名称相关的信息从inode中提出,专门放到dentry 结构中。dentry通过其成员变量d_inode 指向对应的inode数据结构。如下图所示
图1
另外,inode结构中还包括了成员i_fop,其类型是struct file_operations,其中包括的针对该文件的一些操作接口,如上图所示。
2、根据路径名寻找目标文件
在Linux中目录也被作为文件看待,只是目录是一种比较特殊的文件。其特殊之处在于文件的内容是该目录中文件和子目录的dentry的描述符,通过这些dentry的描述符可以找到文件或子目录的dentry,进而找到相应的inode。
下面我们看看如果根据绝对路径寻找一个文件/tmp/temp/abc的。
1、 首先找到根文件系统的根目录文件的 dentry 和 inode
2、由这个 inode 提供的操作接口 i_op->lookup(),找到下一层节点 ‘tmp’ 的 dentry 和 inode
3、由 ‘tmp’ 的 inode 找到 ‘temp’ 的 dentry 和 inode
4、最后由 ‘temp’ 的 inode 找到 ‘abc’ 的 dentry 和 inode
可以看到,整个寻找过程是一个递归的过程。
我们再看看如何通过相对路径寻找文件/tmp/temp/abc,假如我们目前的工作目录为/tmp/temp/dir_a 中,比如我们通过拷贝命令拷贝该文件:cp ../abc ./
如何通过相对路径寻找文件呢?我们来看看dentry这个数据结构的成员,其中有一个是d_parent,数据结构定义如下
struct dentry { 删除了无关的成员
struct dentry *d_parent; /* parent directory */
struct inode *d_inode; /* Where the name belongs to - NULL is * negative */
unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */
}
d_parent指向了本目录的父目录的dentry,这样就在通过“..”时就是通过该指针找到的父目录dentry,找到父母inode,进而找到父目录下的所有文件的信息。
3、进程中打开的文件
一个文件可以被多次打开,并且多个进程对一个文件的访问权限可能不同,因此打开方式就会不同(只读、读写、可执行)。而dentry 和 inode 只能描述一个物理的文件,无法描述“打开”这个概念。
因此有必要引入 file 结构,来描述一个“被打开的文件”。每打开一个文件,就创建一个 file 结构。
file 结构中包含以下信息:
打开这个文件的进程的 uid,pid
打开的方式
读写的方式
当前在文件中的位置
实际上,打开文件的过程正是建立file, dentry, inode 之间的关联的过程。如下图
图2
在进程中如何和打开的文件相关联呢?我们来看看进程的数据结构
struct task_struct { 只保留了相关信息
struct files_struct *files; /* open file information */
}
每个进程包括“files”成员,其类型为files_struct。如下图所示
图3
进程中所有打开的文件的指针都存在了fd_arrary[]数组中。
4、虚拟文件系统
Linux 通过虚拟文件系统 (VFS) 来支持不同的具体的文件系统,那么 VFS 到底是什么?
从程序员的角度看, VFS 就是一套代码框架(framework),它将用户与具体的文件系统隔离开来。每个要通过mount 命令挂接到Linux系统的存储设备,如磁盘、光盘等(它们各自对应具体的文件系统),每个设备对应的文件系统都要按照VFS的要求提供一套统一的接口。这样,用户就可以使用这些统一的接口在不同的文件系统中拷贝数据了。参考下图
安装一个文件系统,除了需要“被安装设备”外,还要指定一个“安装点”。“安装点”是已经存在的一个目录节点。例如把/dev/sda1 安装到 /mnt/win 下,那么 /mnt/win 就是“安装点”。
可是文件系统要先安装后使用。因此,要使用 /mnt/win 这个“安装点”,必然要求它所在文件系统已也经被安装。
也就是说,安装一个文件系统,需要另外一个文件系统已经被安装。
这是一个鸡生蛋,蛋生鸡的问题:最顶层的文件系统是如何被安装的?
答案是,最顶层文件系统的时候是被安装在“根安装点”上的,而根安装点不属于任何文件系统,它对应的 dentry 、inode 是由内核在初始化阶段凭空构造出来的。
最顶层的文件系统叫做“根文件系统”。Linux 在启动的时候,要求用户必须指定一个“根设备”,内核在初始化阶段,将“根设备”安装到“根安装点”上,从而有了根文件系统。这样,文件系统才算准备就绪。此后,用户就可以通过 mount 命令来安装新的设备。
5、mount 设备(文件系统)
我们通过mount命令向Linux系统mount了一个设备。其实该命令触发了两个过程,一个是文件系统注册过程(当然,如果文件系统已注册过的话,就不需要该步骤了),另一个才是真正意义上的mount设备的过程。
文件系统注册过程
Linux内核是可加载的,许多模块式可选的,只有真正需要使用时才加载他们。文件系统注册过程就是把对应某类型文件系统相关的模块加载到内核,并创建相关的数据结构。每个文件系统模块都有一个初始化例程,它的作用就是VFS中进行注册,即填写一个叫做file_system_type的数据结构。所有已注册的文件系统的file_system_type结构形成一个链表,我们把这个链表称为注册链表。
图5
每个设备在mount时都要搜索该注册链表,选择适合自己设备文件系统的一项,并从中取出read_super()函数获取设备的超级块(存储在具体设备上,记录存储设备各种信息的一个存储块),并解析其内容。因为每种类型文件系统的超级块的格式不同,并且各自有特定的信息,每种文件系统必须使用对应的解析函数,否则内核就因为不认识该文件系统而无法完成安装。这就是注册文件系统的意义所在。
设备真正mount过程
总体数据结构,参考下图
图6
1、 创建一个设备的 vfsmount
2、为“被安装设备”创建一个 super_block,并由具体的文件系统来设置这个 super_block。在super_block中包含了该类型设备操作的各种接口的结构成员s_op,类型为super_operations。
3、 为被安装设备的根目录节点创建 dentry
4、 为被安装设备的根目录节点创建 inode, 并由 super_block->s_op->read_inode() 来设置此 inode
5、 将 super_block 与“被安装设备“根目录节点 dentry 关联起来
6、 将 super_block中的s_root与“被安装设备”的根目录节点 dentry 关联起来
如图6所示,在linux2.4.30中有三条链表,文件系统类型结构file_system_type的链表头为file_systems,超级块结构super_block的链表头为super_blocks,挂接点结构vfsmount的链表头为vfsmntlist。
在Linux3.3.5中只有两条链表结构,文件系统类型结构file_system_type的链表头为file_systems,超级块结构super_block的链表头为super_blocks。数据结构vfsmount 的结构定义还存在,但已经没有了mnt_list成员了。
6、挂接设备中查找文件的过程
下面的流程参考了linux3.3.5中的数据结构。
例如要打开 /mnt/win/dir1/abc 这个文件,就是根据这个路径,找到目标节点 ‘abc’ 对应的 dentry ,进而得到 inode 的过程。
寻找过程大致如下:
1、 首先找到根文件系统的根目录节点 dentry 和 inode
2、 由这个 inode 提供的操作接口 i_op->lookup(),找到下一层节点 ‘mnt’ 的 dentry 和 inode
3、 由 ‘mnt’ 的 inode 找到 ‘win’ 的 dentry 和 inode
4、 由于 ‘win’ 是个“安装点”,因此需要找到“被安装设备”/dev/sda1 根目录节点的 dentry 和 inode。“win”的dentry中有d_sb(超级块成员),d_sb中有“struct dentry *s_root;”,s_root就是指向“/dev/sda1”的dentry。
5、 然后由 /dev/sda1 根目录节点的 inode 负责找到下一层节点 ‘dir1’ 的 dentry 和 inode
6、 由于 dir1 是个“安装点”,因此需要借助dir1的dentry->d_sb->s_root找到 /dev/sda2 的根目录节点 dentry 和 inode
7、 最后由这个 inode 负责找到 ‘abc’ 的 dentry 和 inode
可以看到,整个寻找过程是一个递归的过程。
完成寻找后,内存中结构如下,其中红色线条是寻找目标节点的路径
文件系统是linux的一个十分基础的知识,同时也是学习linux的必备知识。
本文将站在一个较高的视图来了解linux的文件系统,主要包括了linux磁盘分区和目录、挂载基本原理、文件存储结构、软链接硬链接、和常见目录的介绍。相信有了这些知识对于深入的学习linux会有一定的帮助。文章例子主要是基于ubuntu发行版。
如有不对之处请大家多多指出。
1.Linux磁盘分区和目录
Linux发行版本之间的差别很少,差别主要表现在系统管理的特色工具以及软件包管理方式的不同。目录结构基本上都是一样的。Windows的文件结构是多个并列的树状结构,最顶部的是不同的磁盘(分区),如:C,D,E,F等。
Linux的文件结构是单个的树状结构.可以用tree进行展示。 在Ubuntu下安装tree(sudo apt-get install tree),并可通过命令来查看。
每次安装系统的时候我们都会进行分区,Linux下磁盘分区和目录的关系如下:
– 任何一个分区都必须挂载到某个目录上。
– 目录是逻辑上的区分。分区是物理上的区分。
– 磁盘Linux分区都必须挂载到目录树中的某个具体的目录上才能进行读写操作。
– 根目录是所有Linux的文件和目录所在的地方,需要挂载上一个磁盘分区。
以下是我们可能存在的一种目录和分区关系:
图1:目录和分区关系
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基本原理
Ubuntu下面Ubuntu下的例子
服务端:
$apt-get install nfs-kernel-server
vi /etc/exports 添加nfs目录: /personal/nfs_share
10.1.60.34(rw,sync,no_root_squash)
$sudo exportfs -r
$sudo /etc/init.d/portmap start
$sudo /etc/init.d/nfs-kernel-server start
客户端:
$sudo apt-get install nfs-common
$sudo mount 10.19.34.76:/personal/nfs_share ~/nfsshare例子:
3.文件类型
Linux下面的文件类型主要有:
a) 普通文件:C语言元代码、SHELL脚本、二进制的可执行文件等。分为纯文本和二进制。
b) 目录文件:目录,存储文件的唯一地方。
c) 链接文件:指向同一个文件或目录的的文件。
d) 特殊文件:与系统外设相关的,通常在/dev下面。分为块设备和字符设备。
可以通过ls –l, file, stat几个命令来查看文件的类型等相关信息。
4.文件存储结构
Linux正统的文件系统(如ext2、ext3)一个文件由目录项、inode和数据块组成。
目录项:包括文件名和inode节点号。
Inode:又称文件索引节点,是文件基本信息的存放地和数据块指针存放地。
数据块:文件的具体内容存放地。
Linux正统的文件系统(如ext2、3等)将硬盘分区时会划分出目录块、inode Table区块和data block数据区域。一个文件由一个目录项、inode和数据区域块组成。Inode包含文件的属性(如读写属性、owner等,以及指向数据块的指针),数据区域块则是文件内容。当查看某个文件时,会先从inode table中查出文件属性及数据存放点,再从数据块中读取数据。
站在2w英尺视图,文件存储结构大概如下:
图3:文件存储结构2w英尺视图
其中目录项的结构如下(每个文件的目录项存储在改文件所属目录的文件内容里):
图4:目录项结构
其中文件的inode结构如下(inode里所包含的文件信息可以通过stat filename查看得到):
图5:inode结构
以上只反映大体的结构,linux文件系统本身在不断发展。但是以上概念基本是不变的。且如ext2、ext3、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文件结构
文件结构是文件存放在磁盘等存贮设备上的组织方法。主要体现在对文件和目录的组织上。
目录提供了管理文件的一个方便而有效的途径。
Linux使用标准的目录结构,在安装的时候,安装程序就已经为用户创建了文件系统和完整而固定的目录组成形式,并指定了每个目录的作用和其中的文件类型。
/根目录
┃
┏━━┳━━━┳━━━┳━━━╋━━━┳━━━┳━━━┳━━━┓
┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃
bin home dev etc lib sbin tmp usr var
┃ ┃
┏━┻━┓ ┏━━┳━━┳━━┳━┻━┳━━┓
┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃
rc.d cron.d X11R6 src lib local man bin
┃
┏━━━┳━━┳━┻━┳━━━┓
┃ ┃ ┃ ┃ ┃
init.d rc0.d rc1.d rc2.d …… linux bin lib src
Linux采用的是树型结构。最上层是根目录,其他的所有目录都是从根目录出发而生成的。微软的DOS和windows也是采用树型结构,但是在DOS和 windows中这样的树型结构的根是磁盘分区的盘符,有几个分区就有几个树型结构,他们之间的关系是并列的。但是在linux中,无论操作系统管理几个磁盘分区,这样的目录树只有一个。从结构上讲,各个磁盘分区上的树型目录不一定是并列的。
如果这样讲不好理解的话,我来举个例子:
有一块硬盘,分成了4个分区,分别是/;/boot;/usr和windows下的fat
对于/和/boot或者/和/usr,它们是从属关系;对于/boot和/usr,它们是并列关系。
如果我把windows下的fat分区挂载到/mnt/winc下,(挂载??哦,别急,呵呵,一会就讲,一会就讲。)那么对于/mnt/winc和/usr或/mnt/winc和/boot来说,它们是从属于目录树上没有任何关系的两个分支。
因为linux是一个多用户系统,制定一个固定的目录规划有助于对系统文件和不同的用户文件进行统一管理。但就是这一点让很多从windows转到linux的初学者感到头疼。下面列出了linux下一些主要目录的功用。
/bin 二进制可执行命令
/dev 设备特殊文件
/etc 系统管理和配置文件
/etc/rc.d 启动的配置文件和脚本
/home 用户主目录的基点,比如用户user的主目录就是/home/user,可以用~user表示
/lib 标准程序设计库,又叫动态链接共享库,作用类似windows里的.dll文件
/sbin 系统管理命令,这里存放的是系统管理员使用的管理程序
/tmp 公用的临时文件存储点
/root 系统管理员的主目录(呵呵,特权阶级)
/mnt 系统提供这个目录是让用户临时挂载其他的文件系统。
/lost+found 这个目录平时是空的,系统非正常关机而留下“无家可归”的文件(windows下叫什么.chk)就在这里
/proc 虚拟的目录,是系统内存的映射。可直接访问这个目录来获取系统信 息。
/var 某些大文件的溢出区,比方说各种服务的日志文件
/usr 最庞大的目录,要用到的应用程序和文件几乎都在这个目录。其中包 含:
/usr/X11R6 存放X window的目录
/usr/bin 众多的应用程序
/usr/sbin 超级用户的一些管理程序
/usr/doc linux文档
/usr/include linux下开发和编译应用程序所需要的头文件
/usr/lib 常用的动态链接库和软件包的配置文件
/usr/man 帮助文档
/usr/src 源代码,linux内核的源代码就放在/usr/src/linux里
/usr/local/bin 本地增加的命令
/usr/local/lib 本地增加的库
二 、linux文件系统
文件系统指文件存在的物理空间,linux系统中每个分区都是一个文件系统,都有自己的目录层次结构。linux会将这些分属不同分区的、单独的文件系统按一定的方式形成一个系统的总的目录层次结构。一个操作系统的运行离不开对文件的操作,因此必然要拥有并维护自己的文件系统。
Llinux文件系统使用索引节点来记录文件信息,作用像windows的文件分配表。
索引节点是一个结构,它包含了一个文件的长度、创建及修改时间、权限、所属关系、磁盘中的位置等信息。一个文件系统维护了一个索引节点的数组,每个文件或目录都与索引节点数组中的唯一一个元素对应。系统给每个索引节点分配了一个号码,也就是该节点在数组中的索引号,称为索引节点号。
linux文件系统将文件索引节点号和文件名同时保存在目录中。所以,目录只是将文件的名称和它的索引节点号结合在一起的一张表,目录中每一对文件名称和索引节点号称为一个连接。
对于一个文件来说有唯一的索引节点号与之对应,对于一个索引节点号,却可以有多个文件名与之对应。因此,在磁盘上的同一个文件可以通过不同的路径去访问它。
可以用ln命令对一个已经存在的文件再建立一个新的连接,而不复制文件的内容。连接有软连接和硬连接之分,软连接又叫符号连接。它们各自的特点是:
硬连接:原文件名和连接文件名都指向相同的物理地址。
目录不能有硬连接;硬连接不能跨越文件系统(不能跨越不同的分区)
文件在磁盘中只有一个拷贝,节省硬盘空间;
由于删除文件要在同一个索引节点属于唯一的连接时才能成功,因此可以防止不必要的误删除。
符号连接:用ln -s命令建立文件的符号连接符号连接是linux特殊文件的一种,作为一个文件,它的数据是它所连接的文件的路径名。类似windows下的快捷方式。
可以删除原有的文件而保存连接文件,没有防止误删除功能。
这一段的的内容过于抽象,又是节点又是数组的,我已经尽量通俗再通俗了,又不好加例子作演示。大家如果还是云里雾里的话,我也没有什么办法了,只有先记住,日后在实际应用中慢慢体会、理解了。这也是我学习的一个方法吧。
三 、挂载文件系统
由上一节知道,linux系统中每个分区都是一个文件系统,都有自己的目录层次结构。linux会将这些分属不同分区的、单独的文件系统按一定的方式形成一个系统的总的目录层次结构。这里所说的“按一定方式”就是指的挂载。
将一个文件系统的顶层目录挂到另一个文件系统的子目录上,使它们成为一个整体,称为挂载。把该子目录称为挂载点。
举个例子吧:
根分区:
/根目录
┃
┏━━━━┳━━━━━┳━━━━━┳━━━━━╋━━━━━┳━━━━━┳━━━━━┳━━━━━┓
┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃
bin home dev etc lib sbin tmp usr var
┃
┏━┻━┓
┃ ┃
rc.d cron.d
┃
┏━━━┳━━━┳━┻━┳━━━━┓
┃ ┃ ┃ ┃ ┃
init.d rc0.d rc1.d rc2.d ……
/usr分区 :
usr
┃
┏━━━━┳━━━╋━━━┳━━━┳━━━┓
┃ ┃ ┃ ┃ ┃ ┃
X11R6 src lib local man bin
┃ ┃
┃ ┏━━━╋━━━┓
┃ ┃ ┃ ┃
linux bin lib src
挂载之后就形成了文章开始时的那个图。像不像挂上去的?
注意:1、挂载点必须是一个目录。
2、一个分区挂载在一个已存在的目录上,这个目录可以不为空,但挂载后这个目录下以前的内容将不可用。
对于其他操作系统建立的文件系统的挂载也是这样。但是需要理解的是:光盘、软盘、其他操作系统使用的文件系统的格式与linux使用的文件系统格式是不一样的。光盘是ISO9660;软盘是fat16或ext2;windows NT是fat16、NTFS;windows98是fat16、fat32;windows2000和windowsXP是fat16、fat32、 NTFS。挂载前要了解linux是否支持所要挂载的文件系统格式。
挂载时使用mount命令:
格式:mount [-参数] [设备名称] [挂载点]
其中常用的参数有
-t 指定设备的文件系统类型,常见的有:
minix linux最早使用的文件系统
ext2 linux目前常用的文件系统
msdos MS-DOS的fat,就是fat16
vfat windows98常用的fat32
nfs 网络文件系统
iso9660 CD-ROM光盘标准文件系统
ntfs windows NT 2000的文件系统
hpfs OS/2文件系统
auto 自动检测文件系统
-o 指定挂载文件系统时的选项。有些也可用在/etc/fstab中。常用的 有
codepage=XXX 代码页
iocharset=XXX 字符集
ro 以只读方式挂载
rw 以读写方式挂载
nouser 使一般用户无法挂载
user 可以让一般用户挂载设备
提醒一下,mount命令没有建立挂载点的功能,因此你应该确保执行mount命令时,挂载点已经存在。(不懂?说白了点就是你要把文件系统挂载到哪,首先要先建上个目录。这样OK?)
例子:windows98装在hda1分区,同时计算机上还有软盘和光盘需要挂载。
# mk /mnt/winc
# mk /mnt/floppy
# mk /mnt/cdrom
# mount -t vfat /dev/hda1 /mnt/winc
# mount -t msdos /dev/fd0 /mnt/floppy
# mount -t iso9660 /dev/cdrom /mnt/cdrom
现在就可以进入/mnt/winc等目录读写这些文件系统了。
要保证最后两行的命令不出错,要确保软驱和光驱里有盘。(要是硬盘的磁盘片也可以经常随时更换的话,我想就不会犯这样的错误了 :-> )
如果你的windows98目录里有中文文件名,使用上面的命令挂载后,显示的是一堆乱码。这就要用到 -o 参数里的codepage iocharset选项。codepage指定文件系统的代码页,简体中文中文代码是936;iocharset指定字符集,简体中文一般用cp936或 gb2312。
当挂载的文件系统linux不支持时,mount一定报错,如windows2000的ntfs文件系统。可以重新编译linux内核以获得对该文件系统的支持。关于重新编译linux内核,就不在这里说了。
四 、自动挂载
每次开机访问windows分区都要运行mount命令显然太烦琐,为什么访问其他的linux分区不用使用mount命令呢?
其实,每次开机时,linux自动将需要挂载的linux分区挂载上了。那么我们是不是可以设定让linux在启动的时候也挂载我们希望挂载的分区,如windows分区,以实现文件系统的自动挂载呢?
这是完全可以的。在/etc目录下有个fstab文件,它里面列出了linux开机时自动挂载的文件系统的列表。我的/etc/fstab文件如下:
/dev/hda2 / ext3 defaults 1 1
/dev/hda1 /boot ext3 defaults 1 2
none /dev/pts devpts gid=5,mode=620 0 0
none /proc proc defaults 0 0
none /dev/shm tmpfs defaults 0 0
/dev/hda3 swap swap defaults 0 0
/dev/cdrom /mnt/cdrom iso9660 noauto,codepage=936,iocharset=gb2312 0 0
/dev/fd0 /mnt/floppy auto noauto,owner,kudzu 0 0
/dev/hdb1 /mnt/winc vfat defaults,codepage=936,iocharset=cp936 0 0
/dev/hda5 /mnt/wind vfat defaults,codepage=936,iocharset=cp936 0 0
在/etc/fstab文件里,第一列是挂载的文件系统的设备名,第二列是挂载点,第三列是挂载的文件系统类型,第四列是挂载的选项,选项间用逗号分隔。第五六列不知道是什么意思,还望高手指点。
在最后两行是我手工添加的windows下的C;D盘,加了codepage=936和iocharset=cp936参数以支持中文文件名。参数defaults实际上包含了一组默认参数:
rw 以可读写模式挂载
suid 开启用户ID和群组ID设置位
dev 可解读文件系统上的字符或区块设备
exec 可执行二进制文件
auto 自动挂载
nouser 使一般用户无法挂载
async 以非同步方式执行文件系统的输入输出操作
大家可以看到在这个列表里,光驱和软驱是不自动挂载的,参数设置为noauto。(如果你非要设成自动挂载,你要确保每次开机时你的光驱和软驱里都要有盘,呵呵。)
Hadoop分布式文件系统:架构和设计要点
一、前提和设计目标
1、硬件错误是常态,而非异常情况,HDFS可能是有成百上千的server组成,任何一个组件都有可能一直失效,因此错误检测和快速、自动的恢复是HDFS的核心架构目标。
2、跑在HDFS上的应用与一般的应用不同,它们主要是以流式读为主,做批量处理;比之关注数据访问的低延迟问题,更关键的在于数据访问的高吞吐量。
3、HDFS以支持大数据集合为目标,一个存储在上面的典型文件大小一般都在千兆至T字节,一个单一HDFS实例应该能支撑数以千万计的文件。
4、 HDFS应用对文件要求的是write-one-read-many访问模型。一个文件经过创建、写,关闭之后就不需要改变。这一假设简化了数据一致性问题,使高吞吐量的数据访问成为可能。典型的如MapReduce框架,或者一个web crawler应用都很适合这个模型。
5、移动计算的代价比之移动数据的代价低。一个应用请求的计算,离它操作的数据越近就越高效,这在数据达到海量级别的时候更是如此。将计算移动到数据附近,比之将数据移动到应用所在显然更好,HDFS提供给应用这样的接口。
6、在异构的软硬件平台间的可移植性。
二、Namenode和Datanode
HDFS采用master/slave架构。一个HDFS集群是有一个Namenode和一定数目的Datanode组成。Namenode是一个中心服务器,负责管理文件系统的namespace和客户端对文件的访问。Datanode在集群中一般是一个节点一个,负责管理节点上它们附带的存储。在内部,一个文件其实分成一个或多个block,这些block存储在Datanode集合里。Namenode执行文件系统的namespace操作,例如打开、关闭、重命名文件和目录,同时决定block到具体Datanode节点的映射。Datanode在Namenode的指挥下进行block的创建、删除和复制。Namenode和Datanode都是设计成可以跑在普通的廉价的运行linux的机器上。HDFS采用java语言开发,因此可以部署在很大范围的机器上。一个典型的部署场景是一台机器跑一个单独的Namenode节点,集群中的其他机器各跑一个Datanode实例。这个架构并不排除一台机器上跑多个Datanode,不过这比较少见。
单一节点的Namenode大大简化了系统的架构。Namenode负责保管和管理所有的HDFS元数据,因而用户数据就不需要通过Namenode(也就是说文件数据的读写是直接在Datanode上)。
三、文件系统的namespace
HDFS支持传统的层次型文件组织,与大多数其他文件系统类似,用户可以创建目录,并在其间创建、删除、移动和重命名文件。HDFS不支持user quotas和访问权限,也不支持链接(link),不过当前的架构并不排除实现这些特性。Namenode维护文件系统的namespace,任何对文件系统namespace和文件属性的修改都将被Namenode记录下来。应用可以设置HDFS保存的文件的副本数目,文件副本的数目称为文件的 replication因子,这个信息也是由Namenode保存。
四、数据复制
HDFS被设计成在一个大集群中可以跨机器地可靠地存储海量的文件。它将每个文件存储成block序列,除了最后一个block,所有的block都是同样的大小。文件的所有block为了容错都会被复制。每个文件的block大小和replication因子都是可配置的。Replication因子可以在文件创建的时候配置,以后也可以改变。HDFS中的文件是write-one,并且严格要求在任何时候只有一个writer。Namenode全权管理block的复制,它周期性地从集群中的每个Datanode接收心跳包和一个Blockreport。心跳包的接收表示该Datanode节点正常工作,而Blockreport包括了该Datanode上所有的block组成的列表。
1、副本的存放,副本的存放是HDFS可靠性和性能的关键。HDFS采用一种称为rack-aware的策略来改进数据的可靠性、有效性和网络带宽的利用。这个策略实现的短期目标是验证在生产环境下的表现,观察它的行为,构建测试和研究的基础,以便实现更先进的策略。庞大的HDFS实例一般运行在多个机架的计算机形成的集群上,不同机架间的两台机器的通讯需要通过交换机,显然通常情况下,同一个机架内的两个节点间的带宽会比不同机架间的两台机器的带宽大。
通过一个称为Rack Awareness的过程,Namenode决定了每个Datanode所属的rack id。一个简单但没有优化的策略就是将副本存放在单独的机架上。这样可以防止整个机架(非副本存放)失效的情况,并且允许读数据的时候可以从多个机架读取。这个简单策略设置可以将副本分布在集群中,有利于组件失败情况下的负载均衡。但是,这个简单策略加大了写的代价,因为一个写操作需要传输block到多个机架。
在大多数情况下,replication因子是3,HDFS的存放策略是将一个副本存放在本地机架上的节点,一个副本放在同一机架上的另一个节点,最后一个副本放在不同机架上的一个节点。机架的错误远远比节点的错误少,这个策略不会影响到数据的可靠性和有效性。三分之一的副本在一个节点上,三分之二在一个机架上,其他保存在剩下的机架中,这一策略改进了写的性能。
2、副本的选择,为了降低整体的带宽消耗和读延时,HDFS会尽量让reader读最近的副本。如果在reader的同一个机架上有一个副本,那么就读该副本。如果一个HDFS集群跨越多个数据中心,那么reader也将首先尝试读本地数据中心的副本。
3、SafeMode
Namenode启动后会进入一个称为SafeMode的特殊状态,处在这个状态的Namenode是不会进行数据块的复制的。Namenode从所有的 Datanode接收心跳包和Blockreport。Blockreport包括了某个Datanode所有的数据块列表。每个block都有指定的最小数目的副本。当Namenode检测确认某个Datanode的数据块副本的最小数目,那么该Datanode就会被认为是安全的;如果一定百分比(这个参数可配置)的数据块检测确认是安全的,那么Namenode将退出SafeMode状态,接下来它会确定还有哪些数据块的副本没有达到指定数目,并将这些block复制到其他Datanode。
五、文件系统元数据的持久化
Namenode存储HDFS的元数据。对于任何对文件元数据产生修改的操作,Namenode都使用一个称为Editlog的事务日志记录下来。例如,在HDFS中创建一个文件,Namenode就会在Editlog中插入一条记录来表示;同样,修改文件的replication因子也将往 Editlog插入一条记录。Namenode在本地OS的文件系统中存储这个Editlog。整个文件系统的namespace,包括block到文件的映射、文件的属性,都存储在称为FsImage的文件中,这个文件也是放在Namenode所在系统的文件系统上。
Namenode在内存中保存着整个文件系统namespace和文件Blockmap的映像。这个关键的元数据设计得很紧凑,因而一个带有4G内存的 Namenode足够支撑海量的文件和目录。当Namenode启动时,它从硬盘中读取Editlog和FsImage,将所有Editlog中的事务作用(apply)在内存中的FsImage ,并将这个新版本的FsImage从内存中flush到硬盘上,然后再truncate这个旧的Editlog,因为这个旧的Editlog的事务都已经作用在FsImage上了。这个过程称为checkpoint。在当前实现中,checkpoint只发生在Namenode启动时,在不久的将来我们将实现支持周期性的checkpoint。
Datanode并不知道关于文件的任何东西,除了将文件中的数据保存在本地的文件系统上。它把每个HDFS数据块存储在本地文件系统上隔离的文件中。 Datanode并不在同一个目录创建所有的文件,相反,它用启发式地方法来确定每个目录的最佳文件数目,并且在适当的时候创建子目录。在同一个目录创建所有的文件不是最优的选择,因为本地文件系统可能无法高效地在单一目录中支持大量的文件。当一个Datanode启动时,它扫描本地文件系统,对这些本地文件产生相应的一个所有HDFS数据块的列表,然后发送报告到Namenode,这个报告就是Blockreport。
六、通讯协议
所有的HDFS通讯协议都是构建在TCP/IP协议上。客户端通过一个可配置的端口连接到Namenode,通过ClientProtocol与 Namenode交互。而Datanode是使用DatanodeProtocol与Namenode交互。从ClientProtocol和 Datanodeprotocol抽象出一个远程调用(RPC),在设计上,Namenode不会主动发起RPC,而是是响应来自客户端和 Datanode 的RPC请求。
七、健壮性
HDFS的主要目标就是实现在失败情况下的数据存储可靠性。常见的三种失败:Namenode failures, Datanode failures和网络分割(network partitions)。
1、硬盘数据错误、心跳检测和重新复制
每个Datanode节点都向Namenode周期性地发送心跳包。网络切割可能导致一部分Datanode跟Namenode失去联系。 Namenode通过心跳包的缺失检测到这一情况,并将这些Datanode标记为dead,不会将新的IO请求发给它们。寄存在dead Datanode上的任何数据将不再有效。Datanode的死亡可能引起一些block的副本数目低于指定值,Namenode不断地跟踪需要复制的 block,在任何需要的情况下启动复制。在下列情况可能需要重新复制:某个Datanode节点失效,某个副本遭到损坏,Datanode上的硬盘错误,或者文件的replication因子增大。
2、集群均衡
HDFS支持数据的均衡计划,如果某个Datanode节点上的空闲空间低于特定的临界点,那么就会启动一个计划自动地将数据从一个Datanode搬移到空闲的Datanode。当对某个文件的请求突然增加,那么也可能启动一个计划创建该文件新的副本,并分布到集群中以满足应用的要求。这些均衡计划目前还没有实现。
3、数据完整性
从某个Datanode获取的数据块有可能是损坏的,这个损坏可能是由于Datanode的存储设备错误、网络错误或者软件bug造成的。HDFS客户端软件实现了HDFS文件内容的校验和。当某个客户端创建一个新的HDFS文件,会计算这个文件每个block的校验和,并作为一个单独的隐藏文件保存这些校验和在同一个HDFS namespace下。当客户端检索文件内容,它会确认从Datanode获取的数据跟相应的校验和文件中的校验和是否匹配,如果不匹配,客户端可以选择从其他Datanode获取该block的副本。
4、元数据磁盘错误
FsImage和Editlog是HDFS的核心数据结构。这些文件如果损坏了,整个HDFS实例都将失效。因而,Namenode可以配置成支持维护多个FsImage和Editlog的拷贝。任何对FsImage或者Editlog的修改,都将同步到它们的副本上。这个同步操作可能会降低 Namenode每秒能支持处理的namespace事务。这个代价是可以接受的,因为HDFS是数据密集的,而非元数据密集。当Namenode重启的时候,它总是选取最近的一致的FsImage和Editlog使用。
Namenode在HDFS是单点存在,如果Namenode所在的机器错误,手工的干预是必须的。目前,在另一台机器上重启因故障而停止服务的Namenode这个功能还没实现。
5、快照
快照支持某个时间的数据拷贝,当HDFS数据损坏的时候,可以恢复到过去一个已知正确的时间点。HDFS目前还不支持快照功能。
八、数据组织
1、数据块
兼容HDFS的应用都是处理大数据集合的。这些应用都是写数据一次,读却是一次到多次,并且读的速度要满足流式读。HDFS支持文件的write- once-read-many语义。一个典型的block大小是64MB,因而,文件总是按照64M切分成chunk,每个chunk存储于不同的 Datanode
2、步骤
某个客户端创建文件的请求其实并没有立即发给Namenode,事实上,HDFS客户端会将文件数据缓存到本地的一个临时文件。应用的写被透明地重定向到这个临时文件。当这个临时文件累积的数据超过一个block的大小(默认64M),客户端才会联系Namenode。Namenode将文件名插入文件系统的层次结构中,并且分配一个数据块给它,然后返回Datanode的标识符和目标数据块给客户端。客户端将本地临时文件flush到指定的 Datanode上。当文件关闭时,在临时文件中剩余的没有flush的数据也会传输到指定的Datanode,然后客户端告诉Namenode文件已经关闭。此时Namenode才将文件创建操作提交到持久存储。如果Namenode在文件关闭前挂了,该文件将丢失。
上述方法是对通过对HDFS上运行的目标应用认真考虑的结果。如果不采用客户端缓存,由于网络速度和网络堵塞会对吞估量造成比较大的影响。
3、流水线复制
当某个客户端向HDFS文件写数据的时候,一开始是写入本地临时文件,假设该文件的replication因子设置为3,那么客户端会从Namenode 获取一张Datanode列表来存放副本。然后客户端开始向第一个Datanode传输数据,第一个Datanode一小部分一小部分(4kb)地接收数据,将每个部分写入本地仓库,并且同时传输该部分到第二个Datanode节点。第二个Datanode也是这样,边收边传,一小部分一小部分地收,存储在本地仓库,同时传给第三个Datanode,第三个Datanode就仅仅是接收并存储了。这就是流水线式的复制。
九、可访问性
HDFS给应用提供了多种访问方式,可以通过DFSShell通过命令行与HDFS数据进行交互,可以通过java API调用,也可以通过C语言的封装API访问,并且提供了浏览器访问的方式。正在开发通过WebDav协议访问的方式。具体使用参考文档。
十、空间的回收
1、文件的删除和恢复
用户或者应用删除某个文件,这个文件并没有立刻从HDFS中删除。相反,HDFS将这个文件重命名,并转移到/trash目录。当文件还在/trash目录时,该文件可以被迅速地恢复。文件在/trash中保存的时间是可配置的,当超过这个时间,Namenode就会将该文件从namespace中删除。文件的删除,也将释放关联该文件的数据块。注意到,在文件被用户删除和HDFS空闲空间的增加之间会有一个等待时间延迟。
当被删除的文件还保留在/trash目录中的时候,如果用户想恢复这个文件,可以检索浏览/trash目录并检索该文件。/trash目录仅仅保存被删除文件的最近一次拷贝。/trash目录与其他文件目录没有什么不同,除了一点:HDFS在该目录上应用了一个特殊的策略来自动删除文件,目前的默认策略是删除保留超过6小时的文件,这个策略以后会定义成可配置的接口。
2、Replication因子的减小
当某个文件的replication因子减小,Namenode会选择要删除的过剩的副本。下次心跳检测就将该信息传递给Datanode, Datanode就会移除相应的block并释放空间,同样,在调用setReplication方法和集群中的空闲空间增加之间会有一个时间延迟。