1 /*-------------------------------------------------------------------------
4 * Implement shared memory using SysV facilities
6 * These routines represent a fairly thin layer on top of SysV shared
7 * memory functionality.
9 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
10 * Portions Copyright (c) 1994, Regents of the University of California
13 * $PostgreSQL: pgsql/src/backend/port/sysv_shmem.c,v 1.43 2005/08/20 23:26:13 tgl Exp $
15 *-------------------------------------------------------------------------
30 #ifdef HAVE_KERNEL_OS_H
31 #include <kernel/OS.h>
34 #include "miscadmin.h"
35 #include "storage/ipc.h"
36 #include "storage/pg_shmem.h"
39 typedef key_t IpcMemoryKey; /* shared memory key passed to shmget(2) */
40 typedef int IpcMemoryId; /* shared memory ID returned by shmget(2) */
42 #define IPCProtection (0600) /* access/modify by user only */
44 #ifdef SHM_SHARE_MMU /* use intimate shared memory on Solaris */
45 #define PG_SHMAT_FLAGS SHM_SHARE_MMU
47 #define PG_SHMAT_FLAGS 0
51 unsigned long UsedShmemSegID = 0;
52 void *UsedShmemSegAddr = NULL;
54 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
55 static void IpcMemoryDetach(int status, Datum shmaddr);
56 static void IpcMemoryDelete(int status, Datum shmId);
57 static PGShmemHeader *PGSharedMemoryAttach(IpcMemoryKey key,
62 * InternalIpcMemoryCreate(memKey, size)
64 * Attempt to create a new shared memory segment with the specified key.
65 * Will fail (return NULL) if such a segment already exists. If successful,
66 * attach the segment to the current process and return its attached address.
67 * On success, callbacks are registered with on_shmem_exit to detach and
68 * delete the segment when on_shmem_exit is called.
70 * If we fail with a failure code other than collision-with-existing-segment,
71 * print out an error and abort. Other types of errors are not recoverable.
74 InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size)
79 shmid = shmget(memKey, size, IPC_CREAT | IPC_EXCL | IPCProtection);
84 * Fail quietly if error indicates a collision with existing
85 * segment. One would expect EEXIST, given that we said IPC_EXCL,
86 * but perhaps we could get a permission violation instead? Also,
87 * EIDRM might occur if an old seg is slated for destruction but
90 if (errno == EEXIST || errno == EACCES
98 * Else complain and abort
101 (errmsg("could not create shared memory segment: %m"),
102 errdetail("Failed system call was shmget(key=%lu, size=%lu, 0%o).",
103 (unsigned long) memKey, (unsigned long) size,
104 IPC_CREAT | IPC_EXCL | IPCProtection),
106 errhint("This error usually means that PostgreSQL's request for a shared memory "
107 "segment exceeded your kernel's SHMMAX parameter. You can either "
108 "reduce the request size or reconfigure the kernel with larger SHMMAX. "
109 "To reduce the request size (currently %lu bytes), reduce "
110 "PostgreSQL's shared_buffers parameter (currently %d) and/or "
111 "its max_connections parameter (currently %d).\n"
112 "If the request size is already small, it's possible that it is less than "
113 "your kernel's SHMMIN parameter, in which case raising the request size or "
114 "reconfiguring SHMMIN is called for.\n"
115 "The PostgreSQL documentation contains more information about shared "
116 "memory configuration.",
117 (unsigned long) size, NBuffers, MaxBackends) : 0,
119 errhint("This error usually means that PostgreSQL's request for a shared "
120 "memory segment exceeded available memory or swap space. "
121 "To reduce the request size (currently %lu bytes), reduce "
122 "PostgreSQL's shared_buffers parameter (currently %d) and/or "
123 "its max_connections parameter (currently %d).\n"
124 "The PostgreSQL documentation contains more information about shared "
125 "memory configuration.",
126 (unsigned long) size, NBuffers, MaxBackends) : 0,
128 errhint("This error does *not* mean that you have run out of disk space. "
129 "It occurs either if all available shared memory IDs have been taken, "
130 "in which case you need to raise the SHMMNI parameter in your kernel, "
131 "or because the system's overall limit for shared memory has been "
132 "reached. If you cannot increase the shared memory limit, "
133 "reduce PostgreSQL's shared memory request (currently %lu bytes), "
134 "by reducing its shared_buffers parameter (currently %d) and/or "
135 "its max_connections parameter (currently %d).\n"
136 "The PostgreSQL documentation contains more information about shared "
137 "memory configuration.",
138 (unsigned long) size, NBuffers, MaxBackends) : 0));
141 /* Register on-exit routine to delete the new segment */
142 on_shmem_exit(IpcMemoryDelete, Int32GetDatum(shmid));
144 /* OK, should be able to attach to the segment */
145 memAddress = shmat(shmid, NULL, PG_SHMAT_FLAGS);
147 if (memAddress == (void *) -1)
148 elog(FATAL, "shmat(id=%d) failed: %m", shmid);
150 /* Register on-exit routine to detach new segment before deleting */
151 on_shmem_exit(IpcMemoryDetach, PointerGetDatum(memAddress));
153 /* Record key and ID in lockfile for data directory. */
154 RecordSharedMemoryInLockFile((unsigned long) memKey,
155 (unsigned long) shmid);
160 /****************************************************************************/
161 /* IpcMemoryDetach(status, shmaddr) removes a shared memory segment */
162 /* from process' address spaceq */
163 /* (called as an on_shmem_exit callback, hence funny argument list) */
164 /****************************************************************************/
166 IpcMemoryDetach(int status, Datum shmaddr)
168 if (shmdt(DatumGetPointer(shmaddr)) < 0)
169 elog(LOG, "shmdt(%p) failed: %m", DatumGetPointer(shmaddr));
172 /****************************************************************************/
173 /* IpcMemoryDelete(status, shmId) deletes a shared memory segment */
174 /* (called as an on_shmem_exit callback, hence funny argument list) */
175 /****************************************************************************/
177 IpcMemoryDelete(int status, Datum shmId)
179 if (shmctl(DatumGetInt32(shmId), IPC_RMID, NULL) < 0)
180 elog(LOG, "shmctl(%d, %d, 0) failed: %m",
181 DatumGetInt32(shmId), IPC_RMID);
185 * PGSharedMemoryIsInUse
187 * Is a previously-existing shmem segment still existing and in use?
189 * The point of this exercise is to detect the case where a prior postmaster
190 * crashed, but it left child backends that are still running. Therefore
191 * we only care about shmem segments that are associated with the intended
192 * DataDir. This is an important consideration since accidental matches of
193 * shmem segment IDs are reasonably common.
196 PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
198 IpcMemoryId shmId = (IpcMemoryId) id2;
199 struct shmid_ds shmStat;
206 * We detect whether a shared memory segment is in use by seeing
207 * whether it (a) exists and (b) has any processes are attached to it.
209 if (shmctl(shmId, IPC_STAT, &shmStat) < 0)
212 * EINVAL actually has multiple possible causes documented in the
213 * shmctl man page, but we assume it must mean the segment no
219 * EACCES implies that the segment belongs to some other userid,
220 * which means it is not a Postgres shmem segment (or at least,
221 * not one that is relevant to our data directory).
226 * Otherwise, we had better assume that the segment is in use.
227 * The only likely case is EIDRM, which implies that the segment
228 * has been IPC_RMID'd but there are still processes attached to it.
233 /* If it has no attached processes, it's not in use */
234 if (shmStat.shm_nattch == 0)
238 * Try to attach to the segment and see if it matches our data directory.
239 * This avoids shmid-conflict problems on machines that are running
240 * several postmasters under the same userid. On Windows, which doesn't
241 * have useful inode numbers, we can't do this so we punt and assume there
245 if (stat(DataDir, &statbuf) < 0)
246 return true; /* if can't stat, be conservative */
248 hdr = (PGShmemHeader *) shmat(shmId, NULL, PG_SHMAT_FLAGS);
250 if (hdr == (PGShmemHeader *) -1)
251 return true; /* if can't attach, be conservative */
253 if (hdr->magic != PGShmemMagic ||
254 hdr->device != statbuf.st_dev ||
255 hdr->inode != statbuf.st_ino)
258 * It's either not a Postgres segment, or not one for my data
259 * directory. In either case it poses no threat.
265 /* Trouble --- looks a lot like there's still live backends */
274 * PGSharedMemoryCreate
276 * Create a shared memory segment of the given size and initialize its
277 * standard header. Also, register an on_shmem_exit callback to release
280 * Dead Postgres segments are recycled if found, but we do not fail upon
281 * collision with non-Postgres shmem segments. The idea here is to detect and
282 * re-use keys that may have been assigned by a crashed postmaster or backend.
284 * makePrivate means to always create a new segment, rather than attach to
285 * or recycle any existing segment.
287 * The port number is passed for possible use as a key (for SysV, we use
288 * it to generate the starting shmem key). In a standalone backend,
289 * zero will be passed.
292 PGSharedMemoryCreate(Size size, bool makePrivate, int port)
294 IpcMemoryKey NextShmemSegID;
302 /* Room for a header? */
303 Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
305 /* Make sure PGSharedMemoryAttach doesn't fail without need */
306 UsedShmemSegAddr = NULL;
308 /* Loop till we find a free IPC key */
309 NextShmemSegID = port * 1000;
311 for (NextShmemSegID++;; NextShmemSegID++)
313 /* Try to create new segment */
314 memAddress = InternalIpcMemoryCreate(NextShmemSegID, size);
316 break; /* successful create and attach */
318 /* Check shared memory and possibly remove and recreate */
320 if (makePrivate) /* a standalone backend shouldn't do this */
323 if ((memAddress = PGSharedMemoryAttach(NextShmemSegID, &shmid)) == NULL)
324 continue; /* can't attach, not one of mine */
327 * If I am not the creator and it belongs to an extant process,
330 hdr = (PGShmemHeader *) memAddress;
331 if (hdr->creatorPID != getpid())
333 if (kill(hdr->creatorPID, 0) == 0 || errno != ESRCH)
336 continue; /* segment belongs to a live process */
341 * The segment appears to be from a dead Postgres process, or from
342 * a previous cycle of life in this same process. Zap it, if
343 * possible. This probably shouldn't fail, but if it does, assume
344 * the segment belongs to someone else after all, and continue
348 if (shmctl(shmid, IPC_RMID, NULL) < 0)
352 * Now try again to create the segment.
354 memAddress = InternalIpcMemoryCreate(NextShmemSegID, size);
356 break; /* successful create and attach */
359 * Can only get here if some other process managed to create the
360 * same shmem key before we did. Let him have that one, loop
361 * around to try next key.
366 * OK, we created a new segment. Mark it as created by this process.
367 * The order of assignments here is critical so that another Postgres
368 * process can't see the header as valid but belonging to an invalid
371 hdr = (PGShmemHeader *) memAddress;
372 hdr->creatorPID = getpid();
373 hdr->magic = PGShmemMagic;
376 /* Fill in the data directory ID info, too */
377 if (stat(DataDir, &statbuf) < 0)
379 (errcode_for_file_access(),
380 errmsg("could not stat data directory \"%s\": %m",
382 hdr->device = statbuf.st_dev;
383 hdr->inode = statbuf.st_ino;
387 * Initialize space allocation status for segment.
389 hdr->totalsize = size;
390 hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
392 /* Save info for possible future use */
393 UsedShmemSegAddr = memAddress;
394 UsedShmemSegID = (unsigned long) NextShmemSegID;
402 * PGSharedMemoryReAttach
404 * Re-attach to an already existing shared memory segment. In the non
405 * EXEC_BACKEND case this is not used, because postmaster children inherit
406 * the shared memory segment attachment via fork().
408 * UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
409 * routine. The caller must have already restored them to the postmaster's
413 PGSharedMemoryReAttach(void)
417 void *origUsedShmemSegAddr = UsedShmemSegAddr;
419 Assert(UsedShmemSegAddr != NULL);
420 Assert(IsUnderPostmaster);
423 /* cygipc (currently) appears to not detach on exec. */
424 PGSharedMemoryDetach();
425 UsedShmemSegAddr = origUsedShmemSegAddr;
428 elog(DEBUG3, "Attaching to %p", UsedShmemSegAddr);
429 hdr = (void *) PGSharedMemoryAttach((IpcMemoryKey) UsedShmemSegID, &shmid);
431 elog(FATAL, "could not reattach to shared memory (key=%d, addr=%p): %m",
432 (int) UsedShmemSegID, UsedShmemSegAddr);
433 if (hdr != origUsedShmemSegAddr)
434 elog(FATAL, "reattaching to shared memory returned unexpected address (got %p, expected %p)",
435 hdr, origUsedShmemSegAddr);
437 UsedShmemSegAddr = hdr; /* probably redundant */
440 #endif /* EXEC_BACKEND */
443 * PGSharedMemoryDetach
445 * Detach from the shared memory segment, if still attached. This is not
446 * intended for use by the process that originally created the segment
447 * (it will have an on_shmem_exit callback registered to do that). Rather,
448 * this is for subprocesses that have inherited an attachment and want to
452 PGSharedMemoryDetach(void)
454 if (UsedShmemSegAddr != NULL)
456 if ((shmdt(UsedShmemSegAddr) < 0)
457 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
458 /* Work-around for cygipc exec bug */
462 elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
463 UsedShmemSegAddr = NULL;
469 * Attach to shared memory and make sure it has a Postgres header
471 * Returns attach address if OK, else NULL
473 static PGShmemHeader *
474 PGSharedMemoryAttach(IpcMemoryKey key, IpcMemoryId *shmid)
478 if ((*shmid = shmget(key, sizeof(PGShmemHeader), 0)) < 0)
481 hdr = (PGShmemHeader *) shmat(*shmid, UsedShmemSegAddr, PG_SHMAT_FLAGS);
483 if (hdr == (PGShmemHeader *) -1)
484 return NULL; /* failed: must be some other app's */
486 if (hdr->magic != PGShmemMagic)
489 return NULL; /* segment belongs to a non-Postgres app */