>>与软件开发有关的知识:操作系统,数据库,网络通信等 书籍支持  卫琴直播  品书摘要  在线测试  资源下载  联系我们
发表一个新主题 开启一个新投票 回复文章 您是本文章第 19905 个阅读者 刷新本主题
 * 贴子主题:  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面向对象编程-->输入与输出(下)
  JavaWeb开发-->Web运作原理(Ⅰ)
  JSP与Hibernate开发-->Java应用分层架构及软件模型
  Java网络编程-->通过JDBC API访问数据库
  精通Spring-->Vue组件开发高级技术
  Vue3开发-->Vue Router路由管理器
  针对 MySQL IO 特点进行的存储优化揭秘
  MySQL千万级别大表,盘点优化技巧
  我必须得告诉大家的MySQL优化原理
  一款SQL自动检查神器,再也不用担心SQL出错了,自动补全、回...
  Windows下安装MySQL8 的步骤
  MySQL 运算符的用法
  MySQL 连接
  SQL 撤销索引、表以及数据库
  SQL CREATE DATABASE 创建数据库语句
  SQL SELECT INTO 语句
  SQL FULL OUTER JOIN 关键字
  XML的DTD定义
  DTD中声明元素
  从十年运维看“云”维趋势
  存储与虚拟主机管理
  更多...
 IPIP: 已设置保密
楼主      
1页 0条记录 当前第1
发表一个新主题 开启一个新投票 回复文章


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