⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 slru.c

📁 PostgreSQL7.4.6 for Linux
💻 C
📖 第 1 页 / 共 2 页
字号:
/*------------------------------------------------------------------------- * * slru.c *		Simple LRU buffering for transaction status logfiles * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * $Header: /cvsroot/pgsql/src/backend/access/transam/slru.c,v 1.7.2.1 2004/02/23 23:03:43 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <fcntl.h>#include <sys/stat.h>#include <unistd.h>#include "access/slru.h"#include "storage/lwlock.h"#include "miscadmin.h"/* * Define segment size.  A page is the same BLCKSZ as is used everywhere * else in Postgres.  The segment size can be chosen somewhat arbitrarily; * we make it 32 pages by default, or 256Kb, i.e. 1M transactions for CLOG * or 64K transactions for SUBTRANS. * * Note: because TransactionIds are 32 bits and wrap around at 0xFFFFFFFF, * page numbering also wraps around at 0xFFFFFFFF/xxxx_XACTS_PER_PAGE (where * xxxx is CLOG or SUBTRANS, respectively), and segment numbering at * 0xFFFFFFFF/xxxx_XACTS_PER_PAGE/SLRU_PAGES_PER_SEGMENT.  We need * take no explicit notice of that fact in this module, except when comparing * segment and page numbers in SimpleLruTruncate (see PagePrecedes()). */#define SLRU_PAGES_PER_SEGMENT	32/*---------- * Shared-memory data structures for SLRU control * * We use a simple least-recently-used scheme to manage a pool of page * buffers.  Under ordinary circumstances we expect that write * traffic will occur mostly to the latest page (and to the just-prior * page, soon after a page transition).  Read traffic will probably touch * a larger span of pages, but in any case a fairly small number of page * buffers should be sufficient.  So, we just search the buffers using plain * linear search; there's no need for a hashtable or anything fancy. * The management algorithm is straight LRU except that we will never swap * out the latest page (since we know it's going to be hit again eventually). * * We use a control LWLock to protect the shared data structures, plus * per-buffer LWLocks that synchronize I/O for each buffer.  A process * that is reading in or writing out a page buffer does not hold the control * lock, only the per-buffer lock for the buffer it is working on. * * To change the page number or state of a buffer, one must normally hold * the control lock.  (The sole exception to this rule is that a writer * process changes the state from DIRTY to WRITE_IN_PROGRESS while holding * only the per-buffer lock.)  If the buffer's state is neither EMPTY nor * CLEAN, then there may be processes doing (or waiting to do) I/O on the * buffer, so the page number may not be changed, and the only allowed state * transition is to change WRITE_IN_PROGRESS to DIRTY after dirtying the page. * To do any other state transition involving a buffer with potential I/O * processes, one must hold both the per-buffer lock and the control lock. * (Note the control lock must be acquired second; do not wait on a buffer * lock while holding the control lock.)  A process wishing to read a page * marks the buffer state as READ_IN_PROGRESS, then drops the control lock, * acquires the per-buffer lock, and rechecks the state before proceeding. * This recheck takes care of the possibility that someone else already did * the read, while the early marking prevents someone else from trying to * read the same page into a different buffer. * * Note we are assuming that read and write of the state value is atomic, * since I/O processes may examine and change the state while not holding * the control lock. * * As with the regular buffer manager, it is possible for another process * to re-dirty a page that is currently being written out.	This is handled * by setting the page's state from WRITE_IN_PROGRESS to DIRTY.  The writing * process must notice this and not mark the page CLEAN when it's done. *---------- */typedef enum{	SLRU_PAGE_EMPTY,			/* buffer is not in use */	SLRU_PAGE_READ_IN_PROGRESS, /* page is being read in */	SLRU_PAGE_CLEAN,			/* page is valid and not dirty */	SLRU_PAGE_DIRTY,			/* page is valid but needs write */	SLRU_PAGE_WRITE_IN_PROGRESS /* page is being written out */} SlruPageStatus;/* * Shared-memory state */typedef struct SlruSharedData{	/*	 * Info for each buffer slot.  Page number is undefined when status is	 * EMPTY.  lru_count is essentially the number of page switches since	 * last use of this page; the page with highest lru_count is the best	 * candidate to replace.	 */	char	   *page_buffer[NUM_CLOG_BUFFERS];	SlruPageStatus page_status[NUM_CLOG_BUFFERS];	int			page_number[NUM_CLOG_BUFFERS];	unsigned int page_lru_count[NUM_CLOG_BUFFERS];	/*	 * latest_page_number is the page number of the current end of the	 * CLOG; this is not critical data, since we use it only to avoid	 * swapping out the latest page.	 */	int			latest_page_number;} SlruSharedData;typedef SlruSharedData *SlruShared;#define SlruFileName(ctl, path, seg) \	snprintf(path, MAXPGPATH, "%s/%04X", (ctl)->Dir, seg)/* * Macro to mark a buffer slot "most recently used". */#define SlruRecentlyUsed(shared, slotno)	\	do { \		if ((shared)->page_lru_count[slotno] != 0) { \			int		iilru; \			for (iilru = 0; iilru < NUM_CLOG_BUFFERS; iilru++) \				(shared)->page_lru_count[iilru]++; \			(shared)->page_lru_count[slotno] = 0; \		} \	} while (0)/* Saved info for SlruReportIOError */typedef enum{	SLRU_OPEN_FAILED,	SLRU_CREATE_FAILED,	SLRU_SEEK_FAILED,	SLRU_READ_FAILED,	SLRU_WRITE_FAILED} SlruErrorCause;static SlruErrorCause slru_errcause;static int	slru_errno;static bool SlruPhysicalReadPage(SlruCtl ctl, int pageno, int slotno);static bool SlruPhysicalWritePage(SlruCtl ctl, int pageno, int slotno);static void SlruReportIOError(SlruCtl ctl, int pageno, TransactionId xid);static int	SlruSelectLRUPage(SlruCtl ctl, int pageno);static bool SlruScanDirectory(SlruCtl ctl, int cutoffPage, bool doDeletions);/* * Initialization of shared memory */intSimpleLruShmemSize(void){	return MAXALIGN(sizeof(SlruSharedData)) + BLCKSZ * NUM_CLOG_BUFFERS#ifdef EXEC_BACKEND		+ MAXALIGN(sizeof(SlruLockData))#endif		;}voidSimpleLruInit(SlruCtl ctl, const char *name, const char *subdir){	bool		found;	char	   *ptr;	SlruShared	shared;	SlruLock	locks;	ptr = ShmemInitStruct(name, SimpleLruShmemSize(), &found);	shared = (SlruShared) ptr;#ifdef EXEC_BACKEND	/*	 * Locks are in shared memory	 */	locks = (SlruLock) (ptr + MAXALIGN(sizeof(SlruSharedData)) +						BLCKSZ * NUM_CLOG_BUFFERS);#else	/*	 * Locks are in private memory	 */	Assert(!IsUnderPostmaster);	locks = malloc(sizeof(SlruLockData));	Assert(locks);#endif	if (!IsUnderPostmaster)		/* Initialize locks and shared memory area */	{		char	   *bufptr;		int			slotno;		Assert(!found);		locks->ControlLock = LWLockAssign();		memset(shared, 0, sizeof(SlruSharedData));		bufptr = (char *) shared + MAXALIGN(sizeof(SlruSharedData));		for (slotno = 0; slotno < NUM_CLOG_BUFFERS; slotno++)		{			locks->BufferLocks[slotno] = LWLockAssign();			shared->page_buffer[slotno] = bufptr;			shared->page_status[slotno] = SLRU_PAGE_EMPTY;			shared->page_lru_count[slotno] = 1;			bufptr += BLCKSZ;		}		/* shared->latest_page_number will be set later */	}	else		Assert(found);	ctl->locks = locks;	ctl->shared = shared;	/* Init directory path */	snprintf(ctl->Dir, MAXPGPATH, "%s/%s", DataDir, subdir);}/* * Initialize (or reinitialize) a page to zeroes. * * The page is not actually written, just set up in shared memory. * The slot number of the new page is returned. * * Control lock must be held at entry, and will be held at exit. */intSimpleLruZeroPage(SlruCtl ctl, int pageno){	int			slotno;	SlruShared	shared = (SlruShared) ctl->shared;	/* Find a suitable buffer slot for the page */	slotno = SlruSelectLRUPage(ctl, pageno);	Assert(shared->page_status[slotno] == SLRU_PAGE_EMPTY ||		   shared->page_status[slotno] == SLRU_PAGE_CLEAN ||		   shared->page_number[slotno] == pageno);	/* Mark the slot as containing this page */	shared->page_number[slotno] = pageno;	shared->page_status[slotno] = SLRU_PAGE_DIRTY;	SlruRecentlyUsed(shared, slotno);	/* Set the buffer to zeroes */	MemSet(shared->page_buffer[slotno], 0, BLCKSZ);	/* Assume this page is now the latest active page */	shared->latest_page_number = pageno;	return slotno;}/* * Find a page in a shared buffer, reading it in if necessary. * The page number must correspond to an already-initialized page. * * The passed-in xid is used only for error reporting, and may be * InvalidTransactionId if no specific xid is associated with the action. * * Return value is the shared-buffer address of the page. * The buffer's LRU access info is updated. * If forwrite is true, the buffer is marked as dirty. * * Control lock must be held at entry, and will be held at exit. */char *SimpleLruReadPage(SlruCtl ctl, int pageno, TransactionId xid, bool forwrite){	SlruShared	shared = (SlruShared) ctl->shared;	/* Outer loop handles restart if we lose the buffer to someone else */	for (;;)	{		int			slotno;		bool		ok;		/* See if page already is in memory; if not, pick victim slot */		slotno = SlruSelectLRUPage(ctl, pageno);		/* Did we find the page in memory? */		if (shared->page_number[slotno] == pageno &&			shared->page_status[slotno] != SLRU_PAGE_EMPTY)		{			/* If page is still being read in, we cannot use it yet */			if (shared->page_status[slotno] != SLRU_PAGE_READ_IN_PROGRESS)			{				/* otherwise, it's ready to use */				SlruRecentlyUsed(shared, slotno);				if (forwrite)					shared->page_status[slotno] = SLRU_PAGE_DIRTY;				return shared->page_buffer[slotno];			}		}		else		{			/* We found no match; assert we selected a freeable slot */			Assert(shared->page_status[slotno] == SLRU_PAGE_EMPTY ||				   shared->page_status[slotno] == SLRU_PAGE_CLEAN);		}		/* Mark the slot read-busy (no-op if it already was) */		shared->page_number[slotno] = pageno;		shared->page_status[slotno] = SLRU_PAGE_READ_IN_PROGRESS;		/*		 * Temporarily mark page as recently-used to discourage		 * SlruSelectLRUPage from selecting it again for someone else.		 */		SlruRecentlyUsed(shared, slotno);		/* Release shared lock, grab per-buffer lock instead */		LWLockRelease(ctl->locks->ControlLock);		LWLockAcquire(ctl->locks->BufferLocks[slotno], LW_EXCLUSIVE);		/*		 * Check to see if someone else already did the read, or took the		 * buffer away from us.  If so, restart from the top.		 */		if (shared->page_number[slotno] != pageno ||			shared->page_status[slotno] != SLRU_PAGE_READ_IN_PROGRESS)		{			LWLockRelease(ctl->locks->BufferLocks[slotno]);			LWLockAcquire(ctl->locks->ControlLock, LW_EXCLUSIVE);			continue;		}		/* Okay, do the read */		ok = SlruPhysicalReadPage(ctl, pageno, slotno);		/* Re-acquire shared control lock and update page state */		LWLockAcquire(ctl->locks->ControlLock, LW_EXCLUSIVE);		Assert(shared->page_number[slotno] == pageno &&			   shared->page_status[slotno] == SLRU_PAGE_READ_IN_PROGRESS);		shared->page_status[slotno] = ok ? SLRU_PAGE_CLEAN : SLRU_PAGE_EMPTY;		LWLockRelease(ctl->locks->BufferLocks[slotno]);		/* Now it's okay to ereport if we failed */		if (!ok)			SlruReportIOError(ctl, pageno, xid);		SlruRecentlyUsed(shared, slotno);		if (forwrite)			shared->page_status[slotno] = SLRU_PAGE_DIRTY;		return shared->page_buffer[slotno];	}}/* * Write a page from a shared buffer, if necessary. * Does nothing if the specified slot is not dirty. * * NOTE: only one write attempt is made here.  Hence, it is possible that * the page is still dirty at exit (if someone else re-dirtied it during * the write).	However, we *do* attempt a fresh write even if the page * is already being written; this is for checkpoints. * * Control lock must be held at entry, and will be held at exit. */voidSimpleLruWritePage(SlruCtl ctl, int slotno){	int			pageno;	bool		ok;	SlruShared	shared = (SlruShared) ctl->shared;	/* Do nothing if page does not need writing */	if (shared->page_status[slotno] != SLRU_PAGE_DIRTY &&		shared->page_status[slotno] != SLRU_PAGE_WRITE_IN_PROGRESS)		return;	pageno = shared->page_number[slotno];	/* Release shared lock, grab per-buffer lock instead */	LWLockRelease(ctl->locks->ControlLock);	LWLockAcquire(ctl->locks->BufferLocks[slotno], LW_EXCLUSIVE);	/*	 * Check to see if someone else already did the write, or took the	 * buffer away from us.  If so, do nothing.  NOTE: we really should	 * never see WRITE_IN_PROGRESS here, since that state should only	 * occur while the writer is holding the buffer lock.  But accept it	 * so that we have a recovery path if a writer aborts.	 */	if (shared->page_number[slotno] != pageno ||		(shared->page_status[slotno] != SLRU_PAGE_DIRTY &&		 shared->page_status[slotno] != SLRU_PAGE_WRITE_IN_PROGRESS))	{		LWLockRelease(ctl->locks->BufferLocks[slotno]);		LWLockAcquire(ctl->locks->ControlLock, LW_EXCLUSIVE);		return;	}	/*	 * Mark the slot write-busy.  After this point, a transaction status	 * update on this page will mark it dirty again.  NB: we are assuming	 * that read/write of the page status field is atomic, since we change	 * the state while not holding control lock.  However, we cannot set	 * this state any sooner, or we'd possibly fool a previous writer into	 * thinking he's successfully dumped the page when he hasn't.	 * (Scenario: other writer starts, page is redirtied, we come along	 * and set WRITE_IN_PROGRESS again, other writer completes and sets	 * CLEAN because redirty info has been lost, then we think it's clean	 * too.)	 */	shared->page_status[slotno] = SLRU_PAGE_WRITE_IN_PROGRESS;	/* Okay, do the write */	ok = SlruPhysicalWritePage(ctl, pageno, slotno);	/* Re-acquire shared control lock and update page state */	LWLockAcquire(ctl->locks->ControlLock, LW_EXCLUSIVE);	Assert(shared->page_number[slotno] == pageno &&		   (shared->page_status[slotno] == SLRU_PAGE_WRITE_IN_PROGRESS ||			shared->page_status[slotno] == SLRU_PAGE_DIRTY));	/* Cannot set CLEAN if someone re-dirtied page since write started */	if (shared->page_status[slotno] == SLRU_PAGE_WRITE_IN_PROGRESS)		shared->page_status[slotno] = ok ? SLRU_PAGE_CLEAN : SLRU_PAGE_DIRTY;	LWLockRelease(ctl->locks->BufferLocks[slotno]);	/* Now it's okay to ereport if we failed */	if (!ok)		SlruReportIOError(ctl, pageno, InvalidTransactionId);}/* * Physical read of a (previously existing) page into a buffer slot * * On failure, we cannot just ereport(ERROR) since caller has put state in * shared memory that must be undone.  So, we return FALSE and save enough * info in static variables to let SlruReportIOError make the report.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -