typedef key_t IpcMemoryKey; /* shared memory key passed to shmget(2) */
typedef int IpcMemoryId; /* shared memory ID returned by shmget(2) */
static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size) {
IpcMemoryId shmid;
void *requestedAddress = NULL;
void *memAddress;
#ifdef EXEC_BACKEND
{
char *pg_shmem_addr = getenv("PG_SHMEM_ADDR");
if (pg_shmem_addr)
requestedAddress = (void *) strtoul(pg_shmem_addr, NULL, 0);
}
#endif
shmid = shmget(memKey, size, IPC_CREAT | IPC_EXCL | IPCProtection);
if (shmid < 0) {
int shmget_errno = errno;
/* 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 (shmget_errno == EEXIST || shmget_errno == EACCES
#ifdef EIDRM
|| shmget_errno == EIDRM
#endif
)
return NULL;
if (shmget_errno == EINVAL){
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);
}
}
/* Else complain and abort. */
errno = shmget_errno;
ereport(FATAL,(errmsg("could not create shared memory segment: %m"),errdetail("Failed system call was shmget(key=%lu, size=%zu, 0%o).",(unsigned long) memKey, size,IPC_CREAT | IPC_EXCL | IPCProtection),(shmget_errno == EINVAL) ? errhint("This error usually means that PostgreSQL's request for a shared memory segment exceeded your kernel's SHMMAX parameter, or possibly that it is less than your kernel's SHMMIN parameter.\nThe PostgreSQL documentation contains more information about shared memory configuration.") : 0, (shmget_errno == ENOMEM) ? errhint("This error usually means that PostgreSQL's request for a shared memory segment exceeded your kernel's SHMALL parameter. You might need to reconfigure the kernel with larger SHMALL.\nThe PostgreSQL documentation contains more information about shared memory configuration.") : 0, (shmget_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.\nThe PostgreSQL documentation contains more information about shared memory configuration.") : 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, requestedAddress, PG_SHMAT_FLAGS);
if (memAddress == (void *) -1)
elog(FATAL, "shmat(id=%d, addr=%p, flags=0x%x) failed: %m", shmid, requestedAddress, PG_SHMAT_FLAGS);
/* Register on-exit routine to detach new segment before deleting */
on_shmem_exit(IpcMemoryDetach, PointerGetDatum(memAddress));
/* Store shmem key and ID in data directory lockfile. Format to try to keep it the same length always (trailing junk in the lockfile won't hurt, but might confuse humans). */
{
char line[64];
sprintf(line, "%9lu %9lu", (unsigned long) memKey, (unsigned long) shmid);
AddToDataDirLockFile(LOCK_FILE_LINE_SHMEM_KEY, line);
}
return memAddress;
}