📄 bufmgr.c
字号:
bufHdr->flags |= BM_IO_ERROR; elog(ERROR, "BufferSync: cannot write %u for %s", bufHdr->tag.blockNum, bufHdr->sb_relname); } BufferFlushCount++; /* * If this buffer was marked by someone as DIRTY while we * were flushing it out we must not clear DIRTY flag - * vadim 01/17/97 */ if (!(bufHdr->flags & BM_JUST_DIRTIED)) bufHdr->flags &= ~BM_DIRTY; if (reln != (Relation) NULL) RelationDecrementReferenceCount(reln); } } } SpinRelease(BufMgrLock); LocalBufferSync();}/* * WaitIO -- Block until the IO_IN_PROGRESS flag on 'buf' * is cleared. Because IO_IN_PROGRESS conflicts are * expected to be rare, there is only one BufferIO * lock in the entire system. All processes block * on this semaphore when they try to use a buffer * that someone else is faulting in. Whenever a * process finishes an IO and someone is waiting for * the buffer, BufferIO is signaled (SignalIO). All * waiting processes then wake up and check to see * if their buffer is now ready. This implementation * is simple, but efficient enough if WaitIO is * rarely called by multiple processes simultaneously. * * ProcSleep atomically releases the spinlock and goes to * sleep. * * Note: there is an easy fix if the queue becomes long. * save the id of the buffer we are waiting for in * the queue structure. That way signal can figure * out which proc to wake up. */#ifdef HAS_TEST_AND_SETstatic voidWaitIO(BufferDesc *buf, SPINLOCK spinlock){ SpinRelease(spinlock); S_LOCK(&(buf->io_in_progress_lock)); S_UNLOCK(&(buf->io_in_progress_lock)); SpinAcquire(spinlock);}#else /* HAS_TEST_AND_SET */IpcSemaphoreId WaitIOSemId;IpcSemaphoreId WaitCLSemId;static voidWaitIO(BufferDesc *buf, SPINLOCK spinlock){ bool inProgress; for (;;) { /* wait until someone releases IO lock */ (*NWaitIOBackendP)++; SpinRelease(spinlock); IpcSemaphoreLock(WaitIOSemId, 0, 1); SpinAcquire(spinlock); inProgress = (buf->flags & BM_IO_IN_PROGRESS); if (!inProgress) break; }}/* * SignalIO */static voidSignalIO(BufferDesc *buf){ /* somebody better be waiting. */ Assert(buf->refcount > 1); IpcSemaphoreUnlock(WaitIOSemId, 0, *NWaitIOBackendP); *NWaitIOBackendP = 0;}#endif /* HAS_TEST_AND_SET */long NDirectFileRead; /* some I/O's are direct file access. * bypass bufmgr */long NDirectFileWrite; /* e.g., I/O in psort and hashjoin. */voidPrintBufferUsage(FILE *statfp){ float hitrate; float localhitrate; if (ReadBufferCount == 0) hitrate = 0.0; else hitrate = (float) BufferHitCount *100.0 / ReadBufferCount; if (ReadLocalBufferCount == 0) localhitrate = 0.0; else localhitrate = (float) LocalBufferHitCount *100.0 / ReadLocalBufferCount; fprintf(statfp, "!\tShared blocks: %10ld read, %10ld written, buffer hit rate = %.2f%%\n", ReadBufferCount - BufferHitCount, BufferFlushCount, hitrate); fprintf(statfp, "!\tLocal blocks: %10ld read, %10ld written, buffer hit rate = %.2f%%\n", ReadLocalBufferCount - LocalBufferHitCount, LocalBufferFlushCount, localhitrate); fprintf(statfp, "!\tDirect blocks: %10ld read, %10ld written\n", NDirectFileRead, NDirectFileWrite);}voidResetBufferUsage(){ BufferHitCount = 0; ReadBufferCount = 0; BufferFlushCount = 0; LocalBufferHitCount = 0; ReadLocalBufferCount = 0; LocalBufferFlushCount = 0; NDirectFileRead = 0; NDirectFileWrite = 0;}/* ---------------------------------------------- * ResetBufferPool * * this routine is supposed to be called when a transaction aborts. * it will release all the buffer pins held by the transaciton. * * ---------------------------------------------- */voidResetBufferPool(){ int i; for (i = 1; i <= NBuffers; i++) { CommitInfoNeedsSave[i - 1] = 0; if (BufferIsValid(i)) { while (PrivateRefCount[i - 1] > 0) ReleaseBuffer(i); } LastRefCount[i - 1] = 0; } ResetLocalBufferPool();}/* ----------------------------------------------- * BufferPoolCheckLeak * * check if there is buffer leak * * ----------------------------------------------- */intBufferPoolCheckLeak(){ int i; int result = 0; for (i = 1; i <= NBuffers; i++) { if (BufferIsValid(i)) { BufferDesc *buf = &(BufferDescriptors[i - 1]); elog(NOTICE, "Buffer Leak: [%03d] (freeNext=%d, freePrev=%d, \relname=%s, blockNum=%d, flags=0x%x, refcount=%d %d)", i - 1, buf->freeNext, buf->freePrev, buf->sb_relname, buf->tag.blockNum, buf->flags, buf->refcount, PrivateRefCount[i - 1]); result = 1; } } return (result);}/* ------------------------------------------------ * FlushBufferPool * * flush all dirty blocks in buffer pool to disk * * ------------------------------------------------ */voidFlushBufferPool(int StableMainMemoryFlag){ if (!StableMainMemoryFlag) { BufferSync(); smgrcommit(); }}/* * BufferGetBlockNumber * Returns the block number associated with a buffer. * * Note: * Assumes that the buffer is valid. */BlockNumberBufferGetBlockNumber(Buffer buffer){ Assert(BufferIsValid(buffer)); /* XXX should be a critical section */ if (BufferIsLocal(buffer)) return LocalBufferDescriptors[-buffer - 1].tag.blockNum; else return BufferDescriptors[buffer - 1].tag.blockNum;}#ifdef NOT_USED/* * BufferGetRelation * Returns the relation desciptor associated with a buffer. * * Note: * Assumes buffer is valid. */RelationBufferGetRelation(Buffer buffer){ Relation relation; Oid relid; Assert(BufferIsValid(buffer)); Assert(!BufferIsLocal(buffer)); /* not supported for local buffers */ /* XXX should be a critical section */ relid = BufferDescriptors[buffer - 1].tag.relId.relId; relation = RelationIdGetRelation(relid); RelationDecrementReferenceCount(relation); if (RelationHasReferenceCountZero(relation)) { /* * elog(NOTICE, "BufferGetRelation: 0->1"); */ RelationIncrementReferenceCount(relation); } return relation;}#endif/* * BufferReplace * * Flush the buffer corresponding to 'bufHdr' * */static intBufferReplace(BufferDesc *bufHdr, bool bufferLockHeld){ Relation reln; Oid bufdb, bufrel; int status; if (!bufferLockHeld) SpinAcquire(BufMgrLock); /* * first try to find the reldesc in the cache, if no luck, don't * bother to build the reldesc from scratch, just do a blind write. */ bufdb = bufHdr->tag.relId.dbId; bufrel = bufHdr->tag.relId.relId; if (bufdb == MyDatabaseId || bufdb == (Oid) NULL) reln = RelationIdCacheGetRelation(bufrel); else reln = (Relation) NULL; /* To check if block content changed while flushing. - vadim 01/17/97 */ bufHdr->flags &= ~BM_JUST_DIRTIED; SpinRelease(BufMgrLock); if (reln != (Relation) NULL) { status = smgrflush(DEFAULT_SMGR, reln, bufHdr->tag.blockNum, (char *) MAKE_PTR(bufHdr->data)); } else { /* blind write always flushes */ status = smgrblindwrt(DEFAULT_SMGR, bufHdr->sb_dbname, bufHdr->sb_relname, bufdb, bufrel, bufHdr->tag.blockNum, (char *) MAKE_PTR(bufHdr->data)); } if (reln != (Relation) NULL) RelationDecrementReferenceCount(reln); if (status == SM_FAIL) return FALSE; BufferFlushCount++; return TRUE;}/* * RelationGetNumberOfBlocks * Returns the buffer descriptor associated with a page in a relation. * * Note: * XXX may fail for huge relations. * XXX should be elsewhere. * XXX maybe should be hidden */BlockNumberRelationGetNumberOfBlocks(Relation relation){ return ((relation->rd_myxactonly) ? relation->rd_nblocks : smgrnblocks(DEFAULT_SMGR, relation));}/* --------------------------------------------------------------------- * ReleaseRelationBuffers * * this function unmarks all the dirty pages of a relation * in the buffer pool so that at the end of transaction * these pages will not be flushed. * XXX currently it sequentially searches the buffer pool, should be * changed to more clever ways of searching. * -------------------------------------------------------------------- */voidReleaseRelationBuffers(Relation rel){ int i; int holding = 0; BufferDesc *buf; if (rel->rd_myxactonly) { for (i = 0; i < NLocBuffer; i++) { buf = &LocalBufferDescriptors[i]; if ((buf->flags & BM_DIRTY) && (buf->tag.relId.relId == RelationGetRelid(rel))) buf->flags &= ~BM_DIRTY; } return; } for (i = 1; i <= NBuffers; i++) { buf = &BufferDescriptors[i - 1]; if (!holding) { SpinAcquire(BufMgrLock); holding = 1; } if ((buf->flags & BM_DIRTY) && (buf->tag.relId.dbId == MyDatabaseId) && (buf->tag.relId.relId == RelationGetRelid(rel))) { buf->flags &= ~BM_DIRTY; if (!(buf->flags & BM_FREE)) { SpinRelease(BufMgrLock); holding = 0; ReleaseBuffer(i); } } } if (holding) SpinRelease(BufMgrLock);}/* --------------------------------------------------------------------- * DropBuffers * * This function marks all the buffers in the buffer cache for a * particular database as clean. This is used when we destroy a * database, to avoid trying to flush data to disk when the directory * tree no longer exists. * * This is an exceedingly non-public interface. * -------------------------------------------------------------------- */voidDropBuffers(Oid dbid){ int i; BufferDesc *buf; SpinAcquire(BufMgrLock); for (i = 1; i <= NBuffers; i++) { buf = &BufferDescriptors[i - 1]; if ((buf->tag.relId.dbId == dbid) && (buf->flags & BM_DIRTY)) buf->flags &= ~BM_DIRTY; } SpinRelease(BufMgrLock);}/* ----------------------------------------------------------------- * PrintBufferDescs * * this function prints all the buffer descriptors, for debugging * use only. * ----------------------------------------------------------------- */voidPrintBufferDescs(){ int i; BufferDesc *buf = BufferDescriptors; if (IsUnderPostmaster) { SpinAcquire(BufMgrLock); for (i = 0; i < NBuffers; ++i, ++buf) { elog(DEBUG, "[%02d] (freeNext=%d, freePrev=%d, relname=%s, \blockNum=%d, flags=0x%x, refcount=%d %d)", i, buf->freeNext, buf->freePrev, buf->sb_relname, buf->tag.blockNum, buf->flags, buf->refcount, PrivateRefCount[i]); } SpinRelease(BufMgrLock); } else { /* interactive backend */ for (i = 0; i < NBuffers; ++i, ++buf) { printf("[%-2d] (%s, %d) flags=0x%x, refcnt=%d %ld)\n", i, buf->sb_relname, buf->tag.blockNum, buf->flags, buf->refcount, PrivateRefCount[i]); } }}voidPrintPinnedBufs(){ int i; BufferDesc *buf = BufferDescriptors; SpinAcquire(BufMgrLock); for (i = 0; i < NBuffers; ++i, ++buf) { if (PrivateRefCount[i] > 0) elog(NOTICE, "[%02d] (freeNext=%d, freePrev=%d, relname=%s, \blockNum=%d, flags=0x%x, refcount=%d %d)\n", i, buf->freeNext, buf->freePrev, buf->sb_relname, buf->tag.blockNum, buf->flags, buf->refcount, PrivateRefCount[i]); } SpinRelease(BufMgrLock);}/* * BufferPoolBlowaway * * this routine is solely for the purpose of experiments -- sometimes * you may want to blowaway whatever is left from the past in buffer * pool and start measuring some performance with a clean empty buffer * pool. */#ifdef NOT_USEDvoidBufferPoolBlowaway(){ int i; BufferSync(); for (i = 1; i <= NBuffers; i++) { if (BufferIsValid(i)) { while (BufferIsValid(i)) ReleaseBuffer(i); } BufTableDelete(&BufferDescriptors[i - 1]); }}#endif/* --------------------------------------------------------------------- * BlowawayRelationBuffers * * This function blowaway all the pages with blocknumber >= passed * of a relation in the buffer pool. Used by vacuum before truncation... * * Returns: 0 - Ok, -1 - DIRTY, -2 - PINNED * * XXX currently it sequentially searches the buffer pool, should be * changed to more clever ways of searching. * -------------------------------------------------------------------- */intBlowawayRelationBuffers(Relation rel, BlockNumber block){ int i; BufferDesc *buf;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -