Linux环境编程之文件I/O(八):文件链接
引言:
执行如下命令时,
ls /usr/local/lib/libfreetype.so -l显示内容:
lrwxrwxrwx 1 root root 20 2014-04-06 22:57 /usr/local/lib/libfreetype.so -> libfreetype.so.6.9.0
表明这是一个链接文件,通过链接可以实现对一个文件从不同路径对它进行引用。对于文件的链接有硬链接和软连接(即符号链接)之分。shell中的ln命令可以创建一个文件的硬链接,加上-s选项可以创建一个文件的软链接。要想深入的理解文件链接的概念,必须先明白文件系统的基本结构以及i节点和指向i节点的目录项之间的区别。因此,先要对文件系统的基本结构做下了解,然后分别深入了解文件的硬链接与软连接。最后,了解一下文件的修改时间。
(一):文件系统的基本结构
每个磁盘可以分成一个或多个分区,每个分区可以包含一个文件系统。文件系统的基本结构如下图:
其中i结点是固定长度的记录项,它包含有关文件的大部分信息。
比较详细的柱面组的i结点和数据块部分如下图:
首先,目录、目录项、索引节点的关系是:
一个目录文件包含了一组目录项,目录项放在数据块中。一个目录项主要包括文件名和索引节点号,索引节点号是指向索引节点表中对应的索引节点的。也可这样理解目录项:因为目录可以包含子目录,目录是可以层层嵌套的,所以形成文件路径,而文件路径中的每一部分就是所谓的目录项。索引节点是文件系统处理文件所需要的所有信息都存放在称为索引节点的数据结构中。主要就是文件的属性,包括链接数、文件所有者、文件建立和修改时间,文件在磁盘的位置,文件大小,使用权限等。
对于上面的两图,应注意以下几点:
1、在图中有两个目录项指向同一i节点。每个i节点中都有一个链接计数,其值是指向该i节点的目录项数。只有当链接计数减小至0时,才可删除该文件(即释放该文件占用的磁盘块)。这就是为什么“解除一个文件的链接”操作并不总是意味着“释放该文件占用的磁盘块”的原因。这也是为什么删除一个目录项的函数被称为unlink而不是delete的原因。在stat结构中,链接计数包含在st_nlik成员中,这种链接类型称为硬链接。
2、另外一种链接类型称为符号链接。对于这种链接,该文件的实际内容(在数据块中)包含了该符号链接所指向的文件的名字。
3、i节点包含了大多数与文件有关的信息:文件类型、文件访问权限位、文件长度和指向该文件所占用的数据块的指针等等。stat结构中的大多数信息都取自i节点。只有两项数据存放在目录项中:文件名和i节点编号。
4、每个文件系统各自对它们的i节点进行编号,因此目录项中的i节点编号数指向同一文件系统中的相应i节点,不能使一个目录项指向另一个文件系统的i节点。这就是为什么ln命令不能跨越文件系统的原因。
5、当在不更换文件系统情况下为一个文件更名时,该文件的实际内容并未移动,只需要构造一个指向现有i节点的新目录项,并解除与旧目录项的链接。
(二)link、unlink、remove、rename函数
上面的函数都是针对硬链接进行操作的。关于硬链接,应该注意一下几点:
1、建立硬链接时,一定要在同一文件系统内执行。
2、对于目录,通常的文件系统实现不允许建立硬链接,即使能够建立,也仅限于超级用户才能这样做。
3、删除目录项时pathname时,会使的pathname所引用文件的链接计数减1。如果还有指向该文件的其他链接,则仍可通过其他链接访问该文件的数据。
4、为了解除对文件的链接,必须对包含该目录项的目录具有写权限和执行权限。
5、只有当链接计数达到0时,该文件的内容才可能被删除。另一个阻止删除文件的条件是有进程打开该文件,其内容也不能被删除。关闭一个文件时,内核首先查看打开该文件的进程数,如果该数达到0,然后内核检查其链接数,如果这个数也是0,那么就删除该文件的内容。
#include <unistd.h>
int link(const char *oldpath, const char *newpath); // 若成功则返回0, 若出错则返回-1。
此函数创建一个新目录项newpath,它引用现有的文件existingpath。如果newpath已经存在,则返回出错。只创建newpath中的最后一个分量,路径中的其他部分应当已经存在。
#include <unistd.h>
int unlink(const char *pathname); // 过成功则返回0,若出错则返回-1
此函数删除目录项,并将由pathname所引用文件的链接数减1.如果还有指向该文件的其他链接,则仍可通过其他链接访问该文件的数据。如果出错,则不对该文件做任何更改。
(三)
符号链接时指向一个文件的间接指针,它与上面所说的硬链接有所不同,硬链接直接指向文件的i节点。引入符号链接的原因是为了避开硬链接的一些限制:
1、硬链接通常要求链接和文件位于同一文件系统中。
2、只有超级用户才能创建指向目录的硬链接。
对符号链接以及它所指向何种对象并无任何文件系统限制,任何用户都可创建指向目录的符号链接。符号链接一般用于将一个文件或整个目录结构移动到系统的另一个位置。
#include <unistd.h>
int symlink(const char *oldpath, const char *newpath);
#include <unistd.h>
ssize_t readlink(const char *path, char *buf, size_t bufsiz);
(四)
对每个文件都有3个时间字段。如下表:
字段 | 说明 | 例子 | ls选项 |
st_atime st_mtime st_ctime |
文件数据的最后访问时间 文件数据的最后修改时间 i节点状态的最后更改时间 |
read write chmod/chown |
-u 默认 -c |
#include <sys/types.h>
#include <utime.h>
int utime(const char *filename, const struct utimbuf *times);
struct utimebuf{
time_t actime;
time_t modtime;
}
此函数的操作以及执行它所需要的特权取决于time参数是否为NULL
1、如果times是一个空指针,则访问时间和修改时间两者都设置为当前时间。为了执行此操作必须满足下列两条件之一:进程的有效用户ID必须等于该文件的所有者ID;或进程对该文件必须具有写权限。
2、如果times是非空指针,则访问时间和修改时间被设置为times所指向结构中的值。此时进程的有效用户ID必须等于该文件的所有者ID,或者进程必须是一个超级用户进程。对文件只具有写权限是不够的。