lienhua34
2014-09-15
1 文件系统数据结构
UNIX 文件系统通过 i 节点来存储文件的信息。如图 1 所示为一个磁盘柱面上的 i 节点和数据块示意图。其中 i 节点是一个固定长度的记录项,它包含了有关文件的大部分信息。数据块用于存储文件的实际内容。每个文件的 i 节点会记录该文件的内容所占用的数据块信息。
图 1: i 节点和数据块
图 1 中还有一些信息需要进行说明:
1. 每个目录项只存储了文件的文件名和 i 节点编号(每个文件系统各自对它们的 i 节点进行编号)。文件的其它信息则记录在 i 节点中,例如,文件类型、文件访问权限位、文件长度等。
2. 每个 i 节点都有一个链接计数,其值是指向该 i 节点的目录项数。只有当链接计数减少至 0 时,才可删除该文件(即释放该文件所占用的数据块)。通过 i 节点链接使多个目录项指向同一个文件的这种链接类型称为硬链接。
2 硬链接和符号链接
上一节讲到了,硬链接是通过 i 节点编号来使多个目录项指向同一个文件。因此,硬链接存在一些限制,
• 硬链接要求链接和文件位于同一个文件系统中(因为每个文件系统具有各自的 i 节点编号)。
• 只有超级用户才能创建指向目录的硬链接(避免在文件系统中存在循环)。
对于符号链接,该文件的实际内容(在数据块中)包含了该符号链接所指向的文件的名字。对符号链接以及它所指向何种对象并无任何文件系统限制,任何用户都可以创建指向目录的符号链接。符号链接一般用于将一个文件或整个目录结构移到文件系统中的另一个位置。
3 创建链接 link 函数、解除链接 unlink 函数
link 函数用于创建一个现有文件的硬链接。
#include <unistd.h>
int link(cosnt char *existingpath, const char *newpath);
返回值:若成功则返回0,若出错则返回-1。
此函数创建一个新目录项 newpath,它引用现有的文件 existingpath。如果 newpath 已经存在,则返回出错。该函数只创建 newpath 中的最后一个分量,路径中其他部分应当已经存在。
unlink 函数删除一个现有的目录项。
#include <unistd.h>
int unlink(const char *pathname);
返回值:若成功则返回0,若出错则返回-1.
此函数删除目录项,比将由 pathname 所引用的文件的链接计数减 1。如果出错,则不对该文件做任何修改。只有当链接计数达到 0 时,该文件的内容才会被删除。另一个阻止删除文件内容的条件是:有进程打开着该文件。关闭一个文件时,内核首先检查打开该文件的进程数。如果该数达到 0,然后内核检查其链接计数,如果链接计数也是 0,那么就删除该文件的内容。
如果 pathname 是符号链接,那么 unlink 删除该符号链接,而不会删除该符号链接所引用的文件。
例子:
下面程序先使用 open 打开文件 tempfile,然后 unlink 删除目录项,接着进程进入 15 秒钟睡眠时间。
#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <fcntl.h> int main(void) { if (open("tempfile", O_RDWR) < 0) { printf("open error: %s ", strerror(errno)); exit(-1); } if (unlink("tempfile") < 0) { printf("unlink error: %s ", strerror(errno)); exit(-1); } printf("file unlinked "); sleep(15); printf("done "); exit(0); }
编译该程序,生成文件 unlinkdemo,然后执行该文件,
1 lienhua34:demo$ ls -l tempfile 2 -rw-r--r-- 1 lienhua34 lienhua34 20094393 9月 15 21:06 tempfile 3 lienhua34:demo$ df /home 4 文件系统 1K-块 已用 可用 已用% 挂载点 5 /dev/sda1 305603832 14811828 275268216 6% / 6 lienhua34:demo$ ./unlinkdemo & 7 [1] 3113 8 lienhua34:demo$ file unlinked 9 ls -l tempfile 10 ls: 无法访问tempfile: 没有那个文件或目录 11 lienhua34:demo$ df /home 12 文件系统 1K-块 已用 可用 已用% 挂载点 13 /dev/sda1 305603832 14811828 275268216 6% / 14 lienhua34:demo$ done 15 df /home 16 文件系统 1K-块 已用 可用 已用% 挂载点 17 /dev/sda1 305603832 14792140 275287904 6% /
通过上面的执行结果,我们可以看出,只有等到执行 unlinkdemo 文件的进程结束之后(第14行),文件 tempfile 的内容才被真正删除。unlink 的这种性质经常被程序用来确保即使是在该程序崩溃时,它所创建的临时文件也不会遗留下来。进程用 open 或 creat 创建一个文件,然后立即调用 unlink。因为该文件被进程打开着,所以不会将其内容删除。只有当进程关闭该文件或终止时(在这种情况下,内核关闭该进程打开的全部文件),该文件的内容才会被删除。
4 符号链接的 symlink 和 readlink 函数
symlink 函数创建一个符号链接。
#include <unistd.h>
int symlink(const char *actualpath, const char *sympath);
返回值:若成功则返回0,若出错则返回-1。
该函数创建了一个指向 actualpath 的新目录项 sympath,在创建此符号链接时,并不要求 actualpath 已经存在。并且,actualpath 和 sympath并不需要位于同一文件系统中。
readlink 函数打开符号链接本身,并读取该链接中的内容(不是该链接所引用的文件的内容)。
#include <unistd.h>
ssize_t readlink(const char *restrict pathname, char *restrict buf, size_t bufsize);
返回值:若成功则返回读到的字节数,若出错则返回-1。
如果此函数成功执行,则返回读入 buf 的字节数。在 buf 中返回的符号链接的内容不以 null 字符终止。
例子:
下面程序创建文件 bar 的符号链接 barlink,然后读入符号链接 barlink的内容并进行打印。
#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include <string.h> #define BUFFER_LEN 2014 int main(void) { char buf[BUFFER_LEN]; ssize_t buflen; if (symlink("bar", "barlink") < 0) { printf("symlink error: %s ", strerror(errno)); exit(-1); } printf("create symbol link "barlink". "); if ((buflen = readlink("barlink", buf, BUFFER_LEN)) < 0) { printf("readlink error: %s ", strerror(errno)); exit(-1); } printf("barlink context: %s ", buf); exit(0); }
编译该程序,生成 symlinkdemo,然后运行该文件,
lienhua34:demo$ gcc -o symlinkdemo symlinkdemo.c lienhua34:demo$ ./symlinkdemo create symbol link "barlink". barlink context: bar lienhua34:demo$ ls -l bar barlink -rw-r--r-- 1 lienhua34 lienhua34 7 9月 15 20:02 bar lrwxrwxrwx 1 lienhua34 lienhua34 3 9月 15 20:07 barlink -> bar lienhua34:demo$ cat bar in bar
(done)