【 tulaoshi.com - 编程语言 】
一、Linux 的文件系统概述
Linux 的一个最重要特点就是它支持许多不同的文件系统。这使Linux 非常灵活,能够与许多其他的操作系统共存。到目前为止,Linux 共支持1 5 种文件系统:ext、ext2xia、minix、umsdos、msdos、vfat、proc、smb、ncp、iso9660、sysv、hpfs、affs和ufs。无疑随着时间的推移,Linux支持的文件系统数还会增加。
正如其它的UNIX操作系统 一样,Linux系统可用的独立文件系统不是通过设备标识来访问的,而是把它们链接到一个单独的树形层次结构中。该树形层次结构把文件系统表示成一个整个的独立实体。Linux 以装配的形式把每个新的文件系统加入到这个单独的文件系统树中。无论什么类型的文件系统,都被装配到某个目录上,由被装配的文件系统的文件覆盖该目录原有的内容。该个目录被称为装配目录或装配点。在文件系统卸载时,装配目录中原有的文件才会显露出来。
在硬盘初始化时,硬盘上的分区结构把物理硬盘化分成若干个逻辑分区。每个分区可以包含一个像EXT2那样的一个独立的文件系统。文件系统用目录将文件按照逻辑层次结构组织起来。目录是记录在物理设备块中的软链接信息。包含文件系统的设备被称为块设备。例如IDE硬盘分区/dev/hda1-系统中第一个IDE硬盘驱动器的第一个分区,就是一个块设备。
Linux文件系统把这些块设备当作简单的线性块的集合,它不知道也不在意下层物理硬盘的实际结构。每个块设备驱动程序的任务就是把系统读该设备上某一块的请求映射成对本设备有意义的术语,如该块所在的磁道、扇区或柱面号。无论文件系统存在在何种设备上,它都可以按相同的方式进行操作。而且在使用文件系统时,由不同的硬件控制器控制的不同物理介质上的不同文件系统对系统用户是透明的。文件系统既可能在本地系统上的,也可能是通过网络链接装配的远程文件系统。
文件是数据的集合,一个文件系统不仅包括该文件系统中所有文件的数据,还包括文件系统的结构信息。它记录下所有Linux用户和进程当作文件看待的信息、目录软链接信息以及文件保护信息等等。而且文件系统必需保证这些信息的安全性,因为操作系统的基本完整性就取决于它的文件系统。没有人会使用一个随时会丢失数据和文件的操作系统。 由于现代的数据库系统需要更大的文件长度,在1993年几乎是为LINUX度身定做的扩展文件系统-EXT2诞生了。在EXT文件系统加入到Linux中时,Linux系统发生了一个重大的发展。真实文件系统从操作系统中分离出来,而由一个接口层提供的真实文件系统的系统服务被称为虚拟文件系统(VFS)。VFS使得Linux可以支持许多种不同的文件系统,而这些文件系统都向VFS提供相同的软件接口。由于所有的Linux文件系统的细节都是由软件进行转换的,所有对Linux系统的其余部分和在系统中运行的程序来说,这些文件系统是完全相同的。Linux的虚拟文件系统层使得你可以同时透明地装配很多不同的文件系统。
实现Linux虚拟文件系统要使得它对文件的访问要尽可能地快、尽可能地高效,而且一定要确保文件和数据的正确性。这两个要求彼此是不对称的。在每个文件系统被装配使用后,Linux的VFS会在内存中缓存来自于这些文件系统的信息。因此由于对文件或目录的创建、写、删除操作而改变了Linux缓存中的数据时,对文件系统的更新操作要格外小心。假如你能在运行的内核中看到文件系统的数据结构,就会看到那些文件系统读/写的数据块。代表被访问的文件和目录的数据结构可能被创建或删除,而设备驱动程序不停地工作,读取数据、保存数据。这些缓存中最重要的一个是缓冲区缓存,它把独立文件系统访问下层块设备的方法集成起来。每个被访问的块都被放到缓冲区缓存中,并根据它们的状态放在相应的队列中。缓冲区缓存不仅缓存数据缓冲区,它还有助于治理块设备驱动程序的异步接口。
二、LINUX文件系统代码的位置
EXT2文件系统的代码都在fs/ext2/目录下,其数据结构定义在include/linux/ex2_fs.h、ext2_fs_i.h和ext2_fs_sb.h中;虚文件系统(Virtual File System)数据结构在include/linux/fs.h中,代码在fs/*中;缓冲区缓存代码在fs/buffer.c中。
三、与文件的输入/输出有关的函数
3.1 引言
本节开始讨论LINUX系统,先说明常用的文件I/O函数:例如打开文件、读文件、写文件等等。
大多数LINUX文件I/O只需用到5个函数:open、read、write、lseek 以及close。然后说明不同缓存器长度对read 和write函数的影响。本节所说明的函数经常被称之为不带缓存的I/O(unbuffered I/O)。不带缓存指的是每个read 和write都调用内核中的一个系统调用。这些不带缓存的I/O函数不是ANSI C 的组成部分,但是是POSIX.1和XPG3的组成部分。只要涉及在多个进程间共享资源,原子操作的概念就变成非常重要。我们将通过文件I/O和传送给open函数的参数来讨论此概念。并进一步讨论在多个进程间如何共享文件,并涉及内核的有关数据结构。在讨论了这些特征后,将说明dup、fcntl和ioctl函数。
3.2 文件描述符
对于内核而言,所有打开文件都由文件描述符引用。文件描述符是一个非负整数。当打开一个现存文件或创建一个新文件时,内核向进程返回一个文件描述符。当读、写一个文件时,用open或creat返回的文件描述符标识该文件,将其作为参数传送给read或write。按照惯例,UNIX shell使文件描述符0与进程的标准输入相结合,文件描述符1与标准输出相结合,文件描述符2与标准出错输出相结合。这是UNIX shell以及很多应用程序使用的惯例,而与内核无关。尽管如此,假如不遵照这种惯例,那么很多LINUX应用程序就不能工作。在POSIX.1应用程序中,幻数0、1、2应被代换成符号常数STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO。这些常数都定义在头文unistd.h中。文件描述符的范围是0~OPEN_MAX(见表2-7)。早期的LINUX版本采用的上限值是19(答应每个进程打开20个文件),现在很多系统则将其增加至63 。
3.3 open 函数
调用open函数可以打开或创建一个文件。例如:
#include sys/types.h
#include sys/stat.h
#include fcntl.h
int open(const char *pathname,int oflag,.../*,mode_t mode*/) ;
返回:若成功为文件描述符,若出错为-1
我们将第三个参数写为. . .,这是ANSI C 说明余下参数的数目和类型可以变化的方法。对于open 函数而言,仅当创建新文件时才使用第三个参数。在函数原型中此参数放置在注释中。pathname是要打开或创建的文件的名字。oflag参数可用来说明此函数的多个选择项。用下列一个或多个常数进行或运算构成oflag参数(这些常数定义在fcntl.h头文件中):
o O_RDONLY 只读打开。
o O_WRONLY 只写打开。
o O_RDWR 读、写打开。
3.4 creat 函数
creat函数用来创建一个新文件。
#include sys/types.h
#include sys/stat.h
#include fcntl.h
int creat(const char *pathname, mode_t mode) ;
返回:若成功为只写打开的文件描述符,若出错为-1
此函数等效于:open(pathname,O_WRONLY |O_CREAT|O_TRUNC,mode) ;在早期的UNIX版本中,open的第二个参数只能是0、1或2。没有办法打开一个尚未存在的文件,因此需要另一个系统调用creat以创建新文件。现在,open函数提供了选择项O_CREAT和O_TRUNC,于是也就不再需要creat函数了。
creat的一个不足之处是它以只写方式打开所创建的文件。在提供open的新版本之前,假如要创建一个临时文件,并要先写该文件,然后又读该文件,则必须先调用creat,close,然后再调用open。现在则可用下列方式调用open:
open(pathname,O_RDWR |O_CREAT|O_TRUNC, mode) ;
3.5 close 函数
close函数用于关闭一个打开文件:
#include unistd.h
int close (int filedes);
返回:若成功为0 ,若出错为-1
关闭一个文件时也会释放该进程加在该文件上的所有记录锁。当一个进程终止时,它所有的打开文件都由内核自动关闭。很多程序都使用这一功能而不显式地用close关闭打开的文件。
3.6 lseek 函数
每个打开文件都有一个与其相关联的"当前文件位移量"。它是一个非负整数,用以度量从文件开始处计算的字节数。(本节稍后将对"非负"这一修饰词的某些例外进行说明。)通常,读、写操作都从当前文件位移量处开始,并使位移量增加所读或写的字节数。按系统默认,当打开一个文件时,除非指定O_APPEND选择项,否则该位移量被设置为0。
可以调用l s e e k 显式地定位一个打开文件。
#include sys/types.h
#include unistd.h
off_t lseek(int f i l e d e s, off_t o f f s e t, int w h e n c e) ;
返回:若成功为新的文件位移,若出错为-1
对参数offset 的解释与参数w h e n c e 的值有关:
o 若whence是SEEK_SET,则将该文件的位移量设置为距文件开始处offset 个字节。
o 若whence是SEEK_CUR,则将该文件的位移量设置为其当前值加offset, offset 可为正或负。
o 若whence是SEEK_END,则将该文件的位移量设置为文件长度加offset, offset 可为正或负。
若lseek成功执行,则返回新的文件位移量,为此可以用下列方式确定一个打开文件的当前位