>>与软件开发有关的知识:操作系统,数据库,网络通信等 书籍支持  视频课程  卫琴专栏  在线测试  资源下载  联系我们
发表一个新主题 开启一个新投票 回复文章 您是本文章第 12205 个阅读者 刷新本主题
 * 贴子主题:  PostgreSQL VFD机制 回复文章 点赞(0)  收藏  
作者:javathinker    发表时间:2020-03-11 19:26:24     消息  查看  搜索  好友  复制  引用

  1、结构体

  VFD 机制中由结构体 struct vfd 来维护。其中各个成员变量的意义如下表所示:
   fd   vfd 实际对应的物理文件文件描述符
   fdstate   FD_DELETE_AT_CLOSE :表示文件在关闭时需删除

  FD_TEMP_FILE_LIMIT :标记临时文件

  FD_CLOSE_AT_EOXACT :

  这几个都针对临时文件
   resowner   owner, for automatic cleanup
   nextFree   VFD 的 free 链表,实际上是数组的下标。
   lruMoreRecently   VFD 的最近最少使用链表,为双向。实际上也是数组的下标
   lruLe***ecently   lruLe***ecently 为正向,每次插入都插入头部
   fileSize   文件大小
   fileName   文件名
   fileFlags   打开文件时的标签,比如 O_CREATE 等
   fileMode   打开文件时的属性,比如读写权限等

  2、初始化

   启动时初始化,使用malloc ,只在本进程中有效,即每个进程都维护各自的 VfdCache 而并非共享内存。初始化时只申请第一个数组,并将其 fd 置为 VFD_CLOSED 。

PostgresMain->BaseInit->InitFileAccess:
    VfdCache = (Vfd *) malloc(sizeof(Vfd));
    MemSet((char *) &(VfdCache[0]), 0, sizeof(Vfd));
    VfdCache->fd = VFD_CLOSED;
    SizeVfdCache = 1;

  2、open时的流程

  1 ) Open 时首先会调用 AllocateVfd ,从 VfdCache 数组中找一个空闲的 slot ,然后返回。该函数流程见 AllocateVfd 调用。

  2 )然后会调用 ReleaseLruFiles 判断是否 open 了最大限制的 fd 。如超出限制,则将 LRU 链表最后一个 VFD 的 fd close 掉。

  3 ) open 文件,并将该 VFD 插入到 LRU 链表。插入 LRU 的函数 Insert 详细流程看下面的函数分析。

  4 )然后对 vfdP 成员变量进行赋值。

PathNameOpenFilePerm->
    file = AllocateVfd();
    vfdP = &VfdCache[file];
    ReleaseLruFiles();
    vfdP->fd = BasicOpenFilePerm(fileName, fileFlags, fileMode);
    Insert(file);
    vfdP->fileName = fnamecopy;
    /* Saved flags are adjusted to be OK for re-opening file */
    vfdP->fileFlags = fileFlags & ~(O_CREAT | O_TRUNC | O_EXCL);
    vfdP->fileMode = fileMode;
    vfdP->fileSize = 0;
    vfdP->fdstate = 0x0;
    vfdP->resowner = NULL;

  AllocateVfd

  1 )每次调用 BasicOpenFilePerm open 文件前都会调用 AllocateVfd 从 VfdCache 中获取一个空闲的 vfd 。

  2 )首先会判断 free 链表中是否为空。初始时刻, SizeVfdCache 为 1 ,则会将 VfdCache 初始化成大小 32 的数组,并将其通过 nextFree 串联起来形成 free 链表,注意该 free 链表为循环。

  3 ) VfdCache[0] 不使用。最开始 32 个的时候,即第一次扩充后 free  链表如下图所示,跳过 VfdCache[1] , 1 会返回。也就是说每次取 VFD 都是  VfdCache[0].nextFree

  4 )后续再次扩充时,都是翻倍进行扩充

AllocateVfd->
    if (VfdCache[0].nextFree == 0){
        Size  newCacheSize = SizeVfdCache * 2;
        if (newCacheSize < 32)
            newCacheSize = 32;
        newVfdCache = (Vfd *) realloc(VfdCache, sizeof(Vfd) * newCacheSize);
        VfdCache = newVfdCache;
        for (i = SizeVfdCache; i < newCacheSize; i++){
            MemSet((char *) &(VfdCache[i]), 0, sizeof(Vfd));
            VfdCache[i].nextFree = i + 1;
            VfdCache[i].fd = VFD_CLOSED;
        }
        VfdCache[newCacheSize - 1].nextFree = 0;
        VfdCache[0].nextFree = SizeVfdCache;
        SizeVfdCache = newCacheSize;
    }
    file = VfdCache[0].nextFree;
    VfdCache[0].nextFree = VfdCache[file].nextFree;
    return file;
点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

  ReleaseLruFiles

  1 ) nfile 为 open 打开的文件数, numAllocatedDescs 为 fopen 打开的文件数, max_safe_fds 为操作系统计算得出的值。

  2 )一旦超出 max_safe_fds 值,就会调用 ReleaseLruFile 从 LRU 链表删除一个,注意删除的是 VfdCache[0].lruMoreRecently ,即链表的尾部,最近最少使用的。

  3 )  首先将该fd 关闭  ,然后将之置为VFD_CLOSED 。调用 Delete 函数将 VFD 从 LRU 链表删除。注意这里只是从 LRU 链表删除,不会释放回收到 free 链表,也不会修改 vfd 数据结构的其他成员变量值。因为后续可能还会用到该物理文件,会重新 open 并将之重新 insert 到 LRU 链表。

ReleaseLruFiles->
    while (nfile + numAllocatedDescs >= max_safe_fds){
        if (!ReleaseLruFile())
           break;
    }
ReleaseLruFile->
    LruDelete(VfdCache[0].lruMoreRecently);->
        vfdP = &VfdCache[file];
        close(vfdP->fd);
        vfdP->fd = VFD_CLOSED;
        --nfile;
        Delete(file);-->
            vfdP = &VfdCache[file];
            VfdCache[vfdP->lruLe***ecently].lruMoreRecently = vfdP->lruMoreRecently;
            VfdCache[vfdP->lruMoreRecently].lruLe***ecently = vfdP->lruLe***ecently;

  3、Insert

Insert->
    vfdP = &VfdCache[file];
    vfdP->lruMoreRecently = 0;
    vfdP->lruLe***ecently = VfdCache[0].lruLe***ecently;
    VfdCache[0].lruLe***ecently = file;
    VfdCache[vfdP->lruLe***ecently].lruMoreRecently = file;
  

  LRU 链表的形式如下:

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

  Insert 一个 VFD 时:

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

  4、Delete

Delete(file);-->
    vfdP = &VfdCache[file];
    VfdCache[vfdP->lruLe***ecently].lruMoreRecently = vfdP->lruMoreRecently;
    VfdCache[vfdP->lruMoreRecently].lruLe***ecently = vfdP->lruLe***ecently;
   例如删除VfdCache[1] :

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

  5、回收VFD

  1 )每次调用 FileClose 时,会回收 vfd 到 free 链表。

  2 )先调用 close 函数

  3 )然后将之从 LRU 链表删除

  4 )如果是临时文件,还会将临时文件删除

  5 )调用 FreeVfd 将 vfd 回收到 free 链表

FileClose->
    close(vfdP->fd);
    --nfile;
    vfdP->fd = VFD_CLOSED;
    Delete(file);
    ...
    FreeVfd(file);
  

  FreeVfd

   调用函数FreeVfd 回收,注意几个成员变量的修改。回收时,将之插入到 free 链表头部。注意每次取时也从头部取

FreeVfd->
    free(vfdP->fileName);//注意fileName需要释放,他是另malloc的
    vfdP->fileName = NULL;
    vfdP->fdstate = 0x0;
    vfdP->nextFree = VfdCache[0].nextFree;
    VfdCache[0].nextFree = file;

        

----------------------------
原文链接:https://blog.51cto.com/yanzongshuai/2447763

程序猿的技术大观园:www.javathinker.net



[这个贴子最后由 flybird 在 2020-03-13 11:45:49 重新编辑]
  Java面向对象编程-->Java常用类(上)
  JavaWeb开发-->JSP技术详解(Ⅱ)
  JSP与Hibernate开发-->第一个helloapp应用
  Java网络编程-->创建非阻塞的HTTP服务器
  精通Spring-->Web应用的国际化
  Vue3开发-->绑定表单
  OSI七层模型与层上协议
  数据库索引创建与优化
  一款SQL自动检查神器,再也不用担心SQL出错了,自动补全、回...
  神奇的 SQL 之 联表细节 → MySQL JOIN 的执行过程
  Zabbix中文使用手册
  MySQL的所有函数的用法
  MySQL 复制表
  SQL AVG() 函数的用法
  SQL ISNULL()、NVL()、IFNULL() 和 COALESCE() 函数
  SQL Server 和 MySQL 中的 Date 函数
  SQL PRIMARY KEY 约束
  存储与虚拟主机管理
  电脑启动不起来,提示Reboot and select proper boot device ...
  我心中的MySQL DBA
  Mysql支持的数据类型(总结)
  更多...
 IPIP: 已设置保密
楼主      
1页 0条记录 当前第1
发表一个新主题 开启一个新投票 回复文章


中文版权所有: JavaThinker技术网站 Copyright 2016-2026 沪ICP备16029593号
荟萃Java程序员智慧的结晶,分享交流Java前沿技术。  联系我们
如有技术文章涉及侵权,请与本站管理员联系。