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-2003, 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.34 2004/05/06 19:23:25 momjian Exp $
15 *-------------------------------------------------------------------------
29 #ifdef HAVE_KERNEL_OS_H
30 #include <kernel/OS.h>
33 #include "miscadmin.h"
34 #include "storage/ipc.h"
35 #include "storage/pg_shmem.h"
38 typedef key_t IpcMemoryKey; /* shared memory key passed to shmget(2) */
39 typedef int IpcMemoryId; /* shared memory ID returned by shmget(2) */
41 #define IPCProtection (0600) /* access/modify by user only */
44 unsigned long UsedShmemSegID = 0;
45 void *UsedShmemSegAddr = NULL;
47 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, uint32 size);
48 static void IpcMemoryDetach(int status, Datum shmaddr);
49 static void IpcMemoryDelete(int status, Datum shmId);
50 static PGShmemHeader *PGSharedMemoryAttach(IpcMemoryKey key,
55 * InternalIpcMemoryCreate(memKey, size)
57 * Attempt to create a new shared memory segment with the specified key.
58 * Will fail (return NULL) if such a segment already exists. If successful,
59 * attach the segment to the current process and return its attached address.
60 * On success, callbacks are registered with on_shmem_exit to detach and
61 * delete the segment when on_shmem_exit is called.
63 * If we fail with a failure code other than collision-with-existing-segment,
64 * print out an error and abort. Other types of errors are not recoverable.
67 InternalIpcMemoryCreate(IpcMemoryKey memKey, uint32 size)
72 shmid = shmget(memKey, size, IPC_CREAT | IPC_EXCL | IPCProtection);
77 * Fail quietly if error indicates a collision with existing
78 * segment. One would expect EEXIST, given that we said IPC_EXCL,
79 * but perhaps we could get a permission violation instead? Also,
80 * EIDRM might occur if an old seg is slated for destruction but
83 if (errno == EEXIST || errno == EACCES
91 * Else complain and abort
94 (errmsg("could not create shared memory segment: %m"),
95 errdetail("Failed system call was shmget(key=%lu, size=%u, 0%o).",
96 (unsigned long) memKey, size,
97 IPC_CREAT | IPC_EXCL | IPCProtection),
99 errhint("This error usually means that PostgreSQL's request for a shared memory "
100 "segment exceeded your kernel's SHMMAX parameter. You can either "
101 "reduce the request size or reconfigure the kernel with larger SHMMAX. "
102 "To reduce the request size (currently %u bytes), reduce "
103 "PostgreSQL's shared_buffers parameter (currently %d) and/or "
104 "its max_connections parameter (currently %d).\n"
105 "If the request size is already small, it's possible that it is less than "
106 "your kernel's SHMMIN parameter, in which case raising the request size or "
107 "reconfiguring SHMMIN is called for.\n"
108 "The PostgreSQL documentation contains more information about shared "
109 "memory configuration.",
110 size, NBuffers, MaxBackends) : 0,
112 errhint("This error usually means that PostgreSQL's request for a shared "
113 "memory segment exceeded available memory or swap space. "
114 "To reduce the request size (currently %u bytes), reduce "
115 "PostgreSQL's shared_buffers parameter (currently %d) and/or "
116 "its max_connections parameter (currently %d).\n"
117 "The PostgreSQL documentation contains more information about shared "
118 "memory configuration.",
119 size, NBuffers, MaxBackends) : 0,
121 errhint("This error does *not* mean that you have run out of disk space. "
122 "It occurs either if all available shared memory IDs have been taken, "
123 "in which case you need to raise the SHMMNI parameter in your kernel, "
124 "or because the system's overall limit for shared memory has been "
125 "reached. If you cannot increase the shared memory limit, "
126 "reduce PostgreSQL's shared memory request (currently %u bytes), "
127 "by reducing its shared_buffers parameter (currently %d) and/or "
128 "its max_connections parameter (currently %d).\n"
129 "The PostgreSQL documentation contains more information about shared "
130 "memory configuration.",
131 size, NBuffers, MaxBackends) : 0));
134 /* Register on-exit routine to delete the new segment */
135 on_shmem_exit(IpcMemoryDelete, Int32GetDatum(shmid));
137 /* OK, should be able to attach to the segment */
139 /* use intimate shared memory on Solaris */
140 memAddress = shmat(shmid, 0, SHM_SHARE_MMU);
144 memAddress = shmat(shmid, UsedShmemSegAddr, 0);
146 memAddress = shmat(shmid, 0, 0);
150 if (memAddress == (void *) -1)
151 elog(FATAL, "shmat(id=%d) failed: %m", shmid);
153 /* Register on-exit routine to detach new segment before deleting */
154 on_shmem_exit(IpcMemoryDetach, PointerGetDatum(memAddress));
156 /* Record key and ID in lockfile for data directory. */
157 RecordSharedMemoryInLockFile((unsigned long) memKey,
158 (unsigned long) shmid);
163 /****************************************************************************/
164 /* IpcMemoryDetach(status, shmaddr) removes a shared memory segment */
165 /* from process' address spaceq */
166 /* (called as an on_shmem_exit callback, hence funny argument list) */
167 /****************************************************************************/
169 IpcMemoryDetach(int status, Datum shmaddr)
171 if (shmdt(DatumGetPointer(shmaddr)) < 0)
172 elog(LOG, "shmdt(%p) failed: %m", DatumGetPointer(shmaddr));
175 /****************************************************************************/
176 /* IpcMemoryDelete(status, shmId) deletes a shared memory segment */
177 /* (called as an on_shmem_exit callback, hence funny argument list) */
178 /****************************************************************************/
180 IpcMemoryDelete(int status, Datum shmId)
182 if (shmctl(DatumGetInt32(shmId), IPC_RMID, NULL) < 0)
183 elog(LOG, "shmctl(%d, %d, 0) failed: %m",
184 DatumGetInt32(shmId), IPC_RMID);
188 * PGSharedMemoryIsInUse
190 * Is a previously-existing shmem segment still existing and in use?
193 PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
195 IpcMemoryId shmId = (IpcMemoryId) id2;
196 struct shmid_ds shmStat;
199 * We detect whether a shared memory segment is in use by seeing
200 * whether it (a) exists and (b) has any processes are attached to it.
202 * If we are unable to perform the stat operation for a reason other than
203 * nonexistence of the segment (most likely, because it doesn't belong
204 * to our userid), assume it is in use.
206 if (shmctl(shmId, IPC_STAT, &shmStat) < 0)
209 * EINVAL actually has multiple possible causes documented in the
210 * shmctl man page, but we assume it must mean the segment no
215 /* Else assume segment is in use */
218 /* If it has attached processes, it's in use */
219 if (shmStat.shm_nattch != 0)
226 * PGSharedMemoryCreate
228 * Create a shared memory segment of the given size and initialize its
229 * standard header. Also, register an on_shmem_exit callback to release
230 * the storage. For an exec'ed backend, it just attaches.
232 * Dead Postgres segments are recycled if found, but we do not fail upon
233 * collision with non-Postgres shmem segments. The idea here is to detect and
234 * re-use keys that may have been assigned by a crashed postmaster or backend.
236 * makePrivate means to always create a new segment, rather than attach to
237 * or recycle any existing segment.
239 * The port number is passed for possible use as a key (for SysV, we use
240 * it to generate the starting shmem key). In a standalone backend,
241 * zero will be passed.
244 PGSharedMemoryCreate(uint32 size, bool makePrivate, int port)
246 IpcMemoryKey NextShmemSegID;
252 /* If Exec case, just attach and return the pointer */
253 if (UsedShmemSegAddr != NULL && !makePrivate && IsUnderPostmaster)
255 void* origUsedShmemSegAddr = UsedShmemSegAddr;
258 /* cygipc (currently) appears to not detach on exec. */
259 PGSharedMemoryDetach();
260 UsedShmemSegAddr = origUsedShmemSegAddr;
262 elog(DEBUG3,"Attaching to %p",UsedShmemSegAddr);
263 hdr = PGSharedMemoryAttach((IpcMemoryKey) UsedShmemSegID, &shmid);
265 elog(FATAL, "could not attach to proper memory at fixed address: shmget(key=%d, addr=%p) failed: %m",
266 (int) UsedShmemSegID, UsedShmemSegAddr);
267 if (hdr != origUsedShmemSegAddr)
268 elog(FATAL,"attaching to shared mem returned unexpected address (got %p, expected %p)",
269 hdr,UsedShmemSegAddr);
270 UsedShmemSegAddr = hdr;
275 /* Room for a header? */
276 Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
278 /* Make sure PGSharedMemoryAttach doesn't fail without need */
279 UsedShmemSegAddr = NULL;
281 /* Loop till we find a free IPC key */
282 NextShmemSegID = port * 1000;
284 for (NextShmemSegID++;; NextShmemSegID++)
286 /* Try to create new segment */
287 memAddress = InternalIpcMemoryCreate(NextShmemSegID, size);
289 break; /* successful create and attach */
291 /* Check shared memory and possibly remove and recreate */
293 if (makePrivate) /* a standalone backend shouldn't do this */
296 if ((memAddress = PGSharedMemoryAttach(NextShmemSegID, &shmid)) == NULL)
297 continue; /* can't attach, not one of mine */
300 * If I am not the creator and it belongs to an extant process,
303 hdr = (PGShmemHeader *) memAddress;
304 if (hdr->creatorPID != getpid())
306 if (kill(hdr->creatorPID, 0) == 0 || errno != ESRCH)
309 continue; /* segment belongs to a live process */
314 * The segment appears to be from a dead Postgres process, or from
315 * a previous cycle of life in this same process. Zap it, if
316 * possible. This probably shouldn't fail, but if it does, assume
317 * the segment belongs to someone else after all, and continue
321 if (shmctl(shmid, IPC_RMID, NULL) < 0)
325 * Now try again to create the segment.
327 memAddress = InternalIpcMemoryCreate(NextShmemSegID, size);
329 break; /* successful create and attach */
332 * Can only get here if some other process managed to create the
333 * same shmem key before we did. Let him have that one, loop
334 * around to try next key.
339 * OK, we created a new segment. Mark it as created by this process.
340 * The order of assignments here is critical so that another Postgres
341 * process can't see the header as valid but belonging to an invalid
344 hdr = (PGShmemHeader *) memAddress;
345 hdr->creatorPID = getpid();
346 hdr->magic = PGShmemMagic;
349 * Initialize space allocation status for segment.
351 hdr->totalsize = size;
352 hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
354 /* Save info for possible future use */
355 UsedShmemSegAddr = memAddress;
356 UsedShmemSegID = (unsigned long) NextShmemSegID;
362 * PGSharedMemoryDetach
364 * Detach from the shared memory segment, if still attached. This is not
365 * intended for use by the process that originally created the segment
366 * (it will have an on_shmem_exit callback registered to do that). Rather,
367 * this is for subprocesses that have inherited an attachment and want to
371 PGSharedMemoryDetach(void)
373 if (UsedShmemSegAddr != NULL)
375 if ((shmdt(UsedShmemSegAddr) < 0)
376 #if (defined(EXEC_BACKEND) && defined(__CYGWIN__))
377 /* Work-around for cygipc exec bug */
381 elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
382 UsedShmemSegAddr = NULL;
388 * Attach to shared memory and make sure it has a Postgres header
390 * Returns attach address if OK, else NULL
392 static PGShmemHeader *
393 PGSharedMemoryAttach(IpcMemoryKey key, IpcMemoryId *shmid)
397 if ((*shmid = shmget(key, sizeof(PGShmemHeader), 0)) < 0)
400 hdr = (PGShmemHeader *) shmat(*shmid,
403 /* use intimate shared memory on Solaris */
410 if (hdr == (PGShmemHeader *) -1)
411 return NULL; /* failed: must be some other app's */
413 if (hdr->magic != PGShmemMagic)
416 return NULL; /* segment belongs to a non-Postgres app */