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节点的呢。这里就关系到了硬链接与符号链接的区别。
如下图:

       对于硬链接来说,如果删掉源文件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的精典例子:

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

unlink与close关系的更多相关文章

  1. unlink和close关系

    今天看到nginx用文件锁实现互斥的实现方案时,发现,unlink文件后还可需用fd,很是纳闷!于是搜索到此文,并自测了下,涨姿势了~分享给大家~ 原理: 每一个文件,都可以通过一个struct st ...

  2. 《图解HTTP》阅读笔记

    HTTP基础的简单理解 在了解HTTP协议之前,我们先了解下TCP/IP的参考模型,TCP/IP参考模型分为四层:应用层.传输层.网络层.链路层(数据链路层). 应用层:为不同的网络应用提供所需的服务 ...

  3. HTTP请求方法对照表

    根据HTTP标准,HTTP请求可以使用多种请求方法. HTTP1.0定义了三种请求方法: GET, POST 和 HEAD方法. HTTP1.1新增了五种请求方法:OPTIONS, PUT, DELE ...

  4. HTTP笔记(一)

    最近在看<图解HTTP>.全书以图解的形式生动形象的讲解了HTTP协议.本文是根据该书整理的笔记,方便以后回顾. HTTP的诞生 HTTP又称超文本传输协议(HTTP,HyperText ...

  5. 【http】http的方法,状态码和组成部分

    Http(Hypertext Transfer Protocol) HTTP协议(HyperText Transfer Protocol,超文本传输协议)是用于从WWW服务器传输超文本到本地浏览器的传 ...

  6. ResfulApi规范

    序号 方法 描述 1 GET 请求指定的页面信息,并返回实体主体. 2 HEAD 类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头 3 POST 向指定资源提交数据进行处理请求(例如提 ...

  7. 【HTTP】267- HTTP 的15个常见知识点复习

    前言 自从入职新公司到现在,我们前端团队内部一直在做 ?每周一练 的知识复习计划,我之前整理了一个 [每周一练 之 数据结构与算法] (https://juejin.im/post/5ce2a20e6 ...

  8. 【HTTP】HTTP 的15个常见知识点复习

    前言 自从入职新公司到现在,我们前端团队内部一直在做 ?每周一练 的知识复习计划,我之前整理了一个 每周一练 之 数据结构与算法 学习内容,大家也快去看看~~ 最近三周,主要复习 网络基础 相关的知识 ...

  9. 图解 HTTP 笔记(二)——简单的 HTTP 协议

    本章主要以 HTTP 1.0 为例,讲解 HTTP 协议的基本结构. 在两台计算机之间使用 HTTP 协议进行通讯时,在一条通讯线路上必定有一端是客户端,另一端则是服务器端. 请求访问文本或图像等资源 ...

随机推荐

  1. OAuth2学习笔记

    参考:https://aaronparecki.com/oauth-2-simplified/ 1.角色定义 应用程序(客户) 需要获取用户的账号信息,获得相关权限. API服务器 资源服务器就是AP ...

  2. redis介绍(5)主从复制

    redis的主从复制: 主从复制介绍:redis的主从复制情况下,一个master节点下可以有多个slave节点,而且每个slave节点又可以有很多slave节点,形成很大的集群量级,我简单画个图,如 ...

  3. REST Framework组件的解析源码

    首先我们要知道解析器的作用 解析器就是对你请求体中的数据进行反序列化.封装 把你的所有的请求数据都封装在request.data中 以后就在request.data中获取数据 我们先导入rest_fr ...

  4. 给UIScrollView添加category实现UIScrollView的轮播效果

    给UIScrollView添加category实现UIScrollView的轮播效果 大家都知道,要给category添加属性是必须通过runtime来实现的,本教程中给UIScrollView添加c ...

  5. Apache源码安装--httpd-2.2.34

    一.下载源码包 二.将源码包移动/usr/src/目录 三.解压源码包,并进入目录:tar -xzvf httpd-2.2.34.tar.gz,cd httpd-2.2.34 四.安装依赖包:yum ...

  6. AT89S52汇编实现l通过按键中断切换led灯的四种闪烁模式(单灯左移,单灯右移,双灯左移,双灯右移)

    ;通过P1口控制8路LED的四种闪烁模式,单独LED灯左移,单独LED灯右移,相邻两个灯左移,相邻两个灯右移;通过一个外部中断0来检测按键的跳变沿来切换闪烁模式,第一次按键按下弹起,灯的闪烁状态由单独 ...

  7. November 23rd 2016 Week 48th Wednesday

    I always like walking in the rain, so no one can see me crying. 我一直喜欢在雨中行走,那样就没人能看到我的眼泪. I like walk ...

  8. Mac Item2 设置别名 永久生效

    使用 Item2 终端, 设置 别名的时候, 按照 网上的说法, 是 去 修改 用户目录下的   .bashrc   或者  .bash_profile 这两个文件都可以, 把 alias 写在 这两 ...

  9. 打印pdf

    #include "pdf_print_helper.h" pdf_print_helper::pdf_print_helper(){ } pdf_print_helper::~p ...

  10. Git学习文档——文件状态git status

    1.已经跟踪的文件有三种状态 已跟踪的文件,即被纳入版本控制的文件,又分为未修改(unmodified).已修改(modified).已暂存(staged)三种状态. 如图: 当在工作目录中新加入一个 ...