Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quellcode-Bibliothek dsm_impl.c

  Sprache: C
 

/*-------------------------------------------------------------------------
 *
 * dsm_impl.c
 *   manage dynamic shared memory segments
 *
 * This file provides low-level APIs for creating and destroying shared
 * memory segments using several different possible techniques.  We refer
 * to these segments as dynamic because they can be created, altered, and
 * destroyed at any point during the server life cycle.  This is unlike
 * the main shared memory segment, of which there is always exactly one
 * and which is always mapped at a fixed address in every PostgreSQL
 * background process.
 *
 * Because not all systems provide the same primitives in this area, nor
 * do all primitives behave the same way on all systems, we provide
 * several implementations of this facility.  Many systems implement
 * POSIX shared memory (shm_open etc.), which is well-suited to our needs
 * in this area, with the exception that shared memory identifiers live
 * in a flat system-wide namespace, raising the uncomfortable prospect of
 * name collisions with other processes (including other copies of
 * PostgreSQL) running on the same system.  Some systems only support
 * the older System V shared memory interface (shmget etc.) which is
 * also usable; however, the default allocation limits are often quite
 * small, and the namespace is even more restricted.
 *
 * We also provide an mmap-based shared memory implementation.  This may
 * be useful on systems that provide shared memory via a special-purpose
 * filesystem; by opting for this implementation, the user can even
 * control precisely where their shared memory segments are placed.  It
 * can also be used as a fallback for systems where shm_open and shmget
 * are not available or can't be used for some reason.  Of course,
 * mapping a file residing on an actual spinning disk is a fairly poor
 * approximation for shared memory because writeback may hurt performance
 * substantially, but there should be few systems where we must make do
 * with such poor tools.
 *
 * As ever, Windows requires its own implementation.
 *
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *   src/backend/storage/ipc/dsm_impl.c
 *
 *-------------------------------------------------------------------------
 */


#include "postgres.h"

#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#ifndef WIN32
#include <sys/mman.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#endif

#include "common/file_perm.h"
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "portability/mem.h"
#include "postmaster/postmaster.h"
#include "storage/dsm_impl.h"
#include "storage/fd.h"
#include "utils/guc.h"
#include "utils/memutils.h"

#ifdef USE_DSM_POSIX
static bool dsm_impl_posix(dsm_op op, dsm_handle handle, Size request_size,
         void **impl_private, void **mapped_address,
         Size *mapped_size, int elevel);
static int dsm_impl_posix_resize(int fd, off_t size);
#endif
#ifdef USE_DSM_SYSV
static bool dsm_impl_sysv(dsm_op op, dsm_handle handle, Size request_size,
        void **impl_private, void **mapped_address,
        Size *mapped_size, int elevel);
#endif
#ifdef USE_DSM_WINDOWS
static bool dsm_impl_windows(dsm_op op, dsm_handle handle, Size request_size,
        void **impl_private, void **mapped_address,
        Size *mapped_size, int elevel);
#endif
#ifdef USE_DSM_MMAP
static bool dsm_impl_mmap(dsm_op op, dsm_handle handle, Size request_size,
        void **impl_private, void **mapped_address,
        Size *mapped_size, int elevel);
#endif
static int errcode_for_dynamic_shared_memory(void);

const struct config_enum_entry dynamic_shared_memory_options[] = {
#ifdef USE_DSM_POSIX
 {"posix", DSM_IMPL_POSIX, false},
#endif
#ifdef USE_DSM_SYSV
 {"sysv", DSM_IMPL_SYSV, false},
#endif
#ifdef USE_DSM_WINDOWS
 {"windows", DSM_IMPL_WINDOWS, false},
#endif
#ifdef USE_DSM_MMAP
 {"mmap", DSM_IMPL_MMAP, false},
#endif
 {NULL, 0false}
};

/* Implementation selector. */
int   dynamic_shared_memory_type = DEFAULT_DYNAMIC_SHARED_MEMORY_TYPE;

/* Amount of space reserved for DSM segments in the main area. */
int   min_dynamic_shared_memory;

/* Size of buffer to be used for zero-filling. */
#define ZBUFFER_SIZE    8192

#define SEGMENT_NAME_PREFIX   "Global/PostgreSQL"

/*------
 * Perform a low-level shared memory operation in a platform-specific way,
 * as dictated by the selected implementation.  Each implementation is
 * required to implement the following primitives.
 *
 * DSM_OP_CREATE.  Create a segment whose size is the request_size and
 * map it.
 *
 * DSM_OP_ATTACH.  Map the segment, whose size must be the request_size.
 *
 * DSM_OP_DETACH.  Unmap the segment.
 *
 * DSM_OP_DESTROY.  Unmap the segment, if it is mapped.  Destroy the
 * segment.
 *
 * Arguments:
 *  op: The operation to be performed.
 *  handle: The handle of an existing object, or for DSM_OP_CREATE, the
 *    identifier for the new handle the caller wants created.
 *  request_size: For DSM_OP_CREATE, the requested size.  Otherwise, 0.
 *  impl_private: Private, implementation-specific data.  Will be a pointer
 *    to NULL for the first operation on a shared memory segment within this
 *    backend; thereafter, it will point to the value to which it was set
 *    on the previous call.
 *  mapped_address: Pointer to start of current mapping; pointer to NULL
 *    if none.  Updated with new mapping address.
 *  mapped_size: Pointer to size of current mapping; pointer to 0 if none.
 *    Updated with new mapped size.
 *  elevel: Level at which to log errors.
 *
 * Return value: true on success, false on failure.  When false is returned,
 * a message should first be logged at the specified elevel, except in the
 * case where DSM_OP_CREATE experiences a name collision, which should
 * silently return false.
 *-----
 */

bool
dsm_impl_op(dsm_op op, dsm_handle handle, Size request_size,
   void **impl_private, void **mapped_address, Size *mapped_size,
   int elevel)
{
 Assert(op == DSM_OP_CREATE || request_size == 0);
 Assert((op != DSM_OP_CREATE && op != DSM_OP_ATTACH) ||
     (*mapped_address == NULL && *mapped_size == 0));

 switch (dynamic_shared_memory_type)
 {
#ifdef USE_DSM_POSIX
  case DSM_IMPL_POSIX:
   return dsm_impl_posix(op, handle, request_size, impl_private,
          mapped_address, mapped_size, elevel);
#endif
#ifdef USE_DSM_SYSV
  case DSM_IMPL_SYSV:
   return dsm_impl_sysv(op, handle, request_size, impl_private,
         mapped_address, mapped_size, elevel);
#endif
#ifdef USE_DSM_WINDOWS
  case DSM_IMPL_WINDOWS:
   return dsm_impl_windows(op, handle, request_size, impl_private,
         mapped_address, mapped_size, elevel);
#endif
#ifdef USE_DSM_MMAP
  case DSM_IMPL_MMAP:
   return dsm_impl_mmap(op, handle, request_size, impl_private,
         mapped_address, mapped_size, elevel);
#endif
  default:
   elog(ERROR, "unexpected dynamic shared memory type: %d",
     dynamic_shared_memory_type);
   return false;
 }
}

#ifdef USE_DSM_POSIX
/*
 * Operating system primitives to support POSIX shared memory.
 *
 * POSIX shared memory segments are created and attached using shm_open()
 * and shm_unlink(); other operations, such as sizing or mapping the
 * segment, are performed as if the shared memory segments were files.
 *
 * Indeed, on some platforms, they may be implemented that way.  While
 * POSIX shared memory segments seem intended to exist in a flat namespace,
 * some operating systems may implement them as files, even going so far
 * to treat a request for /xyz as a request to create a file by that name
 * in the root directory.  Users of such broken platforms should select
 * a different shared memory implementation.
 */

static bool
dsm_impl_posix(dsm_op op, dsm_handle handle, Size request_size,
      void **impl_private, void **mapped_address, Size *mapped_size,
      int elevel)
{
 char  name[64];
 int   flags;
 int   fd;
 char    *address;

 snprintf(name, 64"/PostgreSQL.%u", handle);

 /* Handle teardown cases. */
 if (op == DSM_OP_DETACH || op == DSM_OP_DESTROY)
 {
  if (*mapped_address != NULL
   && munmap(*mapped_address, *mapped_size) != 0)
  {
   ereport(elevel,
     (errcode_for_dynamic_shared_memory(),
      errmsg("could not unmap shared memory segment \"%s\": %m",
       name)));
   return false;
  }
  *mapped_address = NULL;
  *mapped_size = 0;
  if (op == DSM_OP_DESTROY && shm_unlink(name) != 0)
  {
   ereport(elevel,
     (errcode_for_dynamic_shared_memory(),
      errmsg("could not remove shared memory segment \"%s\": %m",
       name)));
   return false;
  }
  return true;
 }

 /*
  * Create new segment or open an existing one for attach.
  *
  * Even though we will close the FD before returning, it seems desirable
  * to use Reserve/ReleaseExternalFD, to reduce the probability of EMFILE
  * failure.  The fact that we won't hold the FD open long justifies using
  * ReserveExternalFD rather than AcquireExternalFD, though.
 */

 ReserveExternalFD();

 flags = O_RDWR | (op == DSM_OP_CREATE ? O_CREAT | O_EXCL : 0);
 if ((fd = shm_open(name, flags, PG_FILE_MODE_OWNER)) == -1)
 {
  ReleaseExternalFD();
  if (op == DSM_OP_ATTACH || errno != EEXIST)
   ereport(elevel,
     (errcode_for_dynamic_shared_memory(),
      errmsg("could not open shared memory segment \"%s\": %m",
       name)));
  return false;
 }

 /*
  * If we're attaching the segment, determine the current size; if we are
  * creating the segment, set the size to the requested value.
 */

 if (op == DSM_OP_ATTACH)
 {
  struct stat st;

  if (fstat(fd, &st) != 0)
  {
   int   save_errno;

   /* Back out what's already been done. */
   save_errno = errno;
   close(fd);
   ReleaseExternalFD();
   errno = save_errno;

   ereport(elevel,
     (errcode_for_dynamic_shared_memory(),
      errmsg("could not stat shared memory segment \"%s\": %m",
       name)));
   return false;
  }
  request_size = st.st_size;
 }
 else if (dsm_impl_posix_resize(fd, request_size) != 0)
 {
  int   save_errno;

  /* Back out what's already been done. */
  save_errno = errno;
  close(fd);
  ReleaseExternalFD();
  shm_unlink(name);
  errno = save_errno;

  ereport(elevel,
    (errcode_for_dynamic_shared_memory(),
     errmsg("could not resize shared memory segment \"%s\" to %zu bytes: %m",
      name, request_size)));
  return false;
 }

 /* Map it. */
 address = mmap(NULL, request_size, PROT_READ | PROT_WRITE,
       MAP_SHARED | MAP_HASSEMAPHORE | MAP_NOSYNC, fd, 0);
 if (address == MAP_FAILED)
 {
  int   save_errno;

  /* Back out what's already been done. */
  save_errno = errno;
  close(fd);
  ReleaseExternalFD();
  if (op == DSM_OP_CREATE)
   shm_unlink(name);
  errno = save_errno;

  ereport(elevel,
    (errcode_for_dynamic_shared_memory(),
     errmsg("could not map shared memory segment \"%s\": %m",
      name)));
  return false;
 }
 *mapped_address = address;
 *mapped_size = request_size;
 close(fd);
 ReleaseExternalFD();

 return true;
}

/*
 * Set the size of a virtual memory region associated with a file descriptor.
 * If necessary, also ensure that virtual memory is actually allocated by the
 * operating system, to avoid nasty surprises later.
 *
 * Returns non-zero if either truncation or allocation fails, and sets errno.
 */

static int
dsm_impl_posix_resize(int fd, off_t size)
{
 int   rc;
 int   save_errno;
 sigset_t save_sigmask;

 /*
  * Block all blockable signals, except SIGQUIT.  posix_fallocate() can run
  * for quite a long time, and is an all-or-nothing operation.  If we
  * allowed SIGUSR1 to interrupt us repeatedly (for example, due to
  * recovery conflicts), the retry loop might never succeed.
 */

 if (IsUnderPostmaster)
  sigprocmask(SIG_SETMASK, &BlockSig, &save_sigmask);

 pgstat_report_wait_start(WAIT_EVENT_DSM_ALLOCATE);
#if defined(HAVE_POSIX_FALLOCATE) && defined(__linux__)

 /*
  * On Linux, a shm_open fd is backed by a tmpfs file.  If we were to use
  * ftruncate, the file would contain a hole.  Accessing memory backed by a
  * hole causes tmpfs to allocate pages, which fails with SIGBUS if there
  * is no more tmpfs space available.  So we ask tmpfs to allocate pages
  * here, so we can fail gracefully with ENOSPC now rather than risking
  * SIGBUS later.
  *
  * We still use a traditional EINTR retry loop to handle SIGCONT.
  * posix_fallocate() doesn't restart automatically, and we don't want this
  * to fail if you attach a debugger.
 */

 do
 {
  rc = posix_fallocate(fd, 0, size);
 } while (rc == EINTR);

 /*
  * The caller expects errno to be set, but posix_fallocate() doesn't set
  * it.  Instead it returns error numbers directly.  So set errno, even
  * though we'll also return rc to indicate success or failure.
 */

 errno = rc;
#else
 /* Extend the file to the requested size. */
 do
 {
  rc = ftruncate(fd, size);
 } while (rc < 0 && errno == EINTR);
#endif
 pgstat_report_wait_end();

 if (IsUnderPostmaster)
 {
  save_errno = errno;
  sigprocmask(SIG_SETMASK, &save_sigmask, NULL);
  errno = save_errno;
 }

 return rc;
}

#endif       /* USE_DSM_POSIX */

#ifdef USE_DSM_SYSV
/*
 * Operating system primitives to support System V shared memory.
 *
 * System V shared memory segments are manipulated using shmget(), shmat(),
 * shmdt(), and shmctl().  As the default allocation limits for System V
 * shared memory are usually quite low, the POSIX facilities may be
 * preferable; but those are not supported everywhere.
 */

static bool
dsm_impl_sysv(dsm_op op, dsm_handle handle, Size request_size,
     void **impl_private, void **mapped_address, Size *mapped_size,
     int elevel)
{
 key_t  key;
 int   ident;
 char    *address;
 char  name[64];
 int     *ident_cache;

 /*
  * POSIX shared memory and mmap-based shared memory identify segments with
  * names.  To avoid needless error message variation, we use the handle as
  * the name.
 */

 snprintf(name, 64"%u", handle);

 /*
  * The System V shared memory namespace is very restricted; names are of
  * type key_t, which is expected to be some sort of integer data type, but
  * not necessarily the same one as dsm_handle.  Since we use dsm_handle to
  * identify shared memory segments across processes, this might seem like
  * a problem, but it's really not.  If dsm_handle is bigger than key_t,
  * the cast below might truncate away some bits from the handle the
  * user-provided, but it'll truncate exactly the same bits away in exactly
  * the same fashion every time we use that handle, which is all that
  * really matters.  Conversely, if dsm_handle is smaller than key_t, we
  * won't use the full range of available key space, but that's no big deal
  * either.
  *
  * We do make sure that the key isn't negative, because that might not be
  * portable.
 */

 key = (key_t) handle;
 if (key < 1)    /* avoid compiler warning if type is unsigned */
  key = -key;

 /*
  * There's one special key, IPC_PRIVATE, which can't be used.  If we end
  * up with that value by chance during a create operation, just pretend it
  * already exists, so that caller will retry.  If we run into it anywhere
  * else, the caller has passed a handle that doesn't correspond to
  * anything we ever created, which should not happen.
 */

 if (key == IPC_PRIVATE)
 {
  if (op != DSM_OP_CREATE)
   elog(DEBUG4, "System V shared memory key may not be IPC_PRIVATE");
  errno = EEXIST;
  return false;
 }

 /*
  * Before we can do anything with a shared memory segment, we have to map
  * the shared memory key to a shared memory identifier using shmget(). To
  * avoid repeated lookups, we store the key using impl_private.
 */

 if (*impl_private != NULL)
 {
  ident_cache = *impl_private;
  ident = *ident_cache;
 }
 else
 {
  int   flags = IPCProtection;
  size_t  segsize;

  /*
   * Allocate the memory BEFORE acquiring the resource, so that we don't
   * leak the resource if memory allocation fails.
 */

  ident_cache = MemoryContextAlloc(TopMemoryContext, sizeof(int));

  /*
   * When using shmget to find an existing segment, we must pass the
   * size as 0.  Passing a non-zero size which is greater than the
   * actual size will result in EINVAL.
 */

  segsize = 0;

  if (op == DSM_OP_CREATE)
  {
   flags |= IPC_CREAT | IPC_EXCL;
   segsize = request_size;
  }

  if ((ident = shmget(key, segsize, flags)) == -1)
  {
   if (op == DSM_OP_ATTACH || errno != EEXIST)
   {
    int   save_errno = errno;

    pfree(ident_cache);
    errno = save_errno;
    ereport(elevel,
      (errcode_for_dynamic_shared_memory(),
       errmsg("could not get shared memory segment: %m")));
   }
   return false;
  }

  *ident_cache = ident;
  *impl_private = ident_cache;
 }

 /* Handle teardown cases. */
 if (op == DSM_OP_DETACH || op == DSM_OP_DESTROY)
 {
  pfree(ident_cache);
  *impl_private = NULL;
  if (*mapped_address != NULL && shmdt(*mapped_address) != 0)
  {
   ereport(elevel,
     (errcode_for_dynamic_shared_memory(),
      errmsg("could not unmap shared memory segment \"%s\": %m",
       name)));
   return false;
  }
  *mapped_address = NULL;
  *mapped_size = 0;
  if (op == DSM_OP_DESTROY && shmctl(ident, IPC_RMID, NULL) < 0)
  {
   ereport(elevel,
     (errcode_for_dynamic_shared_memory(),
      errmsg("could not remove shared memory segment \"%s\": %m",
       name)));
   return false;
  }
  return true;
 }

 /* If we're attaching it, we must use IPC_STAT to determine the size. */
 if (op == DSM_OP_ATTACH)
 {
  struct shmid_ds shm;

  if (shmctl(ident, IPC_STAT, &shm) != 0)
  {
   ereport(elevel,
     (errcode_for_dynamic_shared_memory(),
      errmsg("could not stat shared memory segment \"%s\": %m",
       name)));
   return false;
  }
  request_size = shm.shm_segsz;
 }

 /* Map it. */
 address = shmat(ident, NULL, PG_SHMAT_FLAGS);
 if (address == (void *) -1)
 {
  int   save_errno;

  /* Back out what's already been done. */
  save_errno = errno;
  if (op == DSM_OP_CREATE)
   shmctl(ident, IPC_RMID, NULL);
  errno = save_errno;

  ereport(elevel,
    (errcode_for_dynamic_shared_memory(),
     errmsg("could not map shared memory segment \"%s\": %m",
      name)));
  return false;
 }
 *mapped_address = address;
 *mapped_size = request_size;

 return true;
}
#endif

#ifdef USE_DSM_WINDOWS
/*
 * Operating system primitives to support Windows shared memory.
 *
 * Windows shared memory implementation is done using file mapping
 * which can be backed by either physical file or system paging file.
 * Current implementation uses system paging file as other effects
 * like performance are not clear for physical file and it is used in similar
 * way for main shared memory in windows.
 *
 * A memory mapping object is a kernel object - they always get deleted when
 * the last reference to them goes away, either explicitly via a CloseHandle or
 * when the process containing the reference exits.
 */

static bool
dsm_impl_windows(dsm_op op, dsm_handle handle, Size request_size,
     void **impl_private, void **mapped_address,
     Size *mapped_size, int elevel)
{
 char    *address;
 HANDLE  hmap;
 char  name[64];
 MEMORY_BASIC_INFORMATION info;

 /*
  * Storing the shared memory segment in the Global\ namespace, can allow
  * any process running in any session to access that file mapping object
  * provided that the caller has the required access rights. But to avoid
  * issues faced in main shared memory, we are using the naming convention
  * similar to main shared memory. We can change here once issue mentioned
  * in GetSharedMemName is resolved.
 */

 snprintf(name, 64"%s.%u", SEGMENT_NAME_PREFIX, handle);

 /*
  * Handle teardown cases.  Since Windows automatically destroys the object
  * when no references remain, we can treat it the same as detach.
 */

 if (op == DSM_OP_DETACH || op == DSM_OP_DESTROY)
 {
  if (*mapped_address != NULL
   && UnmapViewOfFile(*mapped_address) == 0)
  {
   _dosmaperr(GetLastError());
   ereport(elevel,
     (errcode_for_dynamic_shared_memory(),
      errmsg("could not unmap shared memory segment \"%s\": %m",
       name)));
   return false;
  }
  if (*impl_private != NULL
   && CloseHandle(*impl_private) == 0)
  {
   _dosmaperr(GetLastError());
   ereport(elevel,
     (errcode_for_dynamic_shared_memory(),
      errmsg("could not remove shared memory segment \"%s\": %m",
       name)));
   return false;
  }

  *impl_private = NULL;
  *mapped_address = NULL;
  *mapped_size = 0;
  return true;
 }

 /* Create new segment or open an existing one for attach. */
 if (op == DSM_OP_CREATE)
 {
  DWORD  size_high;
  DWORD  size_low;
  DWORD  errcode;

  /* Shifts >= the width of the type are undefined. */
#ifdef _WIN64
  size_high = request_size >> 32;
#else
  size_high = 0;
#endif
  size_low = (DWORD) request_size;

  /* CreateFileMapping might not clear the error code on success */
  SetLastError(0);

  hmap = CreateFileMapping(INVALID_HANDLE_VALUE, /* Use the pagefile */
         NULL, /* Default security attrs */
         PAGE_READWRITE, /* Memory is read/write */
         size_high, /* Upper 32 bits of size */
         size_low, /* Lower 32 bits of size */
         name);

  errcode = GetLastError();
  if (errcode == ERROR_ALREADY_EXISTS || errcode == ERROR_ACCESS_DENIED)
  {
   /*
    * On Windows, when the segment already exists, a handle for the
    * existing segment is returned.  We must close it before
    * returning.  However, if the existing segment is created by a
    * service, then it returns ERROR_ACCESS_DENIED. We don't do
    * _dosmaperr here, so errno won't be modified.
 */

   if (hmap)
    CloseHandle(hmap);
   return false;
  }

  if (!hmap)
  {
   _dosmaperr(errcode);
   ereport(elevel,
     (errcode_for_dynamic_shared_memory(),
      errmsg("could not create shared memory segment \"%s\": %m",
       name)));
   return false;
  }
 }
 else
 {
  hmap = OpenFileMapping(FILE_MAP_WRITE | FILE_MAP_READ,
          FALSE/* do not inherit the name */
          name); /* name of mapping object */
  if (!hmap)
  {
   _dosmaperr(GetLastError());
   ereport(elevel,
     (errcode_for_dynamic_shared_memory(),
      errmsg("could not open shared memory segment \"%s\": %m",
       name)));
   return false;
  }
 }

 /* Map it. */
 address = MapViewOfFile(hmap, FILE_MAP_WRITE | FILE_MAP_READ,
       000);
 if (!address)
 {
  int   save_errno;

  _dosmaperr(GetLastError());
  /* Back out what's already been done. */
  save_errno = errno;
  CloseHandle(hmap);
  errno = save_errno;

  ereport(elevel,
    (errcode_for_dynamic_shared_memory(),
     errmsg("could not map shared memory segment \"%s\": %m",
      name)));
  return false;
 }

 /*
  * VirtualQuery gives size in page_size units, which is 4K for Windows. We
  * need size only when we are attaching, but it's better to get the size
  * when creating new segment to keep size consistent both for
  * DSM_OP_CREATE and DSM_OP_ATTACH.
 */

 if (VirtualQuery(address, &info, sizeof(info)) == 0)
 {
  int   save_errno;

  _dosmaperr(GetLastError());
  /* Back out what's already been done. */
  save_errno = errno;
  UnmapViewOfFile(address);
  CloseHandle(hmap);
  errno = save_errno;

  ereport(elevel,
    (errcode_for_dynamic_shared_memory(),
     errmsg("could not stat shared memory segment \"%s\": %m",
      name)));
  return false;
 }

 *mapped_address = address;
 *mapped_size = info.RegionSize;
 *impl_private = hmap;

 return true;
}
#endif

#ifdef USE_DSM_MMAP
/*
 * Operating system primitives to support mmap-based shared memory.
 *
 * Calling this "shared memory" is somewhat of a misnomer, because what
 * we're really doing is creating a bunch of files and mapping them into
 * our address space.  The operating system may feel obliged to
 * synchronize the contents to disk even if nothing is being paged out,
 * which will not serve us well.  The user can relocate the pg_dynshmem
 * directory to a ramdisk to avoid this problem, if available.
 */

static bool
dsm_impl_mmap(dsm_op op, dsm_handle handle, Size request_size,
     void **impl_private, void **mapped_address, Size *mapped_size,
     int elevel)
{
 char  name[64];
 int   flags;
 int   fd;
 char    *address;

 snprintf(name, 64, PG_DYNSHMEM_DIR "/" PG_DYNSHMEM_MMAP_FILE_PREFIX "%u",
    handle);

 /* Handle teardown cases. */
 if (op == DSM_OP_DETACH || op == DSM_OP_DESTROY)
 {
  if (*mapped_address != NULL
   && munmap(*mapped_address, *mapped_size) != 0)
  {
   ereport(elevel,
     (errcode_for_dynamic_shared_memory(),
      errmsg("could not unmap shared memory segment \"%s\": %m",
       name)));
   return false;
  }
  *mapped_address = NULL;
  *mapped_size = 0;
  if (op == DSM_OP_DESTROY && unlink(name) != 0)
  {
   ereport(elevel,
     (errcode_for_dynamic_shared_memory(),
      errmsg("could not remove shared memory segment \"%s\": %m",
       name)));
   return false;
  }
  return true;
 }

 /* Create new segment or open an existing one for attach. */
 flags = O_RDWR | (op == DSM_OP_CREATE ? O_CREAT | O_EXCL : 0);
 if ((fd = OpenTransientFile(name, flags)) == -1)
 {
  if (op == DSM_OP_ATTACH || errno != EEXIST)
   ereport(elevel,
     (errcode_for_dynamic_shared_memory(),
      errmsg("could not open shared memory segment \"%s\": %m",
       name)));
  return false;
 }

 /*
  * If we're attaching the segment, determine the current size; if we are
  * creating the segment, set the size to the requested value.
 */

 if (op == DSM_OP_ATTACH)
 {
  struct stat st;

  if (fstat(fd, &st) != 0)
  {
   int   save_errno;

   /* Back out what's already been done. */
   save_errno = errno;
   CloseTransientFile(fd);
   errno = save_errno;

   ereport(elevel,
     (errcode_for_dynamic_shared_memory(),
      errmsg("could not stat shared memory segment \"%s\": %m",
       name)));
   return false;
  }
  request_size = st.st_size;
 }
 else
 {
  /*
   * Allocate a buffer full of zeros.
   *
   * Note: palloc zbuffer, instead of just using a local char array, to
   * ensure it is reasonably well-aligned; this may save a few cycles
   * transferring data to the kernel.
 */

  char    *zbuffer = (char *) palloc0(ZBUFFER_SIZE);
  Size  remaining = request_size;
  bool  success = true;

  /*
   * Zero-fill the file. We have to do this the hard way to ensure that
   * all the file space has really been allocated, so that we don't
   * later seg fault when accessing the memory mapping.  This is pretty
   * pessimal.
 */

  while (success && remaining > 0)
  {
   Size  goal = remaining;

   if (goal > ZBUFFER_SIZE)
    goal = ZBUFFER_SIZE;
   pgstat_report_wait_start(WAIT_EVENT_DSM_FILL_ZERO_WRITE);
   if (write(fd, zbuffer, goal) == goal)
    remaining -= goal;
   else
    success = false;
   pgstat_report_wait_end();
  }

  if (!success)
  {
   int   save_errno;

   /* Back out what's already been done. */
   save_errno = errno;
   CloseTransientFile(fd);
   unlink(name);
   errno = save_errno ? save_errno : ENOSPC;

   ereport(elevel,
     (errcode_for_dynamic_shared_memory(),
      errmsg("could not resize shared memory segment \"%s\" to %zu bytes: %m",
       name, request_size)));
   return false;
  }
 }

 /* Map it. */
 address = mmap(NULL, request_size, PROT_READ | PROT_WRITE,
       MAP_SHARED | MAP_HASSEMAPHORE | MAP_NOSYNC, fd, 0);
 if (address == MAP_FAILED)
 {
  int   save_errno;

  /* Back out what's already been done. */
  save_errno = errno;
  CloseTransientFile(fd);
  if (op == DSM_OP_CREATE)
   unlink(name);
  errno = save_errno;

  ereport(elevel,
    (errcode_for_dynamic_shared_memory(),
     errmsg("could not map shared memory segment \"%s\": %m",
      name)));
  return false;
 }
 *mapped_address = address;
 *mapped_size = request_size;

 if (CloseTransientFile(fd) != 0)
 {
  ereport(elevel,
    (errcode_for_file_access(),
     errmsg("could not close shared memory segment \"%s\": %m",
      name)));
  return false;
 }

 return true;
}
#endif

/*
 * Implementation-specific actions that must be performed when a segment is to
 * be preserved even when no backend has it attached.
 *
 * Except on Windows, we don't need to do anything at all.  But since Windows
 * cleans up segments automatically when no references remain, we duplicate
 * the segment handle into the postmaster process.  The postmaster needn't
 * do anything to receive the handle; Windows transfers it automatically.
 */

void
dsm_impl_pin_segment(dsm_handle handle, void *impl_private,
      void **impl_private_pm_handle)
{
 switch (dynamic_shared_memory_type)
 {
#ifdef USE_DSM_WINDOWS
  case DSM_IMPL_WINDOWS:
   if (IsUnderPostmaster)
   {
    HANDLE  hmap;

    if (!DuplicateHandle(GetCurrentProcess(), impl_private,
          PostmasterHandle, &hmap, 0FALSE,
          DUPLICATE_SAME_ACCESS))
    {
     char  name[64];

     snprintf(name, 64"%s.%u", SEGMENT_NAME_PREFIX, handle);
     _dosmaperr(GetLastError());
     ereport(ERROR,
       (errcode_for_dynamic_shared_memory(),
        errmsg("could not duplicate handle for \"%s\": %m",
         name)));
    }

    /*
     * Here, we remember the handle that we created in the
     * postmaster process.  This handle isn't actually usable in
     * any process other than the postmaster, but that doesn't
     * matter.  We're just holding onto it so that, if the segment
     * is unpinned, dsm_impl_unpin_segment can close it.
 */

    *impl_private_pm_handle = hmap;
   }
   break;
#endif
  default:
   break;
 }
}

/*
 * Implementation-specific actions that must be performed when a segment is no
 * longer to be preserved, so that it will be cleaned up when all backends
 * have detached from it.
 *
 * Except on Windows, we don't need to do anything at all.  For Windows, we
 * close the extra handle that dsm_impl_pin_segment created in the
 * postmaster's process space.
 */

void
dsm_impl_unpin_segment(dsm_handle handle, void **impl_private)
{
 switch (dynamic_shared_memory_type)
 {
#ifdef USE_DSM_WINDOWS
  case DSM_IMPL_WINDOWS:
   if (IsUnderPostmaster)
   {
    if (*impl_private &&
     !DuplicateHandle(PostmasterHandle, *impl_private,
          NULL, NULL, 0FALSE,
          DUPLICATE_CLOSE_SOURCE))
    {
     char  name[64];

     snprintf(name, 64"%s.%u", SEGMENT_NAME_PREFIX, handle);
     _dosmaperr(GetLastError());
     ereport(ERROR,
       (errcode_for_dynamic_shared_memory(),
        errmsg("could not duplicate handle for \"%s\": %m",
         name)));
    }

    *impl_private = NULL;
   }
   break;
#endif
  default:
   break;
 }
}

static int
errcode_for_dynamic_shared_memory(void)
{
 if (errno == EFBIG || errno == ENOMEM)
  return errcode(ERRCODE_OUT_OF_MEMORY);
 else
  return errcode_for_file_access();
}

Messung V0.5 in Prozent
C=93 H=90 G=91

¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.19Bemerkung:  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=752002