Header And Logo

PostgreSQL
| The world's most advanced open source database.

sysv_shmem.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * sysv_shmem.c
00004  *    Implement shared memory using SysV facilities
00005  *
00006  * These routines represent a fairly thin layer on top of SysV shared
00007  * memory functionality.
00008  *
00009  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00010  * Portions Copyright (c) 1994, Regents of the University of California
00011  *
00012  * IDENTIFICATION
00013  *    src/backend/port/sysv_shmem.c
00014  *
00015  *-------------------------------------------------------------------------
00016  */
00017 #include "postgres.h"
00018 
00019 #include <signal.h>
00020 #include <unistd.h>
00021 #include <sys/file.h>
00022 #include <sys/mman.h>
00023 #include <sys/stat.h>
00024 #ifdef HAVE_SYS_IPC_H
00025 #include <sys/ipc.h>
00026 #endif
00027 #ifdef HAVE_SYS_SHM_H
00028 #include <sys/shm.h>
00029 #endif
00030 
00031 #include "miscadmin.h"
00032 #include "storage/ipc.h"
00033 #include "storage/pg_shmem.h"
00034 
00035 
00036 typedef key_t IpcMemoryKey;     /* shared memory key passed to shmget(2) */
00037 typedef int IpcMemoryId;        /* shared memory ID returned by shmget(2) */
00038 
00039 #define IPCProtection   (0600)  /* access/modify by user only */
00040 
00041 #ifdef SHM_SHARE_MMU            /* use intimate shared memory on Solaris */
00042 #define PG_SHMAT_FLAGS          SHM_SHARE_MMU
00043 #else
00044 #define PG_SHMAT_FLAGS          0
00045 #endif
00046 
00047 /* Linux prefers MAP_ANONYMOUS, but the flag is called MAP_ANON on other systems. */
00048 #ifndef MAP_ANONYMOUS
00049 #define MAP_ANONYMOUS           MAP_ANON
00050 #endif
00051 
00052 /* BSD-derived systems have MAP_HASSEMAPHORE, but it's not present (or needed) on Linux. */
00053 #ifndef MAP_HASSEMAPHORE
00054 #define MAP_HASSEMAPHORE        0
00055 #endif
00056 
00057 #define PG_MMAP_FLAGS           (MAP_SHARED|MAP_ANONYMOUS|MAP_HASSEMAPHORE)
00058 
00059 /* Some really old systems don't define MAP_FAILED. */
00060 #ifndef MAP_FAILED
00061 #define MAP_FAILED ((void *) -1)
00062 #endif
00063 
00064 
00065 unsigned long UsedShmemSegID = 0;
00066 void       *UsedShmemSegAddr = NULL;
00067 static Size AnonymousShmemSize;
00068 static void *AnonymousShmem;
00069 
00070 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
00071 static void IpcMemoryDetach(int status, Datum shmaddr);
00072 static void IpcMemoryDelete(int status, Datum shmId);
00073 static PGShmemHeader *PGSharedMemoryAttach(IpcMemoryKey key,
00074                      IpcMemoryId *shmid);
00075 
00076 
00077 /*
00078  *  InternalIpcMemoryCreate(memKey, size)
00079  *
00080  * Attempt to create a new shared memory segment with the specified key.
00081  * Will fail (return NULL) if such a segment already exists.  If successful,
00082  * attach the segment to the current process and return its attached address.
00083  * On success, callbacks are registered with on_shmem_exit to detach and
00084  * delete the segment when on_shmem_exit is called.
00085  *
00086  * If we fail with a failure code other than collision-with-existing-segment,
00087  * print out an error and abort.  Other types of errors are not recoverable.
00088  */
00089 static void *
00090 InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size)
00091 {
00092     IpcMemoryId shmid;
00093     void       *memAddress;
00094 
00095     shmid = shmget(memKey, size, IPC_CREAT | IPC_EXCL | IPCProtection);
00096 
00097     if (shmid < 0)
00098     {
00099         /*
00100          * Fail quietly if error indicates a collision with existing segment.
00101          * One would expect EEXIST, given that we said IPC_EXCL, but perhaps
00102          * we could get a permission violation instead?  Also, EIDRM might
00103          * occur if an old seg is slated for destruction but not gone yet.
00104          */
00105         if (errno == EEXIST || errno == EACCES
00106 #ifdef EIDRM
00107             || errno == EIDRM
00108 #endif
00109             )
00110             return NULL;
00111 
00112         /*
00113          * Some BSD-derived kernels are known to return EINVAL, not EEXIST, if
00114          * there is an existing segment but it's smaller than "size" (this is
00115          * a result of poorly-thought-out ordering of error tests). To
00116          * distinguish between collision and invalid size in such cases, we
00117          * make a second try with size = 0.  These kernels do not test size
00118          * against SHMMIN in the preexisting-segment case, so we will not get
00119          * EINVAL a second time if there is such a segment.
00120          */
00121         if (errno == EINVAL)
00122         {
00123             int         save_errno = errno;
00124 
00125             shmid = shmget(memKey, 0, IPC_CREAT | IPC_EXCL | IPCProtection);
00126 
00127             if (shmid < 0)
00128             {
00129                 /* As above, fail quietly if we verify a collision */
00130                 if (errno == EEXIST || errno == EACCES
00131 #ifdef EIDRM
00132                     || errno == EIDRM
00133 #endif
00134                     )
00135                     return NULL;
00136                 /* Otherwise, fall through to report the original error */
00137             }
00138             else
00139             {
00140                 /*
00141                  * On most platforms we cannot get here because SHMMIN is
00142                  * greater than zero.  However, if we do succeed in creating a
00143                  * zero-size segment, free it and then fall through to report
00144                  * the original error.
00145                  */
00146                 if (shmctl(shmid, IPC_RMID, NULL) < 0)
00147                     elog(LOG, "shmctl(%d, %d, 0) failed: %m",
00148                          (int) shmid, IPC_RMID);
00149             }
00150 
00151             errno = save_errno;
00152         }
00153 
00154         /*
00155          * Else complain and abort.
00156          *
00157          * Note: at this point EINVAL should mean that either SHMMIN or SHMMAX
00158          * is violated.  SHMALL violation might be reported as either ENOMEM
00159          * (BSDen) or ENOSPC (Linux); the Single Unix Spec fails to say which
00160          * it should be.  SHMMNI violation is ENOSPC, per spec.  Just plain
00161          * not-enough-RAM is ENOMEM.
00162          */
00163         ereport(FATAL,
00164                 (errmsg("could not create shared memory segment: %m"),
00165           errdetail("Failed system call was shmget(key=%lu, size=%lu, 0%o).",
00166                     (unsigned long) memKey, (unsigned long) size,
00167                     IPC_CREAT | IPC_EXCL | IPCProtection),
00168                  (errno == EINVAL) ?
00169                  errhint("This error usually means that PostgreSQL's request for a shared memory "
00170           "segment exceeded your kernel's SHMMAX parameter, or possibly that "
00171                          "it is less than "
00172                          "your kernel's SHMMIN parameter.\n"
00173         "The PostgreSQL documentation contains more information about shared "
00174                          "memory configuration.") : 0,
00175                  (errno == ENOMEM) ?
00176                  errhint("This error usually means that PostgreSQL's request for a shared "
00177                    "memory segment exceeded your kernel's SHMALL parameter.  You may need "
00178                          "to reconfigure the kernel with larger SHMALL.\n"
00179         "The PostgreSQL documentation contains more information about shared "
00180                          "memory configuration.") : 0,
00181                  (errno == ENOSPC) ?
00182                  errhint("This error does *not* mean that you have run out of disk space.  "
00183                          "It occurs either if all available shared memory IDs have been taken, "
00184                          "in which case you need to raise the SHMMNI parameter in your kernel, "
00185           "or because the system's overall limit for shared memory has been "
00186                  "reached.\n"
00187         "The PostgreSQL documentation contains more information about shared "
00188                          "memory configuration.") : 0));
00189     }
00190 
00191     /* Register on-exit routine to delete the new segment */
00192     on_shmem_exit(IpcMemoryDelete, Int32GetDatum(shmid));
00193 
00194     /* OK, should be able to attach to the segment */
00195     memAddress = shmat(shmid, NULL, PG_SHMAT_FLAGS);
00196 
00197     if (memAddress == (void *) -1)
00198         elog(FATAL, "shmat(id=%d) failed: %m", shmid);
00199 
00200     /* Register on-exit routine to detach new segment before deleting */
00201     on_shmem_exit(IpcMemoryDetach, PointerGetDatum(memAddress));
00202 
00203     /*
00204      * Store shmem key and ID in data directory lockfile.  Format to try to
00205      * keep it the same length always (trailing junk in the lockfile won't
00206      * hurt, but might confuse humans).
00207      */
00208     {
00209         char        line[64];
00210 
00211         sprintf(line, "%9lu %9lu",
00212                 (unsigned long) memKey, (unsigned long) shmid);
00213         AddToDataDirLockFile(LOCK_FILE_LINE_SHMEM_KEY, line);
00214     }
00215 
00216     return memAddress;
00217 }
00218 
00219 /****************************************************************************/
00220 /*  IpcMemoryDetach(status, shmaddr)    removes a shared memory segment     */
00221 /*                                      from process' address spaceq        */
00222 /*  (called as an on_shmem_exit callback, hence funny argument list)        */
00223 /****************************************************************************/
00224 static void
00225 IpcMemoryDetach(int status, Datum shmaddr)
00226 {
00227     /* Detach System V shared memory block. */
00228     if (shmdt(DatumGetPointer(shmaddr)) < 0)
00229         elog(LOG, "shmdt(%p) failed: %m", DatumGetPointer(shmaddr));
00230     /* Release anonymous shared memory block, if any. */
00231     if (AnonymousShmem != NULL
00232         && munmap(AnonymousShmem, AnonymousShmemSize) < 0)
00233         elog(LOG, "munmap(%p) failed: %m", AnonymousShmem);
00234 }
00235 
00236 /****************************************************************************/
00237 /*  IpcMemoryDelete(status, shmId)      deletes a shared memory segment     */
00238 /*  (called as an on_shmem_exit callback, hence funny argument list)        */
00239 /****************************************************************************/
00240 static void
00241 IpcMemoryDelete(int status, Datum shmId)
00242 {
00243     if (shmctl(DatumGetInt32(shmId), IPC_RMID, NULL) < 0)
00244         elog(LOG, "shmctl(%d, %d, 0) failed: %m",
00245              DatumGetInt32(shmId), IPC_RMID);
00246 }
00247 
00248 /*
00249  * PGSharedMemoryIsInUse
00250  *
00251  * Is a previously-existing shmem segment still existing and in use?
00252  *
00253  * The point of this exercise is to detect the case where a prior postmaster
00254  * crashed, but it left child backends that are still running.  Therefore
00255  * we only care about shmem segments that are associated with the intended
00256  * DataDir.  This is an important consideration since accidental matches of
00257  * shmem segment IDs are reasonably common.
00258  */
00259 bool
00260 PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2)
00261 {
00262     IpcMemoryId shmId = (IpcMemoryId) id2;
00263     struct shmid_ds shmStat;
00264     struct stat statbuf;
00265     PGShmemHeader *hdr;
00266 
00267     /*
00268      * We detect whether a shared memory segment is in use by seeing whether
00269      * it (a) exists and (b) has any processes attached to it.
00270      */
00271     if (shmctl(shmId, IPC_STAT, &shmStat) < 0)
00272     {
00273         /*
00274          * EINVAL actually has multiple possible causes documented in the
00275          * shmctl man page, but we assume it must mean the segment no longer
00276          * exists.
00277          */
00278         if (errno == EINVAL)
00279             return false;
00280 
00281         /*
00282          * EACCES implies that the segment belongs to some other userid, which
00283          * means it is not a Postgres shmem segment (or at least, not one that
00284          * is relevant to our data directory).
00285          */
00286         if (errno == EACCES)
00287             return false;
00288 
00289         /*
00290          * Some Linux kernel versions (in fact, all of them as of July 2007)
00291          * sometimes return EIDRM when EINVAL is correct.  The Linux kernel
00292          * actually does not have any internal state that would justify
00293          * returning EIDRM, so we can get away with assuming that EIDRM is
00294          * equivalent to EINVAL on that platform.
00295          */
00296 #ifdef HAVE_LINUX_EIDRM_BUG
00297         if (errno == EIDRM)
00298             return false;
00299 #endif
00300 
00301         /*
00302          * Otherwise, we had better assume that the segment is in use. The
00303          * only likely case is EIDRM, which implies that the segment has been
00304          * IPC_RMID'd but there are still processes attached to it.
00305          */
00306         return true;
00307     }
00308 
00309     /* If it has no attached processes, it's not in use */
00310     if (shmStat.shm_nattch == 0)
00311         return false;
00312 
00313     /*
00314      * Try to attach to the segment and see if it matches our data directory.
00315      * This avoids shmid-conflict problems on machines that are running
00316      * several postmasters under the same userid.
00317      */
00318     if (stat(DataDir, &statbuf) < 0)
00319         return true;            /* if can't stat, be conservative */
00320 
00321     hdr = (PGShmemHeader *) shmat(shmId, NULL, PG_SHMAT_FLAGS);
00322 
00323     if (hdr == (PGShmemHeader *) -1)
00324         return true;            /* if can't attach, be conservative */
00325 
00326     if (hdr->magic != PGShmemMagic ||
00327         hdr->device != statbuf.st_dev ||
00328         hdr->inode != statbuf.st_ino)
00329     {
00330         /*
00331          * It's either not a Postgres segment, or not one for my data
00332          * directory.  In either case it poses no threat.
00333          */
00334         shmdt((void *) hdr);
00335         return false;
00336     }
00337 
00338     /* Trouble --- looks a lot like there's still live backends */
00339     shmdt((void *) hdr);
00340 
00341     return true;
00342 }
00343 
00344 
00345 /*
00346  * PGSharedMemoryCreate
00347  *
00348  * Create a shared memory segment of the given size and initialize its
00349  * standard header.  Also, register an on_shmem_exit callback to release
00350  * the storage.
00351  *
00352  * Dead Postgres segments are recycled if found, but we do not fail upon
00353  * collision with non-Postgres shmem segments.  The idea here is to detect and
00354  * re-use keys that may have been assigned by a crashed postmaster or backend.
00355  *
00356  * makePrivate means to always create a new segment, rather than attach to
00357  * or recycle any existing segment.
00358  *
00359  * The port number is passed for possible use as a key (for SysV, we use
00360  * it to generate the starting shmem key).  In a standalone backend,
00361  * zero will be passed.
00362  */
00363 PGShmemHeader *
00364 PGSharedMemoryCreate(Size size, bool makePrivate, int port)
00365 {
00366     IpcMemoryKey NextShmemSegID;
00367     void       *memAddress;
00368     PGShmemHeader *hdr;
00369     IpcMemoryId shmid;
00370     struct stat statbuf;
00371     Size        sysvsize = size;
00372 
00373     /* Room for a header? */
00374     Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
00375 
00376     /*
00377      * As of PostgreSQL 9.3, we normally allocate only a very small amount of
00378      * System V shared memory, and only for the purposes of providing an
00379      * interlock to protect the data directory.  The real shared memory block
00380      * is allocated using mmap().  This works around the problem that many
00381      * systems have very low limits on the amount of System V shared memory
00382      * that can be allocated.  Even a limit of a few megabytes will be enough
00383      * to run many copies of PostgreSQL without needing to adjust system
00384      * settings.
00385      *
00386      * However, we disable this logic in the EXEC_BACKEND case, and fall back
00387      * to the old method of allocating the entire segment using System V shared
00388      * memory, because there's no way to attach an mmap'd segment to a process
00389      * after exec().  Since EXEC_BACKEND is intended only for developer use,
00390      * this shouldn't be a big problem.
00391      */
00392 #ifndef EXEC_BACKEND
00393     {
00394         long    pagesize = sysconf(_SC_PAGE_SIZE);
00395 
00396         /*
00397          * Ensure request size is a multiple of pagesize.
00398          *
00399          * pagesize will, for practical purposes, always be a power of two.
00400          * But just in case it isn't, we do it this way instead of using
00401          * TYPEALIGN().
00402          */
00403         if (pagesize > 0 && size % pagesize != 0)
00404             size += pagesize - (size % pagesize);
00405 
00406         /*
00407          * We assume that no one will attempt to run PostgreSQL 9.3 or later
00408          * on systems that are ancient enough that anonymous shared memory is
00409          * not supported, such as pre-2.4 versions of Linux.  If that turns out
00410          * to be false, we might need to add a run-time test here and do this
00411          * only if the running kernel supports it.
00412          */
00413         AnonymousShmem = mmap(NULL, size, PROT_READ|PROT_WRITE, PG_MMAP_FLAGS,
00414                               -1, 0);
00415         if (AnonymousShmem == MAP_FAILED)
00416             ereport(FATAL,
00417              (errmsg("could not map anonymous shared memory: %m"),
00418               (errno == ENOMEM) ?
00419                errhint("This error usually means that PostgreSQL's request "
00420                        "for a shared memory segment exceeded available memory "
00421                        "or swap space. To reduce the request size (currently "
00422                        "%lu bytes), reduce PostgreSQL's shared memory usage, "
00423                        "perhaps by reducing shared_buffers or "
00424                        "max_connections.",
00425                        (unsigned long) size) : 0));
00426         AnonymousShmemSize = size;
00427 
00428         /* Now we need only allocate a minimal-sized SysV shmem block. */
00429         sysvsize = sizeof(PGShmemHeader);
00430     }
00431 #endif
00432 
00433     /* Make sure PGSharedMemoryAttach doesn't fail without need */
00434     UsedShmemSegAddr = NULL;
00435 
00436     /* Loop till we find a free IPC key */
00437     NextShmemSegID = port * 1000;
00438 
00439     for (NextShmemSegID++;; NextShmemSegID++)
00440     {
00441         /* Try to create new segment */
00442         memAddress = InternalIpcMemoryCreate(NextShmemSegID, sysvsize);
00443         if (memAddress)
00444             break;              /* successful create and attach */
00445 
00446         /* Check shared memory and possibly remove and recreate */
00447 
00448         if (makePrivate)        /* a standalone backend shouldn't do this */
00449             continue;
00450 
00451         if ((memAddress = PGSharedMemoryAttach(NextShmemSegID, &shmid)) == NULL)
00452             continue;           /* can't attach, not one of mine */
00453 
00454         /*
00455          * If I am not the creator and it belongs to an extant process,
00456          * continue.
00457          */
00458         hdr = (PGShmemHeader *) memAddress;
00459         if (hdr->creatorPID != getpid())
00460         {
00461             if (kill(hdr->creatorPID, 0) == 0 || errno != ESRCH)
00462             {
00463                 shmdt(memAddress);
00464                 continue;       /* segment belongs to a live process */
00465             }
00466         }
00467 
00468         /*
00469          * The segment appears to be from a dead Postgres process, or from a
00470          * previous cycle of life in this same process.  Zap it, if possible.
00471          * This probably shouldn't fail, but if it does, assume the segment
00472          * belongs to someone else after all, and continue quietly.
00473          */
00474         shmdt(memAddress);
00475         if (shmctl(shmid, IPC_RMID, NULL) < 0)
00476             continue;
00477 
00478         /*
00479          * Now try again to create the segment.
00480          */
00481         memAddress = InternalIpcMemoryCreate(NextShmemSegID, sysvsize);
00482         if (memAddress)
00483             break;              /* successful create and attach */
00484 
00485         /*
00486          * Can only get here if some other process managed to create the same
00487          * shmem key before we did.  Let him have that one, loop around to try
00488          * next key.
00489          */
00490     }
00491 
00492     /*
00493      * OK, we created a new segment.  Mark it as created by this process. The
00494      * order of assignments here is critical so that another Postgres process
00495      * can't see the header as valid but belonging to an invalid PID!
00496      */
00497     hdr = (PGShmemHeader *) memAddress;
00498     hdr->creatorPID = getpid();
00499     hdr->magic = PGShmemMagic;
00500 
00501     /* Fill in the data directory ID info, too */
00502     if (stat(DataDir, &statbuf) < 0)
00503         ereport(FATAL,
00504                 (errcode_for_file_access(),
00505                  errmsg("could not stat data directory \"%s\": %m",
00506                         DataDir)));
00507     hdr->device = statbuf.st_dev;
00508     hdr->inode = statbuf.st_ino;
00509 
00510     /*
00511      * Initialize space allocation status for segment.
00512      */
00513     hdr->totalsize = size;
00514     hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
00515 
00516     /* Save info for possible future use */
00517     UsedShmemSegAddr = memAddress;
00518     UsedShmemSegID = (unsigned long) NextShmemSegID;
00519 
00520     /*
00521      * If AnonymousShmem is NULL here, then we're not using anonymous shared
00522      * memory, and should return a pointer to the System V shared memory block.
00523      * Otherwise, the System V shared memory block is only a shim, and we must
00524      * return a pointer to the real block.
00525      */
00526     if (AnonymousShmem == NULL)
00527         return hdr;
00528     memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
00529     return (PGShmemHeader *) AnonymousShmem;
00530 }
00531 
00532 #ifdef EXEC_BACKEND
00533 
00534 /*
00535  * PGSharedMemoryReAttach
00536  *
00537  * Re-attach to an already existing shared memory segment.  In the non
00538  * EXEC_BACKEND case this is not used, because postmaster children inherit
00539  * the shared memory segment attachment via fork().
00540  *
00541  * UsedShmemSegID and UsedShmemSegAddr are implicit parameters to this
00542  * routine.  The caller must have already restored them to the postmaster's
00543  * values.
00544  */
00545 void
00546 PGSharedMemoryReAttach(void)
00547 {
00548     IpcMemoryId shmid;
00549     void       *hdr;
00550     void       *origUsedShmemSegAddr = UsedShmemSegAddr;
00551 
00552     Assert(UsedShmemSegAddr != NULL);
00553     Assert(IsUnderPostmaster);
00554 
00555 #ifdef __CYGWIN__
00556     /* cygipc (currently) appears to not detach on exec. */
00557     PGSharedMemoryDetach();
00558     UsedShmemSegAddr = origUsedShmemSegAddr;
00559 #endif
00560 
00561     elog(DEBUG3, "attaching to %p", UsedShmemSegAddr);
00562     hdr = (void *) PGSharedMemoryAttach((IpcMemoryKey) UsedShmemSegID, &shmid);
00563     if (hdr == NULL)
00564         elog(FATAL, "could not reattach to shared memory (key=%d, addr=%p): %m",
00565              (int) UsedShmemSegID, UsedShmemSegAddr);
00566     if (hdr != origUsedShmemSegAddr)
00567         elog(FATAL, "reattaching to shared memory returned unexpected address (got %p, expected %p)",
00568              hdr, origUsedShmemSegAddr);
00569 
00570     UsedShmemSegAddr = hdr;     /* probably redundant */
00571 }
00572 #endif   /* EXEC_BACKEND */
00573 
00574 /*
00575  * PGSharedMemoryDetach
00576  *
00577  * Detach from the shared memory segment, if still attached.  This is not
00578  * intended for use by the process that originally created the segment
00579  * (it will have an on_shmem_exit callback registered to do that).  Rather,
00580  * this is for subprocesses that have inherited an attachment and want to
00581  * get rid of it.
00582  */
00583 void
00584 PGSharedMemoryDetach(void)
00585 {
00586     if (UsedShmemSegAddr != NULL)
00587     {
00588         if ((shmdt(UsedShmemSegAddr) < 0)
00589 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
00590         /* Work-around for cygipc exec bug */
00591             && shmdt(NULL) < 0
00592 #endif
00593             )
00594             elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
00595         UsedShmemSegAddr = NULL;
00596     }
00597 
00598     /* Release anonymous shared memory block, if any. */
00599     if (AnonymousShmem != NULL
00600         && munmap(AnonymousShmem, AnonymousShmemSize) < 0)
00601         elog(LOG, "munmap(%p) failed: %m", AnonymousShmem);
00602 }
00603 
00604 
00605 /*
00606  * Attach to shared memory and make sure it has a Postgres header
00607  *
00608  * Returns attach address if OK, else NULL
00609  */
00610 static PGShmemHeader *
00611 PGSharedMemoryAttach(IpcMemoryKey key, IpcMemoryId *shmid)
00612 {
00613     PGShmemHeader *hdr;
00614 
00615     if ((*shmid = shmget(key, sizeof(PGShmemHeader), 0)) < 0)
00616         return NULL;
00617 
00618     hdr = (PGShmemHeader *) shmat(*shmid, UsedShmemSegAddr, PG_SHMAT_FLAGS);
00619 
00620     if (hdr == (PGShmemHeader *) -1)
00621         return NULL;            /* failed: must be some other app's */
00622 
00623     if (hdr->magic != PGShmemMagic)
00624     {
00625         shmdt((void *) hdr);
00626         return NULL;            /* segment belongs to a non-Postgres app */
00627     }
00628 
00629     return hdr;
00630 }