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) < ) {
printf("open error: %s\n", strerror(errno));
exit(-);
}
if (unlink("tempfile") < ) {
printf("unlink error: %s\n", strerror(errno));
exit(-);
}
printf("file unlinked\n");
sleep();
printf("done\n");
exit();
}

编译该程序,生成文件 unlinkdemo,然后执行该文件,

 lienhua34:demo$ ls -l tempfile
-rw-r--r-- lienhua34 lienhua34 9月 : tempfile
lienhua34:demo$ df /home
文件系统 1K-块 已用 可用 已用% 挂载点
/dev/sda1 % /
lienhua34:demo$ ./unlinkdemo &
[]
lienhua34:demo$ file unlinked
ls -l tempfile
ls: 无法访问tempfile: 没有那个文件或目录
lienhua34:demo$ df /home
文件系统 1K-块 已用 可用 已用% 挂载点
/dev/sda1 % /
lienhua34:demo$ done
df /home
文件系统 1K-块 已用 可用 已用% 挂载点
/dev/sda1 % /

通过上面的执行结果,我们可以看出,只有等到执行 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") < ) {
printf("symlink error: %s\n", strerror(errno));
exit(-);
}
printf("create symbol link \"barlink\".\n");
if ((buflen = readlink("barlink", buf, BUFFER_LEN)) < ) {
printf("readlink error: %s\n", strerror(errno));
exit(-);
}
printf("barlink context: %s\n", buf);
exit();
}

编译该程序,生成 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-- lienhua34 lienhua34
9月 : bar
lrwxrwxrwx lienhua34 lienhua34 9月 : barlink -> bar
lienhua34:demo$ cat bar
in bar

(done)

UNIX环境编程学习笔记(10)——文件I/O之硬链接和符号链接的更多相关文章

  1. UNIX环境编程学习笔记(13)——文件I/O之标准I/O流

    lienhua342014-09-29 1 标准 I/O 流 之前学习的都是不带缓冲的 I/O 操作函数,直接针对文件描述符的,每调用一次函数可能都会触发一次系统调用,单次调用可能比较快捷.但是,对于 ...

  2. UNIX环境编程学习笔记(4)——文件I/O之dup复制文件描述符

    lienhua342014-08-23 UNIX 提供了两个函数 dup 和 dup2 用于复制一个现存的文件描述符. #include <unistd.h> int dup(int fi ...

  3. UNIX环境编程学习笔记(2)——文件I/O之不带缓冲的 I/O

    lienhua342014-08-25 1 文件描述符 对于内核而言,所有打开的文件都通过文件描述符引用.文件描述符是一个非负整数.当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符. ...

  4. UNIX环境编程学习笔记(12)——文件I/O之目录操作

    lienhua342014-09-18 1 引言 在 UNIX 系统中,目录是一种特殊的文件类型.我们可以使用 open 函数来打开目录,获取文件描述符,然后调用 stat 函数来获取目录的属性信息, ...

  5. UNIX环境编程学习笔记(9)——文件I/O之文件访问权限的屏蔽和更改

    lienhua342014-09-10 1 文件访问权限 在文件访问权限和进程访问控制中,我们已经讲述过文件访问权限位,为了方便,我们重新列在下面, 表 1: 文件的 9 个访问权限位  st_mod ...

  6. UNIX环境编程学习笔记(7)——文件I/O之文件访问权限与进程访问控制

    lienhua342014-09-02 1 文件的设置用户 ID位 和设置组 ID位 与进程相关联的 ID 如下表所示, 表 1: 与进程相关联的用户 ID 和组 ID 实际用户 ID 我们实际上是谁 ...

  7. UNIX环境编程学习笔记(6)——文件I/O之判断文件类型

    lienhua342014-09-01 1 文件类型 我们平时最常接触的文件类型有普通文件(regular file)和目录(di-rectory file),但是 UNIX 系统提供了多种文件类型: ...

  8. UNIX环境编程学习笔记(3)——文件I/O之内核 I/O 数据结构

    lienhua342014-08-27 内核使用三种数据结构表示打开的文件,分别是文件描述符表.文件表和 V 节点表. (1) 每个进程在进程表中都有一个记录项,记录项中包含有一张打开文件描述符表,每 ...

  9. UNIX环境编程学习笔记(14)——文件I/O之临时文件

    lienhua342014-10-01 ISO C 标准 I/O 库提供了个两个函数 tmpnam 和 tmpfile 以帮助创建临时文件, #include <stdio.h> char ...

随机推荐

  1. JS实现多行文本最后是省略号紧随其后还有个超链接在同一行的需求

    1.布局及样式如下图: 2.js获得上图的div对象,然后判断div对象的高度,如果大于一行的高度了表示内容有两行了,再获得span标签里面的内容并用正则将后六个字符替换成“......”这里的实现代 ...

  2. Python os.popen() 方法

    简述 就是新建一个管道执行一个命令. 方法是os.popen(命令,权限,缓冲大小) 比如 a = 'mkdir def' b = os.popen(a,) print b 就是等同于使用命令去创建了 ...

  3. python3 scrapy 安装记录

    一开始,想通过pycharm 直接安装,没想到出现下面的错误: Microsoft Visual C++ 14.0 is required... 的错误 网上有方法,是通过安装 VC++14.0 ,但 ...

  4. A-Frame不如x3dom

    昨天才发现还有A-Frame这么个框架,是Mozilla(moz://a 这个logo挺有创意的)出的,但看了一下其实现方式和语法后,感觉与x3d/x3dom还是有差距的. 二者的实现方式都是基于We ...

  5. java你可能不知道的事(2)--堆和栈<转>

    在java语言的学习和使用当中你可能已经了解或者知道堆和栈,但是你可能没有完全的理解它们.今天我们就一起来学习堆.栈的特点以及它们的区别.认识了这个之后,你可能对java有更深的理解. Java堆内存 ...

  6. 编译安装Mysql与管理(十四)

    [教程主题]:编译安装Mysql与管理 [课程录制]: 创E [主要内容] [1]什么是Mysql MyQL是一个开放源码的小型关系型数据库管理系统,开发者为瑞典MySQL AB公司.目前MySQL被 ...

  7. iOS第三方支付集成

    支付宝(alipay)和微信支付(Wechat Pay) 支付宝: 一.总体流程 (1)先与支付宝签约.获得商户ID(partner)和账号ID(seller)(注冊app⽤用) (2)下载对应的公钥 ...

  8. jquery.nestable.min.js可拖动标签

    主容器为一个叫div的classname为dd的东西. 只要对这个主容器进行捕获加上一个方法 nestable就可以了. 可以给的属性为​​ serialize   直接以字符串的形式给入,意思就是按 ...

  9. mysql导入sql脚本

    例如:我的用户名是root 密码是123 sql脚本存在C盘 名字为test.sql 数据库为test 有两种方法可以执行脚本 1:打开CMD输入以下命令(不需要转换目录)>mysql -u r ...

  10. Java设计模式(11)外观模式(Facade模式)

    外观模式(Facade)的定义:为子系统中的一组接口提供一个一致的界面. Facade一个典型应用就是数据库JDBC的应用,如下例对数据库的操作: public class DBCompare { C ...