一、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()
来打开、读取以及关闭目录。