一、Linux 系统中的文件类型 Linux 系统中的文件类型
Linux 下一切皆文件,文件作为 Linux 系统设计思想的核心理念。
1、普通文件
普通文件( regular file )在 Linux 系统下是最常见的,譬如文本文件、二进制文件,我们编写的源代码文件这些都是普通文件,也就是一般意义上的文件。普通文件中的数据存在系统磁盘中,可以访问文件中的内容,文件中的内容以字节为单位进行存储于访问。
⚫ 文本文件: 文件中的内容是由文本构成的,所谓文本指的是 ASCII 码字符。文件中的内容其本质上都是数字(因为计算机本身只有 0 和 1 ,存储在磁盘上的文件内容也都是由 0 和 1 所构成),而文本文件中的数字应该被理解为这个数字所对应的 ASCII 字符码;譬如常见的 .c 、 .h 、 .sh 、 .txt 等 这些都是文本文件,文本文件的好处就是方便人阅读、浏览以及编写。
⚫ 二进制文件: 二进制文件中存储的本质上也是数字,只不过对于二进制文件来说,这些数字并不是文本字符编码,而是真正的数字。譬如 Linux 系统下的可执行文件、 C 代码编译之后得到的 .o 文件、.bin 文件等都是二进制文件。
在 Linux 系统下,可以通过 stat 命令或者 ls 命令来查看文件类型,如下所示:
stat 命令非常友好,会直观把文件类型显示出来;对于 ls 命令来说,并没有直观的显示出文件的类型, 而是通过符号表示出来,在图 5.1.2 中画红色框位置显示出的一串字符中,其中第一个字符( ' - ' )就用于表 示文件的类型,减号' - ' 就表示该文件是一个普通文件;除此之外,来看看其它文件类型使用什么字符表示:
⚫ ' - ' :普通文件
⚫ ' d ' :目录文件
⚫ ' c ' :字符设备文件
⚫ ' b ' :块设备文件
⚫ ' l ' :符号链接文件
⚫ ' s ' :套接字文件
⚫ ' p ' :管道文件
2、目录文件
目录( directory )就是文件夹,文件夹在 Linux 系统中也是一种文件,是一种特殊文件,同样我们也可以使用vi 编辑器来打开文件夹。在 Linux 系统下,会有一些专门的系统调用用于读写文件夹
⚫ 普通文件由 inode 节点和数据块构成
⚫ 目录由 inode 节点和目录块构成
3、字符设备文件和块文件
Linux 系统下,一切皆文件,也包括各种硬件设备。设备文件(字符设备文件、块设备文件)对应的是硬件设备,在 Linux 系统中,硬件设备会对应到一个设备文件,应用程序通过对设备文件的读写来操控、使用硬件设备,譬如 LCD 显示屏、串口、音频、按键等。
虽然有设备文件,但是设备文件并不对应磁盘上的一个文件,也就是说设备文件并不存在于磁盘中, 而是由文件系统虚拟出来的,一般是由内存来维护,当系统关机时,设备文件都会消失;字符设备文件一般存放在 Linux 系统 /dev/ 目录下,所以 /dev 也称为虚拟文件系统 devfs
4、符号链接文件
符号链接文件( link )类似于 Windows 系统中的快捷方式文件,是一种特殊文件,它的内容指向的是另 一个文件路径,当对符号链接文件进行操作时,系统根据情况会对这个操作转移到它指向的文件上去,而不是对它本身进行操作,譬如,读取一个符号链接文件内容时,实际上读到的是它指向的文件的内容。
5、管道文件
管道文件( pipe )主要用于进程间通信
6、套接字文件
套接字文件( socket )也是一种进程间通信的方式,与管道文件不同的是,它们可以在不同主机上的进 程间通信,实际上就是网络通信。
总结:
普通文件是最常见的文件类型;
目录也是一种文件类型;
设备文件对应于硬件设备;
符号链接文件类似于 Windows 的快捷方式;
管道文件用于进程间通信;
套接字文件用于网络通信。
二、stat函数
Linux 下可以使用 stat 命令查看文件的属性,其实这个命令内部就是通过调用 stat() 函数来获取文件属性的,stat 函数是 Linux 中的系统调用,用于获取文件相关的信息。
三、fstat 和 lstat 函数
除了 stat 函数之外,还可以使用 fstat 和 lstat 两个系统调用来获取文件属性信息。fstat 、 lstat 与 stat 的作用一样,但是参数、细节方面有些许不同。
fstat 与 stat 区别在于, stat 是从文件名出发得到文件属性信息,不需要先打开文件;而 fstat 函数则是从文件描述符出发得到文件属性信息,所以使用 fstat 函数之前需要先打开文件得到文件描述符。
四、文件属主
Linux 是一个多用户操作系统,系统中一般存在着好几个不同的用户,而 Linux 系统中的每一个文件都有一个与之相关联的用户和用户组,通过这个信息可以判断文件的所有者和所属组。
文件所属组则表示该文件属于哪一个用户组。在 Linux 中,系统并不是通过用户名或用户组名来识别不同的用户和用户组,而是通过 ID 。 ID 就是一个编号, Linux 系统会为每一个用户或用户组分配一个 ID ,将 用户名或用户组名与对应的 ID 关联起来,所以系统通过用户 ID ( UID )或组 ID ( GID )就可以识别出不同 的用户和用户组。
譬如使用 ls 命令或 stat 命令便可以查看到文件的所有者和所属组,如下所示:
chown 函数
chown 是一个系统调用,该系统调用可用于改变文件的所有者(用户 ID )和所属组(组 ID )。
虽然该函数用法很简单,但是有以下两个限制条件:
⚫ 只有超级用户进程能更改文件的用户 ID ;
⚫ 普通用户进程可以将文件的组 ID 修改为其所从属的任意附属组 ID ,前提条件是该进程的有效用户 ID 等于文件的用户 ID ;而超级用户进程可以将文件的组 ID 修改为任意值。
在 Linux 系统下,可以使用 getuid 和 getgid 两个系统调用分别用于获取当前进程的用户 ID 和用户组 ID。
fchown 和 lchown 函数
这两个同样也是系统调用,作用与 chown 函数相同,只是参数、细节方面有些许不同。fchown()、 lchown() 这两个函数与 chown() 的区别就像是 fstat() 、 lstat() 与 stat 的区别。
五、文件访问权权限
修改文件权限 chmod
在 Linux 系统下,可以使用 chmod 命令修改文件权限,该命令内部实现方法其实是调用了 chmod 函数, chmod 函数是一个系统调用,函数原型如下所示(可通过 "man 2 chmod" 命令查看):
#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);
首先,使用该函数需要包含头文件 <sys/stat.h> 。
函数参数及返回值如下所示:
pathname : 需要进行权限修改的文件路径,若该参数所指为符号链接,实际改变权限的文件是符号链接所指向的文件,而不是符号链接文件本身。
mode : 该参数用于描述文件权限,与 open 函数的第三个参数一样,这里不再重述,可以直接使用八进制数据来描述,也可以使用相应的权限宏(单个或通过位或运算符" | " 组合)。
返回值: 成功返回 0 ;失败返回 -1 ,并设置 errno 。
文件权限对于文件来说是非常重要的属性,是不能随随便便被任何用户所修改的,要想更改文件权限, 要么是超级用户(root )进程、要么进程有效用户 ID 与文件的用户 ID (文件所有者)相配。
六、文件的时间属性
3 个文件的时间属性:文件最后被访问的时间、文件内容最后被修改的时间以及文件状态最后被改变的时间,分别记录在 struct stat 结构体的 st_atim 、 st_mtim 以及 st_ctim 变量中,如下所示:
⚫ 文件最后被访问的时间:访问指的是读取文件内容,文件内容最后一次被读取的时间,譬如使用read()函数读取文件内容便会改变该时间属性;
⚫ 文件内容最后被修改的时间:文件内容发生改变,譬如使用 write() 函数写入数据到文件中便会改变该时间属性;
⚫ 文件状态最后被改变的时间:状态更改指的是该文件的 inode 节点最后一次被修改的时间,譬如更 改文件的访问权限、更改文件的用户 ID 、用户组 ID 、更改链接数等,但它们并没有更改文件的实 际内容,也没有访问(读取)文件内容。为什么文件状态的更改指的是 inode 节点的更改呢? inode 中包含了很多文件信息,譬如:文件字节大 小、文件所有者、文件对应的读/ 写 / 执行权限、文件时间戳(时间属性)、文件数据存储的 block (块)等,所以由此可知,状态的更改指的就是 inode 节点内容的更改。譬如 chmod() 、 chown() 等 这些函数都能改变该时间属性。
七、符号链接(软链接)与硬链接
在 Linux 系统中有两种链接文件,分为软链接(也叫符号链接)文件和硬链接文件,软链接文件也就是前面给大家的 Linux 系统下的七种文件类型之一,其作用类似于 Windows 下的快捷方式。 那么硬链接文件又是什么呢?
首先,从使用角度来讲,两者没有任何区别,都与正常的文件访问方式一样,支持读、写以及执行。
Tips:使用 ln 命令可以为一个文件创建软链接文件或硬链接文件,用法如下:
硬链接:ln 源文件 链接文件
软链接:ln -s 源文件 链接文件
关于该命令其它用法,可以查看 man 手册。
硬链接:
使用 ln 命令创建的两个硬链接文件与源文件 test_file 都拥有相同的 inode 号,既然 inode 相同,也就意味着它们指向了物理硬盘的同一个区块,仅仅只是文件名字不同而已,创建出来的硬链接文件与源文件对文件系统来说是完全平等的关系。
如果删除了硬链接文件或源文
件其中之一,那文件所对应的 inode 以及文件内容在磁盘中的数据块会被文件系统回收吗?事实并不会这样,因为 inode 数据结结构中会记录文件的链接数,这个链接数指的就是硬链接数。
使用 ln 命令创建的两个硬链接文件与源文件 test_file 都拥有相同的 inode 号,既然 inode 相同,也就意味着它们指向了物理硬盘的同一个区块,仅仅只是文件名字不同而已,创建出来的硬链接文件与源文件对文件系统来说是完全平等的关系。同时源文件自身也是一个硬链接文件。
软链接:
软链接文件与源文件有着不同的 inode 号, 所以也就 是意味着它们之间有着不同的数据块,但是软链接文件的数据块中存储的是源文件的路径名,链接文件可以通过这个路径找到被链接的源文件,它们之间类似于一种“主从”关系,当源文件被删除之后,软链接文件依然存在,但此时它指向的是一个无效的文件路径,这种链接文件被称为悬空链接。
软硬链接的优劣
介绍完它们之间的区别之后,大家可能觉得硬链接相对于软链接来说有较大的优势,其实并不是这样, 对于硬链接来说,存在一些限制情况,如下:
⚫ 不能对目录创建硬链接(超级用户可以创建,但必须在底层文件系统支持的情况下)。
⚫ 硬链接通常要求链接文件和源文件位于同一文件系统中。
而软链接文件的使用并没有上述限制条件,优点如下所示:
⚫ 可以对目录创建软链接;
⚫ 可以跨越不同文件系统;
⚫ 可以对不存在的文件创建软链接。
创建链接文件
在 Linux 系统下,可以使用系统调用创建硬链接文件或软链接文件
创建硬链接 link()
创建软链接 symlink()
读取软链接文件本身
软链接文件数据块中存储的是被链接文件的路径信息,那如何读取出软链接文件中 存储的路径信息呢?大家认为使用 read 函数可以吗?答案是不可以,因为使用 read 函数之前,需要先 open 打开该文件得到文件描述符,但是调用 open 打开一个链接文件本身是不会成功的,因为打开的并不是链接 文件本身、而是其指向的文件,所以不能使用 read 来读取,那怎么办呢?可以使用系统调用 readlink 。
八、目录
但是目录作为一种特殊文件,并不适合使用前面介绍的文件 I/O 方式进行读写等操作。在 Linux 系统下,会有一些专门的系统调用或 C 库函数用于对文件夹进行操作,譬如:打开、创建文件夹、删除文件夹、读取文件夹以及遍历文件夹中的文件等。
目录存储形式
所以对此总结如下:
⚫ 普通文件由 inode 节点和数据块构成
⚫ 目录由 inode 节点和目录块构成
创建和删除目录
使用 open 函数可以创建一个普通文件,但不能用于创建目录文件,在 Linux 系统下,提供了专门用于 创建目录 mkdir()以及删除目录 rmdir 相关的系统调用。
打开、读取以及关闭目录
打开、读取、关闭一个普通文件可以使用 open() 、 read() 、 close() ,而对于目录来说,可以使用 opendir() 、 readdir()和 closedir() 来打开、读取以及关闭目录。