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

📄 slru.c

📁 postgresql8.3.4源码,开源数据库
💻 C
📖 第 1 页 / 共 3 页
字号:
/*------------------------------------------------------------------------- * * slru.c *		Simple LRU buffering for transaction status logfiles * * 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.  The control lock * must be held to examine or modify any shared state.	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. * * "Holding the control lock" means exclusive lock in all cases except for * SimpleLruReadPage_ReadOnly(); see comments for SlruRecentlyUsed() for * the implications of that. * * When initiating I/O on a buffer, we acquire the per-buffer lock exclusively * before releasing the control lock.  The per-buffer lock is released after * completing the I/O, re-acquiring the control lock, and updating the shared * state.  (Deadlock is not possible here, because we never try to initiate * I/O when someone else is already doing I/O on the same buffer.) * To wait for I/O to complete, release the control lock, acquire the * per-buffer lock in shared mode, immediately release the per-buffer lock, * reacquire the control lock, and then recheck state (since arbitrary things * could have happened while we didn't have the 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 re-setting the page's page_dirty flag. * * * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * $PostgreSQL: pgsql/src/backend/access/transam/slru.c,v 1.44 2008/01/01 19:45:48 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <fcntl.h>#include <sys/stat.h>#include <unistd.h>#include "access/slru.h"#include "access/transam.h"#include "access/xlog.h"#include "storage/fd.h"#include "storage/shmem.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()). * * Note: this file currently assumes that segment file names will be four * hex digits.	This sets a lower bound on the segment size (64K transactions * for 32-bit TransactionIds). */#define SLRU_PAGES_PER_SEGMENT	32#define SlruFileName(ctl, path, seg) \	snprintf(path, MAXPGPATH, "%s/%04X", (ctl)->Dir, seg)/* * During SimpleLruFlush(), we will usually not need to write/fsync more * than one or two physical files, but we may need to write several pages * per file.  We can consolidate the I/O requests by leaving files open * until control returns to SimpleLruFlush().  This data structure remembers * which files are open. */#define MAX_FLUSH_BUFFERS	16typedef struct SlruFlushData{	int			num_files;		/* # files actually open */	int			fd[MAX_FLUSH_BUFFERS];	/* their FD's */	int			segno[MAX_FLUSH_BUFFERS];		/* their log seg#s */} SlruFlushData;/* * Macro to mark a buffer slot "most recently used".  Note multiple evaluation * of arguments! * * The reason for the if-test is that there are often many consecutive * accesses to the same page (particularly the latest page).  By suppressing * useless increments of cur_lru_count, we reduce the probability that old * pages' counts will "wrap around" and make them appear recently used. * * We allow this code to be executed concurrently by multiple processes within * SimpleLruReadPage_ReadOnly().  As long as int reads and writes are atomic, * this should not cause any completely-bogus values to enter the computation. * However, it is possible for either cur_lru_count or individual * page_lru_count entries to be "reset" to lower values than they should have, * in case a process is delayed while it executes this macro.  With care in * SlruSelectLRUPage(), this does little harm, and in any case the absolute * worst possible consequence is a nonoptimal choice of page to evict.	The * gain from allowing concurrent reads of SLRU pages seems worth it. */#define SlruRecentlyUsed(shared, slotno)	\	do { \		int		new_lru_count = (shared)->cur_lru_count; \		if (new_lru_count != (shared)->page_lru_count[slotno]) { \			(shared)->cur_lru_count = ++new_lru_count; \			(shared)->page_lru_count[slotno] = new_lru_count; \		} \	} while (0)/* Saved info for SlruReportIOError */typedef enum{	SLRU_OPEN_FAILED,	SLRU_SEEK_FAILED,	SLRU_READ_FAILED,	SLRU_WRITE_FAILED,	SLRU_FSYNC_FAILED,	SLRU_CLOSE_FAILED} SlruErrorCause;static SlruErrorCause slru_errcause;static int	slru_errno;static void SimpleLruZeroLSNs(SlruCtl ctl, int slotno);static void SimpleLruWaitIO(SlruCtl ctl, int slotno);static bool SlruPhysicalReadPage(SlruCtl ctl, int pageno, int slotno);static bool SlruPhysicalWritePage(SlruCtl ctl, int pageno, int slotno,					  SlruFlush fdata);static void SlruReportIOError(SlruCtl ctl, int pageno, TransactionId xid);static int	SlruSelectLRUPage(SlruCtl ctl, int pageno);/* * Initialization of shared memory */SizeSimpleLruShmemSize(int nslots, int nlsns){	Size		sz;	/* we assume nslots isn't so large as to risk overflow */	sz = MAXALIGN(sizeof(SlruSharedData));	sz += MAXALIGN(nslots * sizeof(char *));	/* page_buffer[] */	sz += MAXALIGN(nslots * sizeof(SlruPageStatus));	/* page_status[] */	sz += MAXALIGN(nslots * sizeof(bool));		/* page_dirty[] */	sz += MAXALIGN(nslots * sizeof(int));		/* page_number[] */	sz += MAXALIGN(nslots * sizeof(int));		/* page_lru_count[] */	sz += MAXALIGN(nslots * sizeof(LWLockId));	/* buffer_locks[] */	if (nlsns > 0)		sz += MAXALIGN(nslots * nlsns * sizeof(XLogRecPtr));	/* group_lsn[] */	return BUFFERALIGN(sz) + BLCKSZ * nslots;}voidSimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns,			  LWLockId ctllock, const char *subdir){	SlruShared	shared;	bool		found;	shared = (SlruShared) ShmemInitStruct(name,										  SimpleLruShmemSize(nslots, nlsns),										  &found);	if (!IsUnderPostmaster)	{		/* Initialize locks and shared memory area */		char	   *ptr;		Size		offset;		int			slotno;		Assert(!found);		memset(shared, 0, sizeof(SlruSharedData));		shared->ControlLock = ctllock;		shared->num_slots = nslots;		shared->lsn_groups_per_page = nlsns;		shared->cur_lru_count = 0;		/* shared->latest_page_number will be set later */		ptr = (char *) shared;		offset = MAXALIGN(sizeof(SlruSharedData));		shared->page_buffer = (char **) (ptr + offset);		offset += MAXALIGN(nslots * sizeof(char *));		shared->page_status = (SlruPageStatus *) (ptr + offset);		offset += MAXALIGN(nslots * sizeof(SlruPageStatus));		shared->page_dirty = (bool *) (ptr + offset);		offset += MAXALIGN(nslots * sizeof(bool));		shared->page_number = (int *) (ptr + offset);		offset += MAXALIGN(nslots * sizeof(int));		shared->page_lru_count = (int *) (ptr + offset);		offset += MAXALIGN(nslots * sizeof(int));		shared->buffer_locks = (LWLockId *) (ptr + offset);		offset += MAXALIGN(nslots * sizeof(LWLockId));		if (nlsns > 0)		{			shared->group_lsn = (XLogRecPtr *) (ptr + offset);			offset += MAXALIGN(nslots * nlsns * sizeof(XLogRecPtr));		}		ptr += BUFFERALIGN(offset);		for (slotno = 0; slotno < nslots; slotno++)		{			shared->page_buffer[slotno] = ptr;			shared->page_status[slotno] = SLRU_PAGE_EMPTY;			shared->page_dirty[slotno] = false;			shared->page_lru_count[slotno] = 0;			shared->buffer_locks[slotno] = LWLockAssign();			ptr += BLCKSZ;		}	}	else		Assert(found);	/*	 * Initialize the unshared control struct, including directory path. We	 * assume caller set PagePrecedes.	 */	ctl->shared = shared;	ctl->do_fsync = true;		/* default behavior */	StrNCpy(ctl->Dir, subdir, sizeof(ctl->Dir));}/* * 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){	SlruShared	shared = ctl->shared;	int			slotno;	/* 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_VALID &&			!shared->page_dirty[slotno]) ||		   shared->page_number[slotno] == pageno);	/* Mark the slot as containing this page */	shared->page_number[slotno] = pageno;	shared->page_status[slotno] = SLRU_PAGE_VALID;	shared->page_dirty[slotno] = true;	SlruRecentlyUsed(shared, slotno);	/* Set the buffer to zeroes */	MemSet(shared->page_buffer[slotno], 0, BLCKSZ);	/* Set the LSNs for this new page to zero */	SimpleLruZeroLSNs(ctl, slotno);	/* Assume this page is now the latest active page */	shared->latest_page_number = pageno;	return slotno;}/* * Zero all the LSNs we store for this slru page. * * This should be called each time we create a new page, and each time we read * in a page from disk into an existing buffer.  (Such an old page cannot * have any interesting LSNs, since we'd have flushed them before writing * the page in the first place.) */static voidSimpleLruZeroLSNs(SlruCtl ctl, int slotno){	SlruShared	shared = ctl->shared;	if (shared->lsn_groups_per_page > 0)		MemSet(&shared->group_lsn[slotno * shared->lsn_groups_per_page], 0,			   shared->lsn_groups_per_page * sizeof(XLogRecPtr));}/* * Wait for any active I/O on a page slot to finish.  (This does not * guarantee that new I/O hasn't been started before we return, though. * In fact the slot might not even contain the same page anymore.) * * Control lock must be held at entry, and will be held at exit. */static voidSimpleLruWaitIO(SlruCtl ctl, int slotno){	SlruShared	shared = ctl->shared;	/* See notes at top of file */	LWLockRelease(shared->ControlLock);	LWLockAcquire(shared->buffer_locks[slotno], LW_SHARED);	LWLockRelease(shared->buffer_locks[slotno]);	LWLockAcquire(shared->ControlLock, LW_EXCLUSIVE);	/*	 * If the slot is still in an io-in-progress state, then either someone	 * already started a new I/O on the slot, or a previous I/O failed and	 * neglected to reset the page state.  That shouldn't happen, really, but	 * it seems worth a few extra cycles to check and recover from it. We can	 * cheaply test for failure by seeing if the buffer lock is still held (we	 * assume that transaction abort would release the lock).	 */	if (shared->page_status[slotno] == SLRU_PAGE_READ_IN_PROGRESS ||		shared->page_status[slotno] == SLRU_PAGE_WRITE_IN_PROGRESS)	{		if (LWLockConditionalAcquire(shared->buffer_locks[slotno], LW_SHARED))		{			/* indeed, the I/O must have failed */			if (shared->page_status[slotno] == SLRU_PAGE_READ_IN_PROGRESS)				shared->page_status[slotno] = SLRU_PAGE_EMPTY;			else	/* write_in_progress */			{				shared->page_status[slotno] = SLRU_PAGE_VALID;				shared->page_dirty[slotno] = true;			}			LWLockRelease(shared->buffer_locks[slotno]);		}	}}/* * Find a page in a shared buffer, reading it in if necessary. * The page number must correspond to an already-initialized page. * * If write_ok is true then it is OK to return a page that is in * WRITE_IN_PROGRESS state; it is the caller's responsibility to be sure * that modification of the page is safe.  If write_ok is false then we * will not return the page until it is not undergoing active I/O. * * 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 slot number now holding the page. * The buffer's LRU access info is updated. * * Control lock must be held at entry, and will be held at exit. */intSimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok,				  TransactionId xid){	SlruShared	shared = ctl->shared;	/* Outer loop handles restart if we must wait for someone else's I/O */	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 must wait for I/O.  Likewise			 * if the page is being written and the caller said that's not OK.			 */			if (shared->page_status[slotno] == SLRU_PAGE_READ_IN_PROGRESS ||				(shared->page_status[slotno] == SLRU_PAGE_WRITE_IN_PROGRESS &&				 !write_ok))			{				SimpleLruWaitIO(ctl, slotno);				/* Now we must recheck state from the top */				continue;			}

⌨️ 快捷键说明

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