虚拟文件描述符VFD

瀚高数据库
目录
环境
文档用途
详细信息

环境
系统平台:Linux x86-64 Red Hat Enterprise Linux 7
版本:14
文档用途
了解VFD

详细信息

1.相关数据类型

typedef struct vfd

{

    int            fd;                /* current FD, or VFD_CLOSED if none */ OS文件描述符

    unsigned short fdstate;        /* bitflags for VFD's state */ vfd状态

    ResourceOwner resowner;        /* owner, for automatic cleanup */ 拥有者,自动清理用

    File        nextFree;        /* link to next free VFD, if in freelist */  File为int类型,表示下标

    File        lruMoreRecently;    /* doubly linked recency-of-use list */

    File        lruLessRecently;

    off_t        fileSize;        /* current size of file (0 if not temporary) */  

    char       *fileName;        /* name of file, or NULL for unused VFD */

    /* NB: fileName is malloc'd, and must be free'd when closing the VFD */

    int            fileFlags;        /* open(2) flags for (re)opening the file */ 

    mode_t        fileMode;        /* mode to pass to open(2) */  读、 写、 执行等flag

} Vfd;

2. VfdCache初始化

注意:VfdCache[0]不是一个有效的vfd,仅仅是为了减少链表增加、删除时的判断而引入的头结点。

Assert(SizeVfdCache == 0);    /* call me only once */

/* initialize cache header entry */

VfdCache = (Vfd *) malloc(sizeof(Vfd));

MemSet((char *) &(VfdCache[0]), 0, sizeof(Vfd));

VfdCache->fd = VFD_CLOSED; // 全局的VfdCache指向新建的表头节点

SizeVfdCache = 1;  // 记录vfd的数量

初始化完成之后,VfdCache指向内存空间的某一块:

在这里插入图片描述

3.AllocateVfd

Assert(SizeVfdCache > 0);    /* InitFileAccess not called? */



// 空闲链表上没有可用的vfd

if (VfdCache[0].nextFree == 0)

{

/*

* The free list is empty so it is time to increase the size of the

* array.  We choose to double it each time this happens. However,

* there's not much point in starting *real* small.

*/

Size        newCacheSize = SizeVfdCache * 2; // 原有容量*2

Vfd           *newVfdCache;



if (newCacheSize < 32) // 32个起步,考虑初始化就直接分配,这样的话vfd只有头结点,算作一个,这样分配才2个,太少了。

newCacheSize = 32;



/*

* Be careful not to clobber VfdCache ptr if realloc fails.

*/

newVfdCache = (Vfd *) realloc(VfdCache, sizeof(Vfd) * newCacheSize);

if (newVfdCache == NULL)

ereport(ERROR,

(errcode(ERRCODE_OUT_OF_MEMORY),

errmsg("out of memory")));

VfdCache = newVfdCache; // 新内存片的起始地址



/*

* Initialize the new entries and link them into the free list.

*/

// 之前的vfd已经通过realloc拷贝,只需初始化刚刚分配的vfd,从SizeVfdCache开始,一直到末尾

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;



/*

* Record the new size

*/

SizeVfdCache = newCacheSize; // SizeVfdCache记录现在的vfd数组大小

}

// 返回下标并修改指向下一个free节点的指针

file = VfdCache[0].nextFree;

VfdCache[0].nextFree = VfdCache[file].nextFree;

return file;

}

如图所示(依据环境为vfd初始化后第一次分配的情况):
AllocateVfd (1).png

4.释放内核维护的文件描述符

注意:vfd结构本身没有被释放,因为分配的时候是连续分配。只是更改了其中的fd为VFD_CLOSED,并对计数-1。

1.判断是否超过了max_safe_fds,值为一个固定的数字,pg14是48

/*

* Release kernel FDs as needed to get under the max_safe_fds limit.

* After calling this, it's OK to try to open another file.

*/

// nfile是经由fd.c通过open打开的, numAllocatedDescs是经由fd.c通过fopen打开的, numExternalFDs是系统或者没有通过fd.c打开的文件描述符

static void

ReleaseLruFiles(void)

{

while (nfile + numAllocatedDescs + numExternalFDs >= max_safe_fds)

{

    if (!ReleaseLruFile())

    break;

}

}

2.nfile > 0 表示当前有打开的文件,关闭掉最近最少使用的文件描述符(就是最早打开的那个)。

/*

* Release one kernel FD by closing the least-recently-used VFD.

*/

// 最近最少使用的fd关闭掉,直接关闭掉第一个节点

static bool

ReleaseLruFile(void)

{

if (nfile > 0)

{

    /*

    * There are opened files and so there should be at least one used vfd

    * in the ring.

    */

     Assert(VfdCache[0].lruMoreRecently != 0); // 最早打开的文件描述符在表尾,通过VfdCache[0].lruMoreRecently索引

     LruDelete(VfdCache[0].lruMoreRecently);

     return true;            /* freed a file */

}

return false;                /* no files available to free */

}

3.调用close关闭文件描述符,修改vfd结构相关成员变量。

static void

LruDelete(File file)

{

Vfd           *vfdP;

Assert(file != 0); // 不删头结点

        vfdP = &VfdCache[file];

        close(vfdP->fd); // 关闭系统文件描述符

vfdP->fd = VFD_CLOSED;  // fd设置为无效状态

--nfile; // 计数减一



/* delete the vfd record from the LRU ring */

Delete(file);

}

4.调整VfdCache指向的数组结构

static void

Delete(File file)

{

Vfd           *vfdP;



Assert(file != 0);

vfdP = &VfdCache[file];

VfdCache[vfdP->lruLessRecently].lruMoreRecently = vfdP->lruMoreRecently;

VfdCache[vfdP->lruMoreRecently].lruLessRecently = vfdP->lruLessRecently;

}

如图所示:

在这里插入图片描述

  1. 给定文件路径,打开文件, 获取kernel fd
/*

* Open a file with BasicOpenFilePerm() and pass default file mode for the

* fileMode parameter.

*/

// fileFlags : 文件读、写、执行、截断、创建等

// int pg_file_create_mode = PG_FILE_MODE_OWNER = S_IRUSR |  S_IWUSR , 用户读写,受到进程mask的影响

int

BasicOpenFile(const char *fileName, int fileFlags)

{

return BasicOpenFilePerm(fileName, fileFlags, pg_file_create_mode);

}

进入到BasicOpenFilePerm:

int

BasicOpenFilePerm(const char *fileName, int fileFlags, mode_t fileMode)

{

int            fd;

tryAgain:

// pg自定义PG_O_DIRECT不要和系统存在的flag相冲突

// PG_O_DIRECT_USE_F_NOCACHE这个宏主要用于做系统兼容,macOS中open()系统调用没有O_DIRECT,所以只能用fcntl()去修改File status flags,然而大部分类unix系统都可以在open时指定O_DIRECT。

//指定O_DIRECT会在文件读写时绕过操作系统缓存,这个标志只是建议,不一定有效。

#ifdef PG_O_DIRECT_USE_F_NOCACHE



/*

* The value we defined to stand in for O_DIRECT when simulating it with

* F_NOCACHE had better not collide with any of the standard flags.

*/

StaticAssertStmt((PG_O_DIRECT &

(O_APPEND |

O_CREAT |

O_EXCL |

O_RDWR |

O_RDONLY |

O_SYNC |

O_TRUNC |

O_WRONLY)) == 0,

"PG_O_DIRECT value collides with standard flag");



#if defined(O_CLOEXEC)

StaticAssertStmt((PG_O_DIRECT & O_CLOEXEC) == 0,

"PG_O_DIRECT value collides with O_CLOEXEC");

#endif

#if defined(O_DSYNC)

StaticAssertStmt((PG_O_DIRECT & O_DSYNC) == 0,

"PG_O_DIRECT value collides with O_DSYNC");

#endif

      //没有O_DIRECT标志,只能用fcntl在open之后获取到fd之后再修改,所以这个表达式把PG_O_DIRECT拿掉,fileFlags本身没有变化

fd = open(fileName, fileFlags & ~PG_O_DIRECT, fileMode);

#else

fd = open(fileName, fileFlags, fileMode);

#endif



if (fd >= 0)

{

#ifdef PG_O_DIRECT_USE_F_NOCACHE

if (fileFlags & PG_O_DIRECT)

{

     if (fcntl(fd, F_NOCACHE, 1) < 0)

    {

         // errno是全局的,close()也会设置errno,所以这里在进入close之前先保存一下

         int            save_errno = errno;

         close(fd);

         errno = save_errno;

         return -1;

    }

}

#endif

return fd;                /* success! */

}

      // open调用失败,设置errno,判断errno的值:

      // EMFILE:每个进程能打开的文件描述符有限制,意味着超过了限制;

      // ENFILE:超出了当前系统文件描述符的限制,统计了所有进程打开的

      // 当是上面两种错误时,利用lru算法,关闭最近最少使用的文件描述符,重新再打开一次,如果不是,返回-1表示失败。

if (errno == EMFILE || errno == ENFILE)

{

    int            save_errno = errno;

     ereport(LOG,

     (errcode(ERRCODE_INSUFFICIENT_RESOURCES),

     errmsg("out of file descriptors: %m; release and retry")));

     errno = 0;

     if (ReleaseLruFile())

          goto tryAgain;

     errno = save_errno;

}



return -1;                    /* failure */

}

6.FreeVfd

fileName要释放,它是单独申请的一块内存

把该vfd挂到free列表上,这块空间没法free

static void

FreeVfd(File file)

{

Vfd           *vfdP = &VfdCache[file];

if (vfdP->fileName != NULL)

{

free(vfdP->fileName);

vfdP->fileName = NULL;

}

vfdP->fdstate = 0x0;

vfdP->nextFree = VfdCache[0].nextFree;

VfdCache[0].nextFree = file;

}

7.Insert

static void

Insert(File file)

{

Vfd           *vfdP;



Assert(file != 0);

vfdP = &VfdCache[file];



vfdP->lruMoreRecently = 0;

vfdP->lruLessRecently = VfdCache[0].lruLessRecently;

VfdCache[0].lruLessRecently = file;

VfdCache[vfdP->lruLessRecently].lruMoreRecently = file;

}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值