Go to the source code of this file.
Data Structures | |
struct | PGShmemHeader |
Defines | |
#define | PGShmemMagic 679834894 |
Typedefs | |
typedef struct PGShmemHeader | PGShmemHeader |
Functions | |
PGShmemHeader * | PGSharedMemoryCreate (Size size, bool makePrivate, int port) |
bool | PGSharedMemoryIsInUse (unsigned long id1, unsigned long id2) |
void | PGSharedMemoryDetach (void) |
#define PGShmemMagic 679834894 |
Definition at line 30 of file pg_shmem.h.
Referenced by PGSharedMemoryAttach(), PGSharedMemoryIsInUse(), and PGSharedMemoryReAttach().
typedef struct PGShmemHeader PGShmemHeader |
PGShmemHeader* PGSharedMemoryCreate | ( | Size | size, | |
bool | makePrivate, | |||
int | port | |||
) |
Definition at line 364 of file sysv_shmem.c.
References AnonymousShmem, AnonymousShmemSize, Assert, PGShmemHeader::creatorPID, DataDir, PGShmemHeader::device, elog, ereport, errcode_for_file_access(), errdetail(), errhint(), errmsg(), FATAL, free, PGShmemHeader::freeoffset, GetSharedMemName(), i, PGShmemHeader::inode, InternalIpcMemoryCreate(), IPC_RMID, LOG, PGShmemHeader::magic, MAP_FAILED, MAXALIGN, NULL, on_shmem_exit(), PG_MMAP_FLAGS, PGSharedMemoryAttach(), pgwin32_SharedMemoryDelete(), PointerGetDatum, PGShmemHeader::totalsize, TRUE, UsedShmemSegAddr, UsedShmemSegID, and UsedShmemSegSize.
Referenced by CreateSharedMemoryAndSemaphores(), and main().
{ IpcMemoryKey NextShmemSegID; void *memAddress; PGShmemHeader *hdr; IpcMemoryId shmid; struct stat statbuf; Size sysvsize = size; /* Room for a header? */ Assert(size > MAXALIGN(sizeof(PGShmemHeader))); /* * As of PostgreSQL 9.3, we normally allocate only a very small amount of * System V shared memory, and only for the purposes of providing an * interlock to protect the data directory. The real shared memory block * is allocated using mmap(). This works around the problem that many * systems have very low limits on the amount of System V shared memory * that can be allocated. Even a limit of a few megabytes will be enough * to run many copies of PostgreSQL without needing to adjust system * settings. * * However, we disable this logic in the EXEC_BACKEND case, and fall back * to the old method of allocating the entire segment using System V shared * memory, because there's no way to attach an mmap'd segment to a process * after exec(). Since EXEC_BACKEND is intended only for developer use, * this shouldn't be a big problem. */ #ifndef EXEC_BACKEND { long pagesize = sysconf(_SC_PAGE_SIZE); /* * Ensure request size is a multiple of pagesize. * * pagesize will, for practical purposes, always be a power of two. * But just in case it isn't, we do it this way instead of using * TYPEALIGN(). */ if (pagesize > 0 && size % pagesize != 0) size += pagesize - (size % pagesize); /* * We assume that no one will attempt to run PostgreSQL 9.3 or later * on systems that are ancient enough that anonymous shared memory is * not supported, such as pre-2.4 versions of Linux. If that turns out * to be false, we might need to add a run-time test here and do this * only if the running kernel supports it. */ AnonymousShmem = mmap(NULL, size, PROT_READ|PROT_WRITE, PG_MMAP_FLAGS, -1, 0); if (AnonymousShmem == MAP_FAILED) ereport(FATAL, (errmsg("could not map anonymous shared memory: %m"), (errno == ENOMEM) ? errhint("This error usually means that PostgreSQL's request " "for a shared memory segment exceeded available memory " "or swap space. To reduce the request size (currently " "%lu bytes), reduce PostgreSQL's shared memory usage, " "perhaps by reducing shared_buffers or " "max_connections.", (unsigned long) size) : 0)); AnonymousShmemSize = size; /* Now we need only allocate a minimal-sized SysV shmem block. */ sysvsize = sizeof(PGShmemHeader); } #endif /* Make sure PGSharedMemoryAttach doesn't fail without need */ UsedShmemSegAddr = NULL; /* Loop till we find a free IPC key */ NextShmemSegID = port * 1000; for (NextShmemSegID++;; NextShmemSegID++) { /* Try to create new segment */ memAddress = InternalIpcMemoryCreate(NextShmemSegID, sysvsize); if (memAddress) break; /* successful create and attach */ /* Check shared memory and possibly remove and recreate */ if (makePrivate) /* a standalone backend shouldn't do this */ continue; if ((memAddress = PGSharedMemoryAttach(NextShmemSegID, &shmid)) == NULL) continue; /* can't attach, not one of mine */ /* * If I am not the creator and it belongs to an extant process, * continue. */ hdr = (PGShmemHeader *) memAddress; if (hdr->creatorPID != getpid()) { if (kill(hdr->creatorPID, 0) == 0 || errno != ESRCH) { shmdt(memAddress); continue; /* segment belongs to a live process */ } } /* * The segment appears to be from a dead Postgres process, or from a * previous cycle of life in this same process. Zap it, if possible. * This probably shouldn't fail, but if it does, assume the segment * belongs to someone else after all, and continue quietly. */ shmdt(memAddress); if (shmctl(shmid, IPC_RMID, NULL) < 0) continue; /* * Now try again to create the segment. */ memAddress = InternalIpcMemoryCreate(NextShmemSegID, sysvsize); if (memAddress) break; /* successful create and attach */ /* * Can only get here if some other process managed to create the same * shmem key before we did. Let him have that one, loop around to try * next key. */ } /* * OK, we created a new segment. Mark it as created by this process. The * order of assignments here is critical so that another Postgres process * can't see the header as valid but belonging to an invalid PID! */ hdr = (PGShmemHeader *) memAddress; hdr->creatorPID = getpid(); hdr->magic = PGShmemMagic; /* Fill in the data directory ID info, too */ if (stat(DataDir, &statbuf) < 0) ereport(FATAL, (errcode_for_file_access(), errmsg("could not stat data directory \"%s\": %m", DataDir))); hdr->device = statbuf.st_dev; hdr->inode = statbuf.st_ino; /* * Initialize space allocation status for segment. */ hdr->totalsize = size; hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader)); /* Save info for possible future use */ UsedShmemSegAddr = memAddress; UsedShmemSegID = (unsigned long) NextShmemSegID; /* * If AnonymousShmem is NULL here, then we're not using anonymous shared * memory, and should return a pointer to the System V shared memory block. * Otherwise, the System V shared memory block is only a shim, and we must * return a pointer to the real block. */ if (AnonymousShmem == NULL) return hdr; memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader)); return (PGShmemHeader *) AnonymousShmem; }
void PGSharedMemoryDetach | ( | void | ) |
Definition at line 584 of file sysv_shmem.c.
References AnonymousShmem, AnonymousShmemSize, elog, LOG, NULL, and UsedShmemSegAddr.
Referenced by pgarch_start(), pgstat_start(), pgwin32_SharedMemoryDelete(), and SysLogger_Start().
{ if (UsedShmemSegAddr != NULL) { if ((shmdt(UsedShmemSegAddr) < 0) #if defined(EXEC_BACKEND) && defined(__CYGWIN__) /* Work-around for cygipc exec bug */ && shmdt(NULL) < 0 #endif ) elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr); UsedShmemSegAddr = NULL; } /* Release anonymous shared memory block, if any. */ if (AnonymousShmem != NULL && munmap(AnonymousShmem, AnonymousShmemSize) < 0) elog(LOG, "munmap(%p) failed: %m", AnonymousShmem); }
bool PGSharedMemoryIsInUse | ( | unsigned long | id1, | |
unsigned long | id2 | |||
) |
Definition at line 260 of file sysv_shmem.c.
References DataDir, EIDRM, FALSE, free, GetSharedMemName(), IPC_STAT, NULL, PG_SHMAT_FLAGS, and PGShmemMagic.
Referenced by CreateLockFile().
{ IpcMemoryId shmId = (IpcMemoryId) id2; struct shmid_ds shmStat; struct stat statbuf; PGShmemHeader *hdr; /* * We detect whether a shared memory segment is in use by seeing whether * it (a) exists and (b) has any processes attached to it. */ if (shmctl(shmId, IPC_STAT, &shmStat) < 0) { /* * EINVAL actually has multiple possible causes documented in the * shmctl man page, but we assume it must mean the segment no longer * exists. */ if (errno == EINVAL) return false; /* * EACCES implies that the segment belongs to some other userid, which * means it is not a Postgres shmem segment (or at least, not one that * is relevant to our data directory). */ if (errno == EACCES) return false; /* * Some Linux kernel versions (in fact, all of them as of July 2007) * sometimes return EIDRM when EINVAL is correct. The Linux kernel * actually does not have any internal state that would justify * returning EIDRM, so we can get away with assuming that EIDRM is * equivalent to EINVAL on that platform. */ #ifdef HAVE_LINUX_EIDRM_BUG if (errno == EIDRM) return false; #endif /* * Otherwise, we had better assume that the segment is in use. The * only likely case is EIDRM, which implies that the segment has been * IPC_RMID'd but there are still processes attached to it. */ return true; } /* If it has no attached processes, it's not in use */ if (shmStat.shm_nattch == 0) return false; /* * Try to attach to the segment and see if it matches our data directory. * This avoids shmid-conflict problems on machines that are running * several postmasters under the same userid. */ if (stat(DataDir, &statbuf) < 0) return true; /* if can't stat, be conservative */ hdr = (PGShmemHeader *) shmat(shmId, NULL, PG_SHMAT_FLAGS); if (hdr == (PGShmemHeader *) -1) return true; /* if can't attach, be conservative */ if (hdr->magic != PGShmemMagic || hdr->device != statbuf.st_dev || hdr->inode != statbuf.st_ino) { /* * It's either not a Postgres segment, or not one for my data * directory. In either case it poses no threat. */ shmdt((void *) hdr); return false; } /* Trouble --- looks a lot like there's still live backends */ shmdt((void *) hdr); return true; }