常规文件
常规文件(Regular files):是包含有用户信息的文件。包括:C 语言元代码、Shell 脚本、文本文件、图像文件、二进制的可执行文件等。从常规文件读取数据或将数据写入时,内核会根据文件系统的规则执行操作,写入可能被延迟,记录日志或者接受其他操作。一般分为 ASCII 码文件或者二进制文件。
ASCII 码文件
ASCII 码文件:由文本组成。在一些系统中,每行都会用回车符结束(ASCII 码是 13,控制字符 CR,转义字符 \r),另外一些则会使用换行符(ASCII 码是 10,控制字符 LF,转义字符 \n),也有系统(比如 Windows)两者都会使用。ASCII 文件的优点在于显示 和 打印,还可以用任何文本编辑器进行编辑。进一步来说,如果许多应用程序使用 ASCII 码作为输入和输出,那么很容易就能够把多个程序连接起来,一个程序的输出可能是另一个程序的输入,就像管道一样。
二进制文件
二进制文件:取自早期的 UNIX,尽管从技术上来看这个文件只是字节序列,但是操作系统只有在文件格式正确的情况下才会执行。
这个文件有五个段:文件头、正文、数据、重定位位和符号表。文件头以魔数(magic number)开始,表明这个文件是一个可执行文件(以防止意外执行非此格式的文件)。然后是文件各个部分的大小,开始执行的标志以及一些标志位。程序本身的正文和数据在文件头后面,他们被加载到内存中或者重定位会根据重定位位进行判断。符号表则用于调试(e.g. gdb)。
二进制文件的另外一种形式是存档文件,它由已编译但没有链接的库(模块)组合而成。每个文件都以模块头开始,其中记录了名称、创建日期、所有者、保护码和文件大小。和可执行文件一样,模块头也都是二进制数,将它们复制到打印机将会产生乱码。
所有的操作系统必须至少能够识别一种文件类型:它自己的可执行文件。以前的 TOPS-20 系统(用于 DECsystem 20)甚至要检查要执行的任何文件的创建时间,为了定位资源文件来检查自动文件创建后是否被修改过。如果被修改过了,那么就会自动编译文件。在 UNIX 中,就是在 Shell 中嵌入 make 程序。此时操作系统要求用户必须采用固定的文件扩展名,从而确定哪个源程序生成哪个二进制文件。
什么是 make 程序?在软件发展过程中,make 程序是一个自动编译的工具,它通过读取称为 Makefiles 的文件来自动从源代码构建可执行程序和库,该文件指定了如何导出目标程序。尽管集成开发环境和特定语言的编译器功能也可以用于管理构建过程,但 Make 仍被广泛使用,尤其是在 UNIX 和类似 UNIX 的操作系统中使用。
当程序从文件中读写数据时,请求会转到内核处理程序(Kernel driver)。如果文件是常规文件,则数据由文件系统驱动程序处理,并且通常存储在磁盘或其他存储介质上的某块区域中,从文件中读取的数据就是之前在该位置写入的数据。
当数据读取或写入到设备文件时,请求会被设备驱动程序处理。每个设备文件都有一个关联的编号,该编号标示要使用的设备驱动程序。设备处理数据的工作是它自己的事儿。
设备文件
设备文件:与系统外设相关的,通常在 /dev 下面。分为块设备和字符设备。
字符设备文件
字符设备,也称为字符特殊文件(Character special file):和输入/输出有关,用于串行 I/O 类设备,如:终端、打印机、网络等。它的行为类似于管道、串行端口。将字节写入字符设备可能会导致它在屏幕上显示,在串行端口上输出,转换为声音。
块设备文件
块设备,也称为块特殊文件(block special file):用于磁盘类设备。它的行为通常与普通文件相似:它们是字节数组,并且在给定位置读取的值是最后写入该位置的值。来自块设备的数据可以缓存在内存中,并从缓存中读取;写入可以被缓冲。块设备通常是可搜索的,块设备的概念是,相应的硬件可以一次读取或者写入整个块,例如磁盘上的一个扇区。
目录文件
目录(Directories)是管理文件系统结构的系统文件。它是用于在计算机上存储文,存储文件的唯一地方。目录位于分层文件系统中,例如 Linux,MS-DOS 和 UNIX。
和 DOS 等操作系统不同,Linux 操作系统中单独的文件系统并不是由驱动器号或驱动器名称(e.g. A:、C: 等)来标识的。相反,和 UNIX 操作系统一样,Linux 操作系统将独立的文件系统组合成了一个层次化的树形结构,并且由一个单独的实体代表这一文件系统。Linux 将新的文件系统通过一个称为 “挂装” 或 “挂上” 的操作将其挂装到某个目录上,从而让不同的文件系统结合成为一个整体。
Linux 使用标准的目录结构,在安装的时候,安装程序就已经为用户创建了文件系统和完整而固定的目录结构,并指定了每个目录的作用和其中的文件类型。
完整的目录树可划分为小的部分,这些小部分又可以单独存放在自己的磁盘或分区上。这样,相对稳定的部分和经常变化的部分可单独存放在不同的分区中,从而方便备份或系统管理。
Linux 采用的是树型结构。最上层是根目录,其他的所有目录都是从根目录出发而生成的。在 Linux 中,无论操作系统管理几个磁盘分区,这样的目录树只有一个。从结构上讲,各个磁盘分区上的树型目录不一定是并列的。
- /bin:二进制可执行命令
- /dev:设备特殊文件
- /etc:系统管理和配置文件
- /etc/rc.d:启动的配置文件和脚本
- /home:用户主目录的基点
- /lib:标准程序设计库,又叫动态链接共享库
- /sbin:系统管理命令,这里存放的是系统管理员使用的管理程序
- /tmp:公用的临时文件存储点
- /root:系统管理员的主目录
- /mnt:系统提供这个目录是让用户临时挂载其他的文件系统
- /lost+found:这个目录平时是空的,存放系统在非正常关机情况下而留下 “无家可归” 的文件
- /proc:虚拟的目录,是系统内存的映射。可直接访问这个目录来获取系统信息。
- /var:某些大文件的溢出区,比方说各种服务的日志文件
- /usr:最庞大的目录,要用到的应用程序和文件几乎都在这个目录。其中包含:
- /usr/X11R6:存放 X window 程序的目录
- /usr/bin:众多的应用程序
- /usr/sbin:root 的一些管理程序
- /usr/doc:Linux 文档
- /usr/include:Linux 下开发和编译应用程序所需要的头文件
- /usr/lib:常用的动态链接库和软件包的配置文件
- /usr/man:帮助文档
- /usr/src:源代码,Linux 内核的源代码就放在 /usr/src/linux 下
- /usr/local/bin:本地增加的命令
- /usr/local/lib:本地增加的库
其他文件
- 链接文件:指向同一个文件或目录的的文件。
- 管道(FIFO)文件 : 提供进程建通信的一种方式。
- 套接字(Socket) 文件: 该文件类型与网络通信有关。
文件的构造有多种方式。上图列出了常用的三种构造方式:
-
上图中的 a 是一种无结构的字节序列,操作系统不关心序列的内容是什么,操作系统能看到的就是字节(Bytes)。其文件内容的任何含义只在用户程序中进行解释。UNIX 和 Windows 都采用这种办法。把文件看成字节序列提供了最大的灵活性。用户程序可以向文件中写任何内容,并且可以通过任何方便的形式命名。操作系统不会为为用户写入内容提供帮助,当然也不会干扰阻塞你。对于想做特殊操作的用户来说,后者是十分重要的。所有的 UNIX 版本(包括 Linux 和 OS X)和 Windows 都使用这种文件模型。
-
图 b 表示在文件结构上的第一步改进。在这个模型中,文件是具有固定长度记录的序列,每个记录都有其内部结构。把文件作为记录序列的核心思想是:读操作返回一个记录,而写操作重写或者追加一个记录。
-
第三种文件结构如上图 c 所示。在这种组织结构中,文件由一颗记录树构成,记录树的长度不一定相同,每个记录树都在记录中的固定位置包含一个key 字段。这棵树按 key 进行排序,从而可以对特定的 key 进行快速查找。在记录树的结构中,可以取出下一个记录,但是最关键的还是根据 key 搜索指定的记录。如上图 c 所示,用户可以读出指定的 pony 记录,而不必关心记录在文件中的确切位置。用户也可以在文件中添加新的记录。但是用户不能决定添加到何处位置,添加到何处位置是由操作系统决定的。
- Create,创建不包含任何数据的文件。调用的目的是表示文件即将建立,并对文件设置一些属性。
- Delete,当文件不再需要,必须删除它以释放内存空间。为此总会有一个系统调用来删除文件。
- Open,在使用文件之前,必须先打开文件。这个调用的目的是允许系统将属性和磁盘地址列表保存到主存中,用来以后的快速访问。
- Close,当所有进程完成时,属性和磁盘地址不再需要,因此应关闭文件以释放表空间。很多系统限制进程打开文件的个数,以此达到鼓励用户关闭不再使用的文件。磁盘以块为单位写入,关闭文件时会强制写入最后一块,即使这个块空间内部还不满。
- Read,数据从文件中读取。通常情况下,读取的数据来自文件的当前位置。调用者必须指定需要读取多少数据,并且提供存放这些数据的缓冲区。
- Write,向文件写数据,写操作一般也是从文件的当前位置开始进行。如果当前位置是文件的末尾,则会直接追加进行写入。如果当前位置在文件中,则现有数据被覆盖,并且永远消失。
- Append,使用 append 只能向文件末尾添加数据。
- Seek,对于随机访问的文件,要指定从何处开始获取数据。通常的方法是用 seek 系统调用把当前位置指针指向文件中的特定位置。seek 调用结束后,就可以从指定位置开始读写数据了。
- Get attributes,进程运行时通常需要读取文件属性。
- Set attributes,用户可以自己设置一些文件属性,甚至是在文件创建之后,实现该功能的是 set attributes 系统调用。
- Rename,用户可以自己更改已有文件的名字,rename 系统调用用于这一目的。
- create,创建目录,除了目录项 . 和 … 外,目录内容为空。
- delete,删除目录,只有空目录可以删除。只包含 . 和 … 的目录被认为是空目录,这两个目录项通常不能删除
- opendir,目录内容可被读取。例如,未列出目录中的全部文件,程序必须先打开该目录,然后读其中全部文件的文件名。与打开和读文件相同,在读目录前,必须先打开文件。
- closedir,读目录结束后,应该关闭目录用于释放内部表空间。
- readdir,系统调用 readdir 返回打开目录的下一个目录项。以前也采用 read 系统调用来读取目录,但是这种方法有一个缺点:程序员必须了解和处理目录的内部结构。相反,不论采用哪一种目录结构,readdir 总是以标准格式返回一个目录项。
- rename,在很多方面目录和文件都相似。文件可以更换名称,目录也可以。
- link,链接技术允许在多个目录中出现同一个文件。这个系统调用指定一个存在的文件和一个路径名,并建立从该文件到路径所指名字的链接。这样,可以在多个目录中出现同一个文件。有时也被称为硬链接(hard link)。
- unlink,删除目录项。如果被解除链接的文件只出现在一个目录中,则将它从文件中删除。如果它出现在多个目录中,则只删除指定路径名的链接,依然保留其他路径名的链接。在 UNIX 中,用于删除文件的系统调用就是 unlink。