暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PostgreSQL VFD机制

yanzongshuaiDBA 2019-11-03
991

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

VFDfree链表,实际上是数组的下标。

lruMoreRecently

VFD的最近最少使用链表,为双向。实际上也是数组的下标

lruLessRecently

lruLessRecently为正向,每次插入都插入头部

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时的流程

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

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

    3open文件,并将该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文件前都会调用AllocateVfdVfdCache中获取一个空闲的vfd

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

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

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

        AllocateVfd->
        if (VfdCache[0].nextFree == 0){
        SizenewCacheSize = 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;
        复制

        ReleaseLruFiles

        1nfileopen打开的文件数,numAllocatedDescsfopen打开的文件数,max_safe_fds为操作系统计算得出的值。

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

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

          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->lruLessRecently].lruMoreRecently = vfdP->lruMoreRecently;
            VfdCache[vfdP->lruMoreRecently].lruLessRecently = vfdP->lruLessRecently;
            复制

            3、Insert

              Insert->
              vfdP = &VfdCache[file];
              vfdP->lruMoreRecently = 0;
              vfdP->lruLessRecently = VfdCache[0].lruLessRecently;
              VfdCache[0].lruLessRecently = file;
                  VfdCache[vfdP->lruLessRecently].lruMoreRecently = file;
              复制

              LRU链表的形式如下:

              Insert一个VFD时:


              4、Delete

                Delete(file);-->
                vfdP = &VfdCache[file];
                VfdCache[vfdP->lruLessRecently].lruMoreRecently = vfdP->lruMoreRecently;
                VfdCache[vfdP->lruMoreRecently].lruLessRecently = vfdP->lruLessRecently;
                复制

                例如删除VfdCache[1]

                5、回收VFD

                1)每次调用FileClose时,会回收vfdfree链表。

                2)先调用close函数

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

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

                5)调用FreeVfdvfd回收到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;
                    复制


                    文章转载自yanzongshuaiDBA,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                    评论