Ejemplo n.º 1
0
/*
 *	InternalIpcMemoryCreate(memKey, size)
 *
 * Attempt to create a new shared memory segment with the specified key.
 * Will fail (return NULL) if such a segment already exists.  If successful,
 * attach the segment to the current process and return its attached address.
 * On success, callbacks are registered with on_shmem_exit to detach and
 * delete the segment when on_shmem_exit is called.
 *
 * If we fail with a failure code other than collision-with-existing-segment,
 * print out an error and abort.  Other types of errors are not recoverable.
 */
static void *
InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size)
{
	IpcMemoryId shmid;
	void	   *memAddress;

	shmid = shmget(memKey, size, IPC_CREAT | IPC_EXCL | IPCProtection);

	if (shmid < 0)
	{
		/*
		 * Fail quietly if error indicates a collision with existing segment.
		 * One would expect EEXIST, given that we said IPC_EXCL, but perhaps
		 * we could get a permission violation instead?  Also, EIDRM might
		 * occur if an old seg is slated for destruction but not gone yet.
		 */
		if (errno == EEXIST || errno == EACCES
#ifdef EIDRM
			|| errno == EIDRM
#endif
			)
			return NULL;

		/*
		 * Else complain and abort
		 */
		ereport(FATAL,
				(errmsg("could not create shared memory segment: %m"),
		  errdetail("Failed system call was shmget(key=%lu, size=%lu, 0%o).",
					(unsigned long) memKey, (unsigned long) size,
					IPC_CREAT | IPC_EXCL | IPCProtection),
				 (errno == EINVAL) ?
				 errhint("This error usually means that PostgreSQL's request for a shared memory "
		  "segment exceeded your kernel's SHMMAX parameter.  You can either "
						 "reduce the request size or reconfigure the kernel with larger SHMMAX.  "
				  "To reduce the request size (currently %lu bytes), reduce "
			   "PostgreSQL's shared_buffers parameter (currently %d) and/or "
						 "its max_connections parameter (currently %d).\n"
						 "If the request size is already small, it's possible that it is less than "
						 "your kernel's SHMMIN parameter, in which case raising the request size or "
						 "reconfiguring SHMMIN is called for.\n"
		"The PostgreSQL documentation contains more information about shared "
						 "memory configuration.",
						 (unsigned long) size, NBuffers, MaxBackends) : 0,
				 (errno == ENOMEM) ?
				 errhint("This error usually means that PostgreSQL's request for a shared "
				   "memory segment exceeded available memory or swap space. "
				  "To reduce the request size (currently %lu bytes), reduce "
			   "PostgreSQL's shared_buffers parameter (currently %d) and/or "
						 "its max_connections parameter (currently %d).\n"
		"The PostgreSQL documentation contains more information about shared "
						 "memory configuration.",
						 (unsigned long) size, NBuffers, MaxBackends) : 0,
				 (errno == ENOSPC) ?
				 errhint("This error does *not* mean that you have run out of disk space. "
						 "It occurs either if all available shared memory IDs have been taken, "
						 "in which case you need to raise the SHMMNI parameter in your kernel, "
		  "or because the system's overall limit for shared memory has been "
				 "reached.  If you cannot increase the shared memory limit, "
		  "reduce PostgreSQL's shared memory request (currently %lu bytes), "
			"by reducing its shared_buffers parameter (currently %d) and/or "
						 "its max_connections parameter (currently %d).\n"
		"The PostgreSQL documentation contains more information about shared "
						 "memory configuration.",
						 (unsigned long) size, NBuffers, MaxBackends) : 0));
	}

	/* Register on-exit routine to delete the new segment */
	on_shmem_exit(IpcMemoryDelete, Int32GetDatum(shmid));

	/* OK, should be able to attach to the segment */
	memAddress = shmat(shmid, NULL, PG_SHMAT_FLAGS);

	if (memAddress == (void *) -1)
		elog(FATAL, "shmat(id=%d) failed: %m", shmid);

	/* Register on-exit routine to detach new segment before deleting */
	on_shmem_exit(IpcMemoryDetach, PointerGetDatum(memAddress));

	/* Record key and ID in lockfile for data directory. */
	RecordSharedMemoryInLockFile((unsigned long) memKey,
								 (unsigned long) shmid);

	return memAddress;
}
Ejemplo n.º 2
0
/*
 *	InternalIpcMemoryCreate(memKey, size)
 *
 * Attempt to create a new shared memory segment with the specified key.
 * Will fail (return NULL) if such a segment already exists.  If successful,
 * attach the segment to the current process and return its attached address.
 * On success, callbacks are registered with on_shmem_exit to detach and
 * delete the segment when on_shmem_exit is called.
 *
 * If we fail with a failure code other than collision-with-existing-segment,
 * print out an error and abort.  Other types of errors are not recoverable.
 */
static void *
InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size)
{
    IpcMemoryId shmid;
    void	   *memAddress;

    shmid = shmget(memKey, size, IPC_CREAT | IPC_EXCL | IPCProtection);

    if (shmid < 0)
    {
        /*
         * Fail quietly if error indicates a collision with existing segment.
         * One would expect EEXIST, given that we said IPC_EXCL, but perhaps
         * we could get a permission violation instead?  Also, EIDRM might
         * occur if an old seg is slated for destruction but not gone yet.
         */
        if (errno == EEXIST || errno == EACCES
#ifdef EIDRM
                || errno == EIDRM
#endif
           )
            return NULL;

        /*
         * Some BSD-derived kernels are known to return EINVAL, not EEXIST, if
         * there is an existing segment but it's smaller than "size" (this is
         * a result of poorly-thought-out ordering of error tests). To
         * distinguish between collision and invalid size in such cases, we
         * make a second try with size = 0.  These kernels do not test size
         * against SHMMIN in the preexisting-segment case, so we will not get
         * EINVAL a second time if there is such a segment.
         */
        if (errno == EINVAL)
        {
            int			save_errno = errno;

            shmid = shmget(memKey, 0, IPC_CREAT | IPC_EXCL | IPCProtection);

            if (shmid < 0)
            {
                /* As above, fail quietly if we verify a collision */
                if (errno == EEXIST || errno == EACCES
#ifdef EIDRM
                        || errno == EIDRM
#endif
                   )
                    return NULL;
                /* Otherwise, fall through to report the original error */
            }
            else
            {
                /*
                 * On most platforms we cannot get here because SHMMIN is
                 * greater than zero.  However, if we do succeed in creating a
                 * zero-size segment, free it and then fall through to report
                 * the original error.
                 */
                if (shmctl(shmid, IPC_RMID, NULL) < 0)
                    elog(LOG, "shmctl(%d, %d, 0) failed: %m",
                         (int) shmid, IPC_RMID);
            }

            errno = save_errno;
        }

        /*
         * Else complain and abort.
         *
         * Note: at this point EINVAL should mean that either SHMMIN or SHMMAX
         * is violated.  SHMALL violation might be reported as either ENOMEM
         * (BSDen) or ENOSPC (Linux); the Single Unix Spec fails to say which
         * it should be.  SHMMNI violation is ENOSPC, per spec.  Just plain
         * not-enough-RAM is ENOMEM.
         */
        ereport(FATAL,
                (errmsg("could not create shared memory segment: %m"),
                 errdetail("Failed system call was shmget(key=%lu, size=%lu, 0%o).",
                           (unsigned long) memKey, (unsigned long) size,
                           IPC_CREAT | IPC_EXCL | IPCProtection),
                 (errno == EINVAL) ?
                 errhint("This error usually means that PostgreSQL's request for a shared memory "
                         "segment exceeded your kernel's SHMMAX parameter.  You can either "
                         "reduce the request size or reconfigure the kernel with larger SHMMAX.  "
                         "To reduce the request size (currently %lu bytes), reduce "
                         "PostgreSQL's shared_buffers parameter (currently %d) and/or "
                         "its max_connections parameter (currently %d).\n"
                         "If the request size is already small, it's possible that it is less than "
                         "your kernel's SHMMIN parameter, in which case raising the request size or "
                         "reconfiguring SHMMIN is called for.\n"
                         "The PostgreSQL documentation contains more information about shared "
                         "memory configuration.",
                         (unsigned long) size, NBuffers, MaxBackends) : 0,
                 (errno == ENOMEM) ?
                 errhint("This error usually means that PostgreSQL's request for a shared "
                         "memory segment exceeded available memory or swap space, "
                         "or exceeded your kernel's SHMALL parameter.  You can either "
                         "reduce the request size or reconfigure the kernel with larger SHMALL.  "
                         "To reduce the request size (currently %lu bytes), reduce "
                         "PostgreSQL's shared_buffers parameter (currently %d) and/or "
                         "its max_connections parameter (currently %d).\n"
                         "The PostgreSQL documentation contains more information about shared "
                         "memory configuration.",
                         (unsigned long) size, NBuffers, MaxBackends) : 0,
                 (errno == ENOSPC) ?
                 errhint("This error does *not* mean that you have run out of disk space. "
                         "It occurs either if all available shared memory IDs have been taken, "
                         "in which case you need to raise the SHMMNI parameter in your kernel, "
                         "or because the system's overall limit for shared memory has been "
                         "reached.  If you cannot increase the shared memory limit, "
                         "reduce PostgreSQL's shared memory request (currently %lu bytes), "
                         "by reducing its shared_buffers parameter (currently %d) and/or "
                         "its max_connections parameter (currently %d).\n"
                         "The PostgreSQL documentation contains more information about shared "
                         "memory configuration.",
                         (unsigned long) size, NBuffers, MaxBackends) : 0));
    }

    /* Register on-exit routine to delete the new segment */
    on_shmem_exit(IpcMemoryDelete, Int32GetDatum(shmid));

    /* OK, should be able to attach to the segment */
    memAddress = shmat(shmid, NULL, PG_SHMAT_FLAGS);

    if (memAddress == (void *) -1)
        elog(FATAL, "shmat(id=%d) failed: %m", shmid);

    /* Register on-exit routine to detach new segment before deleting */
    on_shmem_exit(IpcMemoryDetach, PointerGetDatum(memAddress));

    /* Record key and ID in lockfile for data directory. */
    RecordSharedMemoryInLockFile((unsigned long) memKey,
                                 (unsigned long) shmid);

    return memAddress;
}