unlink与close关系

简介: close和unlink。以前时候总是不太理解两者的区别,最近看到一篇博客比较详细地描述了二者的本质区别,这里我引用了它的原文。         “每一个文件,都可以通过一个struct stat的结构体来获得文件信息,其中一个成员st_nlink代表文件的链接数。
close和unlink。以前时候总是不太理解两者的区别,最近看到一篇博客比较详细地描述了二者的本质区别,这里我引用了它的原文。
        “每一个文件,都可以通过一个struct stat的结构体来获得文件信息,其中一个成员st_nlink代表文件的链接数。当通过shell的touch命令或者在程序中open一个带有O_CREAT的不存在的文件时,文件的链接数为1。
        通常open一个已存在的文件不会影响文件的链接数。open的作用只是使 调用进程与文件之间建立一种访问关系,即open之后返回fd,调用进程可以通过fd来read 、write 、 ftruncate等等一系列对文件的操作。
        close()就是 消除这种调用进程与文件之间的访问关系。自然,不会影响文件的链接数。在调用close时,内核会检查打开该文件的进程数,如果此数为0,进一步检查文件的链接数,如果这个数也为0,那么就删除文件内容。
        link函数 创建一个新目录项,并且增加一个链接数
        unlink函 数删除目录项,并且减少一个链接数。如果 链接数达到0并且没有任何进程打开该文件,该文件内容才被真正删除。如果在unlilnk之前没有close,那么依旧可以访问文件内容。
         综上所诉,真正影响链接数的操作是link、unlink以及open的创建。
        删除文件内容的真正含义是文件的链接数为0,而这个操作的本质完成者是unlink。close能够实施删除文件内容的操作,必定是因为在close之前有一个unlink操作。”

UNIX文件系统包括引导块、超级块、i节点区、文件存储区、进程对换区等几部分。
引导块占用第0号物理块,不属于文件系统管辖,如果系统中有多个文件系统,只有根文件系统才有引导程序放在引导块中,其余文件系统都不使用引导块。
超级块占用第1号物理块,是文件系统的控制块,超级块包括:文件系统的大小、空闲块数目、空闲块索引表、空闲i节点数目、空闲i节点索引表、封锁标记等。超级块是文件系统为文件分配存储空间、回收存储空间的依据。
i节点区存放i节点,i节点是对文件进行控制和管理的一种数据结构。
文件存储区是存放文件内容的区域,文件存储区中各数据块的使用情况在超级中有记录,系统利用超级块中的记录完成对数据块的分配和回收。
unix文件系统中很重要的概念之一就是i节点,下面就开始说说这个重要的概念。
每一个文件都由自己的i节点,每个i节点都有唯一的i节点号。
i节点的结构如下(参考/usr/include/sys/ino.h)
struct dinode
{
ushort  di_mode;//文件类型与权限
short   di_nlink;
ushort di_uid;
ushort di_gid;
off_t  di_size;
char di_addr[40];
time_t di_atime;
time_t di_mtime;
time_t di_ctime;
}
从上面的结构中可以看出:
1、i节点保存了文件的属性和类型、存放文件内容的物理块地址、最近一次的存取时间、最后一次修改时间、创建此文件的时间。
2、i节点中没有记录文件名字,那么文件的名字是怎么关联到i节点的呢?这么设计又有什么好处呢?
     在linux系统中,文件的查找不是通过文件名称来查找的。实际上是通过i节点来实现文件的查找定位的。我们可以形象的将i节点看作是一个指针。当文件存储到磁盘上去的时候,文件肯定会存放到一个磁盘位置上,可以这样想象,既然文件数据是存放在磁盘上的,如果我们知道这个文件数据的地址,当我们想读写文件的时候,我们是不是直接使用这个地址去找到文件就可以了呢?
     是的,在linux下,i节点其实就是可以这么认为,把i节点看作是一个指向磁盘上该文件存储区的地址。只不过这个地址我们一般没办法直接使用,而是通过文件名来间接使用的。事实上,i节点不仅包含了文件数据存储区的地址,上面也提到过,它还包含了很多其他信息(文件的属性和类型,存放文件内容的物理块地址,最近一次的存取时间,最近一次的修改时间,创建文件的时间)。但是i节点是不保存文件名的。文件名保存在一个目录项中。每个目录项中都包含了文件名和i节点。回到正题,i节点中没有记录文件爱你名字,那么文件按名是怎么关联到i节点的呢。这里就关系到了硬链接与符号链接的区别。
如下图:
unix下i节点的理解
       对于硬链接来说,如果删掉源文件helloA.c,那么磁盘上数据文件按是不会被删除的。因为i节点上记录了该文件的硬链接数。只有硬链接数为0的时候,删除文件名的时候,该数据在磁盘上才会删除。
       所谓的符号链接,其实是指文件索引的索引。当源文件helloB.c删除之后,其实磁盘数据文件还在,helloC.c也无法使用。符号链接包含了一个文件名的路径,如果这个文件名被删除,这个符号链接自然就不能正常工作了。
3、di_mode这个是怎么保存文件类型+用户权限的呢?
比如:
drwxr-xr-x         7         root            sys              512      dec  15   2012   var
 -rw-r--r---         1         root            sys              4003     Ju1 4 23 : 37      1
红色字体部分说明是文件类型标记和文件权限,这个字符串跟ushort di_mode怎么关联的?
ushort di_mode是16位2进制数,保存的就是文件类型及用户权限信息,具体结构如下:
4                  8                    12                    16
第1-4位:文件类型
第5位:suid位
第6位:sgid位
第7位:sticky位
第8-10位:文件属主权限位
第11-13位:文件属组权限位
第14-16位:其他用户权限位
1)文件类型分类:
d--目录文件、f--普通文件、b--块设备文件、c--字符设备文件、l--链接文件
2)文件类型位算法
从系统的头文件/usr/include/sys/stat.h中可以知道:
a、#define   S_IFMT   0170000 -- 文件类型掩码宏,0170000以0开头,表示这是一个8进制数,转换成2进制,正好是 1 111 000 000 000 000 ,高4位全置1;
b、#define   S_IFREG 0100000 -- 普通文件类型掩码,0100000,转换成2进制,1 000 000 000 000 000,最高位置1;
c、#define S_ISREG(m)   (((m) & S_IFMT) == S_IFREG) --   判断文件是否普通文件的宏函数
举例说明:m值即为我们取到ushort di_mode,假设其2进制值为 0 011 000 000 000 000,对应的8进制为 060000   。S_ISREG(0060000) 即 (((0060000) & 0170000 ) == 0100000) ,该值返回False,则代表该文件不是普通文件。


说明:stat结构中的大多数信息都取自i节点。只有两项数据存放在目录项中:文件名和i节点编号。i节点编号的数据类型是ino_t。
注意:每个文件系统各自对它们的i节点进行编号,因此目录项中的i节点编号数指向同一文件系统中相应的i节点,不能使一个目录项指向另一个文件系统的i节点。
讨论linux中link,unlink,close,fclose函数对st_nlink的影响
     linux中每一个文件,都可以通过一个struct stat的结构体来获得文件信息,其中一个成员st_nlink代表文件的链接数。
       当通过shell的touch命令或者在程序中open一个带有O_CREAT的不存在的文件时,文件的链接数为1。
       通常open一个已存在的文件不会影响文件的链接数。open的作用只是使调用进程与文件之间建立一种访问关系,即open之后返回fd,调用进程可以通过fd来read 、write 、 ftruncate等等一系列对文件的操作。
       close()就是消除这种调用进程与文件之间的访问关系。自然,不会影响文件的链接数。在调用close时,内核会检查打开该文件的进程数,如果此数为0,进一步检查文件的链接数,如果这个数也为0,那么就删除文件内容。
       link函数创建一个新目录项,并且增加一个链接数。
       unlink函数删除目录项,并且减少一个链接数。如果链接数达到0并且没有任何进程打开该文件,该文件内容才被真正删除。如果在unlilnk之前没有close,那么依旧可以访问文件内容。
       综上所诉,真正影响链接数的操作是link、unlink以及open的创建。
       删除文件内容的真正含义是文件的链接数为0,而这个操作的本质完成者是unlink。close能够实施删除文件内容的操作,必定是因为在close之前有一个unlink操作。
       举个例子简单说明:通过shell   touch test.txt
  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <sys/stat.h>
  4 #include <sys/types.h>
  5 #include <fcntl.h>
  6
  7 int main(int argc,char *argv[])
  8 {
  9     struct stat buf;
 10     stat("test.txt",&buf);
 11     printf("1.link=%d\n",buf.st_nlink);
 12
 13 int fd;
 14 fd = open("test.txt",O_RDONLY);
 15 stat("test.txt",&buf);
 16 printf("2.link=%d\n",buf.st_nlink);
 17
 18     close(fd);
 19     stat("test.txt",&buf);
 20     printf("3.link=%d\n",buf.st_nlink);
 21 link("test.txt","test2.txt");
 22 stat("test.txt",&buf);
 23 printf("4.link=%d\n",buf.st_nlink);
 24     unlink("test2.txt");
 25     stat("test.txt",&buf);
 26     printf("5.link=%d\n",buf.st_nlink);
 27 fd = open("test.txt",O_RDONLY);
 28 stat("test.txt",&buf);
 29 printf("6.link=%d\n",buf.st_nlink);
 30     unlink("test.txt");
 31     fstat(fd,&buf);
 32     printf("7.link=%d\n",buf.st_nlink);
 33
 34     return 0;
 35
 36 }
顺次执行以上8个步骤,结果如下:
1.link=1
2.link=1    //open不影响链接数
3.link=1    //close不影响链接数
4.link=2    //link之后链接数加1
5.link=1    //unlink后链接数减1
6.link=1    //重新打开  链接数不变
7.link=0    //unlink之后再减1,此处我们改用fstat函数而非stat,因为unlilnk已经删除文件名,所以不可以通过   文件名访问,但是fd仍然是打开着的,文件内容还没有被真正删除,依旧可以使用fd获得文件信息。
执行步骤8,文件内容被删除....
注意:在第步骤6中,文件test.txt此时已经打开并没有将其关闭,而步骤七中直接将其释放,此时,文件的内容没有真正的被删除。进程任然可以继续读文件中的内容。直到关闭该文件或进程结束自动关闭后,内核首先会先检查打开文件的进程数,如果为0,然后内核检查其链接数,由于在第七步中已经释放了最后一个,所以其链接数为0,那么就删除该文件的内容。
下图是抄之apue的精典例子:
unix下i节点的理解

    unlink的这种性质经常被程序用来确保即使是在该程序崩溃时,它所创建的临时文件也不会被遗留下来。进程用open或creat创建一个文件,然后立即调用unlink。因为该文件仍旧是打开的,所以不会将其内容删除。只有当进程关闭该文件或终止时(这种情况下,内核会关闭该进程打开的全部文件),该文件的内容才会被删除。
    unlink删除该符号链接,而不是删除由该链接所引用的文件。给出符号链接名的情况下,没有一个函数能删除由该链接所引用的文件。

相关文章
|
29天前
|
网络协议 Linux API
Linux网络编程:shutdown() 与 close() 函数详解:剖析 shutdown()、close() 函数的实现原理、参数说明和使用技巧
Linux网络编程:shutdown() 与 close() 函数详解:剖析 shutdown()、close() 函数的实现原理、参数说明和使用技巧
60 0
|
10天前
|
Linux 开发者
Linux文件编程(open read write close函数)
通过这些函数,开发者可以在Linux环境下进行文件的读取、写入和管理。 买CN2云服务器,免备案服务器,高防服务器,就选蓝易云。百度搜索:蓝易云
82 4
|
22天前
|
算法 Unix Linux
select函数中的文件描述符(File Descriptor)范围
select函数中的文件描述符(File Descriptor)范围
16 0
select函数中的文件描述符(File Descriptor)范围
|
6月前
56 # 实现 pipe 方法进行拷贝
56 # 实现 pipe 方法进行拷贝
21 0
|
7月前
|
存储 C++
close()关闭文件方法
我们知道,调用 open() 方法打开文件,是文件流对象和文件之间建立关联的过程。那么,调用 close() 方法关闭已打开的文件,就可以理解为是切断文件流对象和文件之间的关联。注意,close() 方法的功能仅是切断文件流与文件之间的关联,该文件流并会被销毁,其后续还可用于关联其它的文件。 close() 方法的用法很简单,其语法格式如下: void close( ) 可以看到,该方法既不需要传递任何参数,也没有返回值。 举个例子: #include <fstream> using namespace std; int main() {
81 0
|
物联网 Linux 开发者
open_close 函数|学习笔记
快速学习 open_close 函数
106 0
|
网络协议 Java Linux
从linux源码看socket的close
从linux源码看socket的close 笔者一直觉得如果能知道从应用到框架再到操作系统的每一处代码,是一件Exciting的事情。上篇博客讲了socket的阻塞和非阻塞,这篇就开始谈一谈socket的close(以tcp为例且基于linux-2.
2102 0