1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-02 09:02:37 +03:00

Consistently test for in-use shared memory.

postmaster startup scrutinizes any shared memory segment recorded in
postmaster.pid, exiting if that segment matches the current data
directory and has an attached process.  When the postmaster.pid file was
missing, a starting postmaster used weaker checks.  Change to use the
same checks in both scenarios.  This increases the chance of a startup
failure, in lieu of data corruption, if the DBA does "kill -9 `head -n1
postmaster.pid` && rm postmaster.pid && pg_ctl -w start".  A postmaster
will no longer stop if shmat() of an old segment fails with EACCES.  A
postmaster will no longer recycle segments pertaining to other data
directories.  That's good for production, but it's bad for integration
tests that crash a postmaster and immediately delete its data directory.
Such a test now leaks a segment indefinitely.  No "make check-world"
test does that.  win32_shmem.c already avoided all these problems.  In
9.6 and later, enhance PostgresNode to facilitate testing.  Back-patch
to 9.4 (all supported versions).

Reviewed (in earlier versions) by Daniel Gustafsson and Kyotaro HORIGUCHI.

Discussion: https://postgr.es/m/20190408064141.GA2016666@rfd.leadboat.com
This commit is contained in:
Noah Misch
2019-04-12 22:36:38 -07:00
parent 67f6e645e7
commit c565de6431
7 changed files with 177 additions and 143 deletions

View File

@ -70,6 +70,26 @@
typedef key_t IpcMemoryKey; /* shared memory key passed to shmget(2) */ typedef key_t IpcMemoryKey; /* shared memory key passed to shmget(2) */
typedef int IpcMemoryId; /* shared memory ID returned by shmget(2) */ typedef int IpcMemoryId; /* shared memory ID returned by shmget(2) */
/*
* How does a given IpcMemoryId relate to this PostgreSQL process?
*
* One could recycle unattached segments of different data directories if we
* distinguished that case from other SHMSTATE_FOREIGN cases. Doing so would
* cause us to visit less of the key space, making us less likely to detect a
* SHMSTATE_ATTACHED key. It would also complicate the concurrency analysis,
* in that postmasters of different data directories could simultaneously
* attempt to recycle a given key. We'll waste keys longer in some cases, but
* avoiding the problems of the alternative justifies that loss.
*/
typedef enum
{
SHMSTATE_ANALYSIS_FAILURE, /* unexpected failure to analyze the ID */
SHMSTATE_ATTACHED, /* pertinent to DataDir, has attached PIDs */
SHMSTATE_ENOENT, /* no segment of that ID */
SHMSTATE_FOREIGN, /* exists, but not pertinent to DataDir */
SHMSTATE_UNATTACHED /* pertinent to DataDir, no attached PIDs */
} IpcMemoryState;
unsigned long UsedShmemSegID = 0; unsigned long UsedShmemSegID = 0;
void *UsedShmemSegAddr = NULL; void *UsedShmemSegAddr = NULL;
@ -82,8 +102,8 @@ static void *AnonymousShmem = NULL;
static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size); static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
static void IpcMemoryDetach(int status, Datum shmaddr); static void IpcMemoryDetach(int status, Datum shmaddr);
static void IpcMemoryDelete(int status, Datum shmId); static void IpcMemoryDelete(int status, Datum shmId);
static PGShmemHeader *PGSharedMemoryAttach(IpcMemoryKey key, static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
IpcMemoryId *shmid); PGShmemHeader **addr);
/* /*
@ -287,11 +307,36 @@ IpcMemoryDelete(int status, Datum shmId)
bool bool
PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2) PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
{ {
IpcMemoryId shmId = (IpcMemoryId) id2; PGShmemHeader *memAddress;
IpcMemoryState state;
state = PGSharedMemoryAttach((IpcMemoryId) id2, &memAddress);
if (memAddress && shmdt(memAddress) < 0)
elog(LOG, "shmdt(%p) failed: %m", memAddress);
switch (state)
{
case SHMSTATE_ENOENT:
case SHMSTATE_FOREIGN:
case SHMSTATE_UNATTACHED:
return false;
case SHMSTATE_ANALYSIS_FAILURE:
case SHMSTATE_ATTACHED:
return true;
}
return true;
}
/* See comment at IpcMemoryState. */
static IpcMemoryState
PGSharedMemoryAttach(IpcMemoryId shmId,
PGShmemHeader **addr)
{
struct shmid_ds shmStat; struct shmid_ds shmStat;
struct stat statbuf; struct stat statbuf;
PGShmemHeader *hdr; PGShmemHeader *hdr;
*addr = NULL;
/* /*
* We detect whether a shared memory segment is in use by seeing whether * We detect whether a shared memory segment is in use by seeing whether
* it (a) exists and (b) has any processes attached to it. * it (a) exists and (b) has any processes attached to it.
@ -304,15 +349,15 @@ PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
* exists. * exists.
*/ */
if (errno == EINVAL) if (errno == EINVAL)
return false; return SHMSTATE_ENOENT;
/* /*
* EACCES implies that the segment belongs to some other userid, which * EACCES implies we have no read permission, which means it is not a
* means it is not a Postgres shmem segment (or at least, not one that * Postgres shmem segment (or at least, not one that is relevant to
* is relevant to our data directory). * our data directory).
*/ */
if (errno == EACCES) if (errno == EACCES)
return false; return SHMSTATE_FOREIGN;
/* /*
* Some Linux kernel versions (in fact, all of them as of July 2007) * Some Linux kernel versions (in fact, all of them as of July 2007)
@ -323,7 +368,7 @@ PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
*/ */
#ifdef HAVE_LINUX_EIDRM_BUG #ifdef HAVE_LINUX_EIDRM_BUG
if (errno == EIDRM) if (errno == EIDRM)
return false; return SHMSTATE_ENOENT;
#endif #endif
/* /*
@ -331,25 +376,32 @@ PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
* only likely case is EIDRM, which implies that the segment has been * only likely case is EIDRM, which implies that the segment has been
* IPC_RMID'd but there are still processes attached to it. * IPC_RMID'd but there are still processes attached to it.
*/ */
return true; return SHMSTATE_ANALYSIS_FAILURE;
} }
/* 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. * Try to attach to the segment and see if it matches our data directory.
* This avoids shmid-conflict problems on machines that are running * This avoids shmid-conflict problems on machines that are running
* several postmasters under the same userid. * several postmasters under the same userid.
*/ */
if (stat(DataDir, &statbuf) < 0) if (stat(DataDir, &statbuf) < 0)
return true; /* if can't stat, be conservative */ return SHMSTATE_ANALYSIS_FAILURE; /* can't stat; be conservative */
hdr = (PGShmemHeader *) shmat(shmId, NULL, PG_SHMAT_FLAGS);
/*
* Attachment fails if we have no write permission. Since that will never
* happen with Postgres IPCProtection, such a failure shows the segment is
* not a Postgres segment. If attachment fails for some other reason, be
* conservative.
*/
hdr = (PGShmemHeader *) shmat(shmId, UsedShmemSegAddr, PG_SHMAT_FLAGS);
if (hdr == (PGShmemHeader *) -1) if (hdr == (PGShmemHeader *) -1)
return true; /* if can't attach, be conservative */ {
if (errno == EACCES)
return SHMSTATE_FOREIGN;
else
return SHMSTATE_ANALYSIS_FAILURE;
}
*addr = hdr;
if (hdr->magic != PGShmemMagic || if (hdr->magic != PGShmemMagic ||
hdr->device != statbuf.st_dev || hdr->device != statbuf.st_dev ||
@ -357,16 +409,12 @@ PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
{ {
/* /*
* It's either not a Postgres segment, or not one for my data * It's either not a Postgres segment, or not one for my data
* directory. In either case it poses no threat. * directory.
*/ */
shmdt((void *) hdr); return SHMSTATE_FOREIGN;
return false;
} }
/* Trouble --- looks a lot like there's still live backends */ return shmStat.shm_nattch == 0 ? SHMSTATE_UNATTACHED : SHMSTATE_ATTACHED;
shmdt((void *) hdr);
return true;
} }
#ifdef USE_ANONYMOUS_SHMEM #ifdef USE_ANONYMOUS_SHMEM
@ -542,25 +590,21 @@ AnonymousShmemDetach(int status, Datum arg)
* standard header. Also, register an on_shmem_exit callback to release * standard header. Also, register an on_shmem_exit callback to release
* the storage. * the storage.
* *
* Dead Postgres segments are recycled if found, but we do not fail upon * Dead Postgres segments pertinent to this DataDir are recycled if found, but
* collision with non-Postgres shmem segments. The idea here is to detect and * we do not fail upon collision with foreign shmem segments. The idea here
* re-use keys that may have been assigned by a crashed postmaster or backend. * is to detect and re-use keys that may have been assigned by a crashed
* * postmaster or backend.
* makePrivate means to always create a new segment, rather than attach to
* or recycle any existing segment.
* *
* The port number is passed for possible use as a key (for SysV, we use * The port number is passed for possible use as a key (for SysV, we use
* it to generate the starting shmem key). In a standalone backend, * it to generate the starting shmem key).
* zero will be passed.
*/ */
PGShmemHeader * PGShmemHeader *
PGSharedMemoryCreate(Size size, bool makePrivate, int port, PGSharedMemoryCreate(Size size, int port,
PGShmemHeader **shim) PGShmemHeader **shim)
{ {
IpcMemoryKey NextShmemSegID; IpcMemoryKey NextShmemSegID;
void *memAddress; void *memAddress;
PGShmemHeader *hdr; PGShmemHeader *hdr;
IpcMemoryId shmid;
struct stat statbuf; struct stat statbuf;
Size sysvsize; Size sysvsize;
@ -591,11 +635,20 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port,
/* Make sure PGSharedMemoryAttach doesn't fail without need */ /* Make sure PGSharedMemoryAttach doesn't fail without need */
UsedShmemSegAddr = NULL; UsedShmemSegAddr = NULL;
/* Loop till we find a free IPC key */ /*
NextShmemSegID = port * 1000; * Loop till we find a free IPC key. Trust CreateDataDirLockFile() to
* ensure no more than one postmaster per data directory can enter this
* loop simultaneously. (CreateDataDirLockFile() does not ensure that,
* but prefer fixing it over coping here.)
*/
NextShmemSegID = 1 + port * 1000;
for (NextShmemSegID++;; NextShmemSegID++) for (;;)
{ {
IpcMemoryId shmid;
PGShmemHeader *oldhdr;
IpcMemoryState state;
/* Try to create new segment */ /* Try to create new segment */
memAddress = InternalIpcMemoryCreate(NextShmemSegID, sysvsize); memAddress = InternalIpcMemoryCreate(NextShmemSegID, sysvsize);
if (memAddress) if (memAddress)
@ -603,58 +656,71 @@ PGSharedMemoryCreate(Size size, bool makePrivate, int port,
/* Check shared memory and possibly remove and recreate */ /* Check shared memory and possibly remove and recreate */
if (makePrivate) /* a standalone backend shouldn't do this */ /*
continue; * shmget() failure is typically EACCES, hence SHMSTATE_FOREIGN.
* ENOENT, a narrow possibility, implies SHMSTATE_ENOENT, but one can
* safely treat SHMSTATE_ENOENT like SHMSTATE_FOREIGN.
*/
shmid = shmget(NextShmemSegID, sizeof(PGShmemHeader), 0);
if (shmid < 0)
{
oldhdr = NULL;
state = SHMSTATE_FOREIGN;
}
else
state = PGSharedMemoryAttach(shmid, &oldhdr);
if ((memAddress = PGSharedMemoryAttach(NextShmemSegID, &shmid)) == NULL) switch (state)
continue; /* can't attach, not one of mine */ {
case SHMSTATE_ANALYSIS_FAILURE:
case SHMSTATE_ATTACHED:
ereport(FATAL,
(errcode(ERRCODE_LOCK_FILE_EXISTS),
errmsg("pre-existing shared memory block (key %lu, ID %lu) is still in use",
(unsigned long) NextShmemSegID,
(unsigned long) shmid),
errhint("Terminate any old server processes associated with data directory \"%s\".",
DataDir)));
break;
case SHMSTATE_ENOENT:
/* /*
* If I am not the creator and it belongs to an extant process, * To our surprise, some other process deleted since our last
* continue. * InternalIpcMemoryCreate(). Moments earlier, we would have
* seen SHMSTATE_FOREIGN. Try that same ID again.
*/ */
hdr = (PGShmemHeader *) memAddress; elog(LOG,
if (hdr->creatorPID != getpid()) "shared memory block (key %lu, ID %lu) deleted during startup",
{ (unsigned long) NextShmemSegID,
if (kill(hdr->creatorPID, 0) == 0 || errno != ESRCH) (unsigned long) shmid);
{ break;
shmdt(memAddress); case SHMSTATE_FOREIGN:
continue; /* segment belongs to a live process */ NextShmemSegID++;
} break;
} case SHMSTATE_UNATTACHED:
/* /*
* The segment appears to be from a dead Postgres process, or from a * The segment pertains to DataDir, and every process that had
* previous cycle of life in this same process. Zap it, if possible, * used it has died or detached. Zap it, if possible, and any
* and any associated dynamic shared memory segments, as well. This * associated dynamic shared memory segments, as well. This
* probably shouldn't fail, but if it does, assume the segment belongs * shouldn't fail, but if it does, assume the segment belongs
* to someone else after all, and continue quietly. * to someone else after all, and try the next candidate.
* Otherwise, try again to create the segment. That may fail
* if some other process creates the same shmem key before we
* do, in which case we'll try the next key.
*/ */
if (hdr->dsm_control != 0) if (oldhdr->dsm_control != 0)
dsm_cleanup_using_control_segment(hdr->dsm_control); dsm_cleanup_using_control_segment(oldhdr->dsm_control);
shmdt(memAddress);
if (shmctl(shmid, IPC_RMID, NULL) < 0) if (shmctl(shmid, IPC_RMID, NULL) < 0)
continue; NextShmemSegID++;
break;
/*
* 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.
*/
} }
/* if (oldhdr && shmdt(oldhdr) < 0)
* OK, we created a new segment. Mark it as created by this process. The elog(LOG, "shmdt(%p) failed: %m", oldhdr);
* order of assignments here is critical so that another Postgres process }
* can't see the header as valid but belonging to an invalid PID!
*/ /* Initialize new segment. */
hdr = (PGShmemHeader *) memAddress; hdr = (PGShmemHeader *) memAddress;
hdr->creatorPID = getpid(); hdr->creatorPID = getpid();
hdr->magic = PGShmemMagic; hdr->magic = PGShmemMagic;
@ -714,7 +780,8 @@ void
PGSharedMemoryReAttach(void) PGSharedMemoryReAttach(void)
{ {
IpcMemoryId shmid; IpcMemoryId shmid;
void *hdr; PGShmemHeader *hdr;
IpcMemoryState state;
void *origUsedShmemSegAddr = UsedShmemSegAddr; void *origUsedShmemSegAddr = UsedShmemSegAddr;
Assert(UsedShmemSegAddr != NULL); Assert(UsedShmemSegAddr != NULL);
@ -727,14 +794,18 @@ PGSharedMemoryReAttach(void)
#endif #endif
elog(DEBUG3, "attaching to %p", UsedShmemSegAddr); elog(DEBUG3, "attaching to %p", UsedShmemSegAddr);
hdr = (void *) PGSharedMemoryAttach((IpcMemoryKey) UsedShmemSegID, &shmid); shmid = shmget(UsedShmemSegID, sizeof(PGShmemHeader), 0);
if (hdr == NULL) if (shmid < 0)
state = SHMSTATE_FOREIGN;
else
state = PGSharedMemoryAttach(shmid, &hdr);
if (state != SHMSTATE_ATTACHED)
elog(FATAL, "could not reattach to shared memory (key=%d, addr=%p): %m", elog(FATAL, "could not reattach to shared memory (key=%d, addr=%p): %m",
(int) UsedShmemSegID, UsedShmemSegAddr); (int) UsedShmemSegID, UsedShmemSegAddr);
if (hdr != origUsedShmemSegAddr) if (hdr != origUsedShmemSegAddr)
elog(FATAL, "reattaching to shared memory returned unexpected address (got %p, expected %p)", elog(FATAL, "reattaching to shared memory returned unexpected address (got %p, expected %p)",
hdr, origUsedShmemSegAddr); hdr, origUsedShmemSegAddr);
dsm_set_control_handle(((PGShmemHeader *) hdr)->dsm_control); dsm_set_control_handle(hdr->dsm_control);
UsedShmemSegAddr = hdr; /* probably redundant */ UsedShmemSegAddr = hdr; /* probably redundant */
} }
@ -810,31 +881,3 @@ PGSharedMemoryDetach(void)
} }
#endif #endif
} }
/*
* Attach to shared memory and make sure it has a Postgres header
*
* Returns attach address if OK, else NULL
*/
static PGShmemHeader *
PGSharedMemoryAttach(IpcMemoryKey key, IpcMemoryId *shmid)
{
PGShmemHeader *hdr;
if ((*shmid = shmget(key, sizeof(PGShmemHeader), 0)) < 0)
return NULL;
hdr = (PGShmemHeader *) shmat(*shmid, UsedShmemSegAddr, PG_SHMAT_FLAGS);
if (hdr == (PGShmemHeader *) -1)
return NULL; /* failed: must be some other app's */
if (hdr->magic != PGShmemMagic)
{
shmdt((void *) hdr);
return NULL; /* segment belongs to a non-Postgres app */
}
return hdr;
}

View File

@ -131,14 +131,9 @@ PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
* *
* Create a shared memory segment of the given size and initialize its * Create a shared memory segment of the given size and initialize its
* standard header. * standard header.
*
* makePrivate means to always create a new segment, rather than attach to
* or recycle any existing segment. On win32, we always create a new segment,
* since there is no need for recycling (segments go away automatically
* when the last backend exits)
*/ */
PGShmemHeader * PGShmemHeader *
PGSharedMemoryCreate(Size size, bool makePrivate, int port, PGSharedMemoryCreate(Size size, int port,
PGShmemHeader **shim) PGShmemHeader **shim)
{ {
void *memAddress; void *memAddress;

View File

@ -2518,7 +2518,7 @@ reset_shared(int port)
* determine IPC keys. This helps ensure that we will clean up dead IPC * determine IPC keys. This helps ensure that we will clean up dead IPC
* objects if the postmaster crashes and is restarted. * objects if the postmaster crashes and is restarted.
*/ */
CreateSharedMemoryAndSemaphores(false, port); CreateSharedMemoryAndSemaphores(port);
} }
@ -4805,7 +4805,7 @@ SubPostmasterMain(int argc, char *argv[])
InitProcess(); InitProcess();
/* Attach process to shared data structures */ /* Attach process to shared data structures */
CreateSharedMemoryAndSemaphores(false, 0); CreateSharedMemoryAndSemaphores(0);
/* And run the backend */ /* And run the backend */
BackendRun(&port); /* does not return */ BackendRun(&port); /* does not return */
@ -4819,7 +4819,7 @@ SubPostmasterMain(int argc, char *argv[])
InitAuxiliaryProcess(); InitAuxiliaryProcess();
/* Attach process to shared data structures */ /* Attach process to shared data structures */
CreateSharedMemoryAndSemaphores(false, 0); CreateSharedMemoryAndSemaphores(0);
AuxiliaryProcessMain(argc - 2, argv + 2); /* does not return */ AuxiliaryProcessMain(argc - 2, argv + 2); /* does not return */
} }
@ -4832,7 +4832,7 @@ SubPostmasterMain(int argc, char *argv[])
InitProcess(); InitProcess();
/* Attach process to shared data structures */ /* Attach process to shared data structures */
CreateSharedMemoryAndSemaphores(false, 0); CreateSharedMemoryAndSemaphores(0);
AutoVacLauncherMain(argc - 2, argv + 2); /* does not return */ AutoVacLauncherMain(argc - 2, argv + 2); /* does not return */
} }
@ -4845,7 +4845,7 @@ SubPostmasterMain(int argc, char *argv[])
InitProcess(); InitProcess();
/* Attach process to shared data structures */ /* Attach process to shared data structures */
CreateSharedMemoryAndSemaphores(false, 0); CreateSharedMemoryAndSemaphores(0);
AutoVacWorkerMain(argc - 2, argv + 2); /* does not return */ AutoVacWorkerMain(argc - 2, argv + 2); /* does not return */
} }
@ -4863,7 +4863,7 @@ SubPostmasterMain(int argc, char *argv[])
InitProcess(); InitProcess();
/* Attach process to shared data structures */ /* Attach process to shared data structures */
CreateSharedMemoryAndSemaphores(false, 0); CreateSharedMemoryAndSemaphores(0);
/* Fetch MyBgworkerEntry from shared memory */ /* Fetch MyBgworkerEntry from shared memory */
shmem_slot = atoi(argv[1] + 15); shmem_slot = atoi(argv[1] + 15);

View File

@ -85,12 +85,9 @@ RequestAddinShmemSpace(Size size)
* through the same code as before. (Note that the called routines mostly * through the same code as before. (Note that the called routines mostly
* check IsUnderPostmaster, rather than EXEC_BACKEND, to detect this case. * check IsUnderPostmaster, rather than EXEC_BACKEND, to detect this case.
* This is a bit code-wasteful and could be cleaned up.) * This is a bit code-wasteful and could be cleaned up.)
*
* If "makePrivate" is true then we only need private memory, not shared
* memory. This is true for a standalone backend, false for a postmaster.
*/ */
void void
CreateSharedMemoryAndSemaphores(bool makePrivate, int port) CreateSharedMemoryAndSemaphores(int port)
{ {
PGShmemHeader *shim = NULL; PGShmemHeader *shim = NULL;
@ -155,7 +152,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
/* /*
* Create the shmem segment * Create the shmem segment
*/ */
seghdr = PGSharedMemoryCreate(size, makePrivate, port, &shim); seghdr = PGSharedMemoryCreate(size, port, &shim);
InitShmemAccess(seghdr); InitShmemAccess(seghdr);
@ -170,12 +167,9 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
{ {
/* /*
* We are reattaching to an existing shared memory segment. This * We are reattaching to an existing shared memory segment. This
* should only be reached in the EXEC_BACKEND case, and even then only * should only be reached in the EXEC_BACKEND case.
* with makePrivate == false.
*/ */
#ifdef EXEC_BACKEND #ifndef EXEC_BACKEND
Assert(!makePrivate);
#else
elog(PANIC, "should be attached to shared memory already"); elog(PANIC, "should be attached to shared memory already");
#endif #endif
} }

View File

@ -402,9 +402,11 @@ InitCommunication(void)
{ {
/* /*
* We're running a postgres bootstrap process or a standalone backend. * We're running a postgres bootstrap process or a standalone backend.
* Create private "shmem" and semaphores. * Though we won't listen on PostPortNumber, use it to select a shmem
* key. This increases the chance of detecting a leftover live
* backend of this DataDir.
*/ */
CreateSharedMemoryAndSemaphores(true, 0); CreateSharedMemoryAndSemaphores(PostPortNumber);
} }
} }

View File

@ -75,6 +75,6 @@ extern void on_exit_reset(void);
/* ipci.c */ /* ipci.c */
extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook; extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
extern void CreateSharedMemoryAndSemaphores(bool makePrivate, int port); extern void CreateSharedMemoryAndSemaphores(int port);
#endif /* IPC_H */ #endif /* IPC_H */

View File

@ -30,7 +30,7 @@ typedef struct PGShmemHeader /* standard header for all Postgres shmem */
{ {
int32 magic; /* magic # to identify Postgres segments */ int32 magic; /* magic # to identify Postgres segments */
#define PGShmemMagic 679834894 #define PGShmemMagic 679834894
pid_t creatorPID; /* PID of creating process */ pid_t creatorPID; /* PID of creating process (set but unread) */
Size totalsize; /* total size of segment */ Size totalsize; /* total size of segment */
Size freeoffset; /* offset to first free space */ Size freeoffset; /* offset to first free space */
dsm_handle dsm_control; /* ID of dynamic shared memory control seg */ dsm_handle dsm_control; /* ID of dynamic shared memory control seg */
@ -65,8 +65,8 @@ extern void PGSharedMemoryReAttach(void);
extern void PGSharedMemoryNoReAttach(void); extern void PGSharedMemoryNoReAttach(void);
#endif #endif
extern PGShmemHeader *PGSharedMemoryCreate(Size size, bool makePrivate, extern PGShmemHeader *PGSharedMemoryCreate(Size size, int port,
int port, PGShmemHeader **shim); PGShmemHeader **shim);
extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2); extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
extern void PGSharedMemoryDetach(void); extern void PGSharedMemoryDetach(void);