• 基于C++实现页面的缓冲区管理器


    1. 实验目标

    本次实验要实现一个页面的缓冲区管理器。具体要实现以下的函数:

    • ~BufMgr():

    清除所有脏页并释放缓冲池和 BufDesc 表

    • void advanceClock():

    用来找到下一个时钟的位置

    • void allocBuf(FrameId& frame):

    使用时钟算法分配自由帧;如有必要,将脏页写回磁盘。

    • void readPage(File* file, const PageId PageNo, Page*& page)

    通过调用 lookup()方法检查页是否已经在缓冲池中。当页不在缓冲池中可以在哈希表上抛出 HashNotFoundException 以获取帧编号。

    • void unPinPage(File* file, const PageId PageNo, const bool dirty)

    减少一个页面的占用次数

    • void allocPage(File* file, PageId& PageNo, Page*& page)

    通过调用 file->allocatePage()方法在指定的文件中分配一个空页。此方法将返回新分配的页。然后调用 allocBuf()以获取缓冲池帧。接下来,将一个条目插入到哈希表中,并在帧上调用 Set(),以正确设置它。

    • void disposePage(File* file, const PageId pageNo)

    功能是释放一个页面

    • void flushFile(File* file)

    功能是找到含有对应文件的页面,并释放

    2. 代码实现

    BufMgr::~BufMgr() {
        delete hashTable;
        delete[] bufPool;
        delete[] bufDescTable;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    直接调用 delete 删除哈希表、缓冲池、缓冲池表

    void BufMgr::advanceClock() {
        clockHand++;
        if (clockHand >= numBufs) {
            clockHand %= numBufs;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    将时钟提前到缓冲池的下一帧。

    如果指针超过了最大值,进行取模操作。

    void BufMgr::allocBuf(FrameId &frame) {
        unsigned pinned = 0;
        while (true) {
            advanceClock();
            if (!bufDescTable[clockHand].valid) {
                frame = clockHand;
                return;
            }
            if (bufDescTable[clockHand].refbit) {
                bufDescTable[clockHand].refbit = false;
                continue;
            }
            if (bufDescTable[clockHand].pinCnt) {
                pinned++;
                if (pinned == numBufs) {
                    throw BufferExceededException();
                } else {
                    continue;
                }
            }
            if (bufDescTable[clockHand].dirty) {
                bufDescTable[clockHand].file->writePage(bufPool[clockHand]);
                bufDescTable[clockHand].dirty = false;
            }
            frame = clockHand;
            if (bufDescTable[clockHand].valid) {
                try {
                    hashTable->remove(bufDescTable[clockHand].file, bufDescTable[clockHand].pageNo);
                }
                catch (HashNotFoundException &) {
                }
            }
            break;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    遍历栈区寻找可用的页面。如果是没有被使用过的页面,直接进行分配。如果缓冲区所有的页面都被占用,那么会进行报错 BufferExceededException()。如果找到脏页,会将它写回磁盘,并将脏页标记给清除。如果不是脏页,那么就进行分配操作。如果它在哈希表中要将它移除。

    void BufMgr::readPage(File *file, const PageId pageNo, Page *&page) {
        FrameId frame;
        try {
            hashTable->lookup(file, pageNo, frame);
            bufDescTable[frame].refbit = true;
            bufDescTable[frame].pinCnt++;
            page = (bufPool + frame);
        } catch (HashNotFoundException &) {
            allocBuf(frame);
            bufPool[frame] = file->readPage(pageNo);
            hashTable->insert(file, pageNo, frame);
            bufDescTable[frame].Set(file, pageNo);
            page = (bufPool + frame);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    如果页面在缓冲池中,增加它的占用次数,调用 page 返回指向该页面的指针。

    如果页面不在缓冲池中,那么将页面读取到缓冲池,插入哈希表中,调用 set 正确设置该界面,调用 page 返回指向该页面的指针。

    void BufMgr::unPinPage(File *file, const PageId pageNo, const bool dirty) {
        FrameId frame;
        try {
            hashTable->lookup(file, pageNo, frame);
        } catch (HashNotFoundException &) {
            //没有该页面
            cerr << "Warning: unpinning a nonexistent page" << endl;
            return;
        }
        //找到页面
        if (bufDescTable[frame].pinCnt > 0) {
            bufDescTable[frame].pinCnt--;
            if (dirty) {
                bufDescTable[frame].dirty = true;
            }
        } else {
            //pin = 0,抛出异常
            throw PageNotPinnedException(bufDescTable[frame].file->filename(), bufDescTable[frame].pageNo, frame);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    如果缓冲池中没有该页面,进行异常提示。

    如果在缓冲池中,那么将它的占用次数减少。如果占用次数为 0,进行报错。

    void BufMgr::flushFile(const File *file) {
        for (FrameId fi = 0; fi < numBufs; fi++) {
            if (bufDescTable[fi].file == file) {
                if (!bufDescTable[fi].valid) {
                    throw BadBufferException(fi, bufDescTable[fi].dirty, bufDescTable[fi].valid, bufDescTable[fi].refbit);
                }
                if (bufDescTable[fi].pinCnt > 0) {
                    throw PagePinnedException(file->filename(), bufDescTable[fi].pageNo, fi);
                }
                if (bufDescTable[fi].dirty) {
                    bufDescTable[fi].file->writePage(bufPool[fi]);
                    bufDescTable[fi].dirty = false;
                }
                hashTable->remove(file, bufDescTable[fi].pageNo);
                bufDescTable[fi].Clear();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    遍历整个表,找到含有对应页面的缓冲页,移除并清空该页面。如果页面是脏页,则将其写回磁盘,初始化脏页标记。如果页面被占用或者页面不可用,则进行报错。

    void BufMgr::allocPage(File *file, PageId &pageNo, Page *&page) {
        FrameId frame;
        Page p = file->allocatePage();
        allocBuf(frame);
        bufPool[frame] = p;
        pageNo = p.page_number();
        hashTable->insert(file, pageNo, frame);
        bufDescTable[frame].Set(file, pageNo);
        page = bufPool + frame;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    掉用 allocatePage()分配一个新页面,加入哈希表,调用 set(),返回该页面指针。

    void BufMgr::disposePage(File *file, const PageId PageNo) {
        FrameId frame;
        try {
            hashTable->lookup(file, PageNo, frame);
            hashTable->remove(file, PageNo);
            bufDescTable[frame].Clear();
        } catch (HashNotFoundException &) {
        }
        file->deletePage(PageNo);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    删除一个页面。如果它在缓冲池中,要将缓冲内容一并删除。

    3.实验结果

    12个样例均能通过,实验结果如下:

    在这里插入图片描述

    me].Clear();
    } catch (HashNotFoundException &) {
    }
    file->deletePage(PageNo);
    }

    
    删除一个页面。如果它在缓冲池中,要将缓冲内容一并删除。
    
    
    
    • 1
    • 2
    • 3
    • 4
  • 相关阅读:
    最近的工作和生活
    用AI学习HIVE
    CSS 笔记(十一):属性 —— 特效
    K8s-Helm
    Kafka重点笔记
    博客园商业化之路-众包平台:500位驭码好汉,等你来发单挑战
    中期国际2.19黄金市场分析:美国通胀数据火热,黄金面临高利率削弱的挑战
    Puppeteer动态代理实战:提升数据抓取效率
    van-calendar 实现移动端日历效果
    laravel生命周期超细腻
  • 原文地址:https://blog.csdn.net/newlw/article/details/126084368