From ce51876f87f1e4317e25baf64184749448fcd033 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Thu, 30 Nov 2023 00:07:34 +0200
Subject: [PATCH v3 7/7] Refactor postmaster child process launching

- Move code related to launching backend processes to new source file,
  launch_backend.c

- Introduce new postmaster_child_launch() function that deals with the
  differences between EXEC_BACKEND and fork mode.

- Refactor the mechanism of passing information from the parent to
  child process. Instead of using different command-line arguments when
  launching the child process in EXEC_BACKEND mode, pass a
  variable-length blob of data along with all the global variables. The
  contents of that blob depend on the kind of child process being
  launched. In !EXEC_BACKEND mode, we use the same blob, but it's simply
  inherited from the parent to child process.

Reviewed-by: Tristan Partin, Andres Freund
Discussion: https://www.postgresql.org/message-id/7a59b073-5b5b-151e-7ed3-8b01ff7ce9ef@iki.fi
---
 src/backend/postmaster/Makefile             |    2 +-
 src/backend/postmaster/autovacuum.c         |  173 +--
 src/backend/postmaster/auxprocess.c         |   72 +-
 src/backend/postmaster/bgworker.c           |   14 +-
 src/backend/postmaster/bgwriter.c           |    9 +-
 src/backend/postmaster/checkpointer.c       |    9 +-
 src/backend/postmaster/fork_process.c       |  126 --
 src/backend/postmaster/launch_backend.c     | 1272 +++++++++++++++
 src/backend/postmaster/meson.build          |    2 +-
 src/backend/postmaster/pgarch.c             |    9 +-
 src/backend/postmaster/postmaster.c         | 1553 +++----------------
 src/backend/postmaster/startup.c            |    9 +-
 src/backend/postmaster/syslogger.c          |  275 ++--
 src/backend/postmaster/walwriter.c          |    9 +-
 src/backend/replication/logical/launcher.c  |    1 -
 src/backend/replication/walreceiver.c       |    9 +-
 src/backend/storage/ipc/shmem.c             |    2 +
 src/backend/utils/init/globals.c            |    1 +
 src/backend/utils/init/miscinit.c           |  133 --
 src/include/libpq/libpq-be.h                |   11 +-
 src/include/miscadmin.h                     |    1 -
 src/include/postmaster/autovacuum.h         |   12 +-
 src/include/postmaster/auxprocess.h         |    4 +-
 src/include/postmaster/bgworker_internals.h |    2 +-
 src/include/postmaster/bgwriter.h           |    4 +-
 src/include/postmaster/fork_process.h       |   17 -
 src/include/postmaster/pgarch.h             |    2 +-
 src/include/postmaster/postmaster.h         |   44 +-
 src/include/postmaster/startup.h            |    2 +-
 src/include/postmaster/syslogger.h          |    4 +-
 src/include/postmaster/walwriter.h          |    2 +-
 src/include/replication/walreceiver.h       |    2 +-
 src/tools/pgindent/typedefs.list            |    2 +
 33 files changed, 1787 insertions(+), 2002 deletions(-)
 delete mode 100644 src/backend/postmaster/fork_process.c
 create mode 100644 src/backend/postmaster/launch_backend.c
 delete mode 100644 src/include/postmaster/fork_process.h

diff --git a/src/backend/postmaster/Makefile b/src/backend/postmaster/Makefile
index 047448b34eb..fc88f5bae0b 100644
--- a/src/backend/postmaster/Makefile
+++ b/src/backend/postmaster/Makefile
@@ -18,8 +18,8 @@ OBJS = \
 	bgworker.o \
 	bgwriter.o \
 	checkpointer.o \
-	fork_process.o \
 	interrupt.o \
+	launch_backend.o \
 	pgarch.o \
 	postmaster.o \
 	startup.o \
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 2f54485c217..0dda47ba300 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -84,7 +84,6 @@
 #include "nodes/makefuncs.h"
 #include "pgstat.h"
 #include "postmaster/autovacuum.h"
-#include "postmaster/fork_process.h"
 #include "postmaster/interrupt.h"
 #include "postmaster/postmaster.h"
 #include "storage/bufmgr.h"
@@ -315,13 +314,6 @@ static WorkerInfo MyWorkerInfo = NULL;
 /* PID of launcher, valid only in worker while shutting down */
 int			AutovacuumLauncherPid = 0;
 
-#ifdef EXEC_BACKEND
-static pid_t avlauncher_forkexec(void);
-static pid_t avworker_forkexec(void);
-#endif
-NON_EXEC_STATIC void AutoVacWorkerMain(int argc, char *argv[]) pg_attribute_noreturn();
-NON_EXEC_STATIC void AutoVacLauncherMain(int argc, char *argv[]) pg_attribute_noreturn();
-
 static Oid	do_start_worker(void);
 static void HandleAutoVacLauncherInterrupts(void);
 static void AutoVacLauncherShutdown(void) pg_attribute_noreturn();
@@ -365,85 +357,21 @@ static void avl_sigusr2_handler(SIGNAL_ARGS);
  *					  AUTOVACUUM LAUNCHER CODE
  ********************************************************************/
 
-#ifdef EXEC_BACKEND
 /*
- * forkexec routine for the autovacuum launcher process.
- *
- * Format up the arglist, then fork and exec.
- */
-static pid_t
-avlauncher_forkexec(void)
-{
-	char	   *av[10];
-	int			ac = 0;
-
-	av[ac++] = "postgres";
-	av[ac++] = "--forkavlauncher";
-	av[ac++] = NULL;			/* filled in by postmaster_forkexec */
-	av[ac] = NULL;
-
-	Assert(ac < lengthof(av));
-
-	return postmaster_forkexec(ac, av);
-}
-
-/*
- * We need this set from the outside, before InitProcess is called
+ * Main loop for the autovacuum launcher process.
  */
 void
-AutovacuumLauncherIAm(void)
-{
-	am_autovacuum_launcher = true;
-}
-#endif
-
-/*
- * Main entry point for autovacuum launcher process, to be called from the
- * postmaster.
- */
-int
-StartAutoVacLauncher(void)
+AutoVacLauncherMain(char *startup_data, size_t startup_data_len)
 {
-	pid_t		AutoVacPID;
+	sigjmp_buf	local_sigjmp_buf;
 
-#ifdef EXEC_BACKEND
-	switch ((AutoVacPID = avlauncher_forkexec()))
-#else
-	switch ((AutoVacPID = fork_process()))
-#endif
+	/* Release postmaster's working memory context */
+	if (PostmasterContext)
 	{
-		case -1:
-			ereport(LOG,
-					(errmsg("could not fork autovacuum launcher process: %m")));
-			return 0;
-
-#ifndef EXEC_BACKEND
-		case 0:
-			/* in postmaster child ... */
-			InitPostmasterChild();
-
-			/* Close the postmaster's sockets */
-			ClosePostmasterPorts(false);
-
-			AutoVacLauncherMain(0, NULL);
-			break;
-#endif
-		default:
-			return (int) AutoVacPID;
+		MemoryContextDelete(PostmasterContext);
+		PostmasterContext = NULL;
 	}
 
-	/* shouldn't get here */
-	return 0;
-}
-
-/*
- * Main loop for the autovacuum launcher process.
- */
-NON_EXEC_STATIC void
-AutoVacLauncherMain(int argc, char *argv[])
-{
-	sigjmp_buf	local_sigjmp_buf;
-
 	am_autovacuum_launcher = true;
 
 	MyBackendType = B_AUTOVAC_LAUNCHER;
@@ -475,6 +403,9 @@ AutoVacLauncherMain(int argc, char *argv[])
 	pqsignal(SIGFPE, FloatExceptionHandler);
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	/* autovacuum needs this set before calling InitProcess */
+	am_autovacuum_launcher = true;
+
 	/*
 	 * Create a per-backend PGPROC struct in shared memory.  We must do this
 	 * before we can use LWLocks or access any shared memory.
@@ -1432,87 +1363,22 @@ avl_sigusr2_handler(SIGNAL_ARGS)
  *					  AUTOVACUUM WORKER CODE
  ********************************************************************/
 
-#ifdef EXEC_BACKEND
-/*
- * forkexec routines for the autovacuum worker.
- *
- * Format up the arglist, then fork and exec.
- */
-static pid_t
-avworker_forkexec(void)
-{
-	char	   *av[10];
-	int			ac = 0;
-
-	av[ac++] = "postgres";
-	av[ac++] = "--forkavworker";
-	av[ac++] = NULL;			/* filled in by postmaster_forkexec */
-	av[ac] = NULL;
-
-	Assert(ac < lengthof(av));
-
-	return postmaster_forkexec(ac, av);
-}
-
 /*
- * We need this set from the outside, before InitProcess is called
+ * AutoVacWorkerMain
  */
 void
-AutovacuumWorkerIAm(void)
+AutoVacWorkerMain(char *startup_data, size_t startup_data_len)
 {
-	am_autovacuum_worker = true;
-}
-#endif
-
-/*
- * Main entry point for autovacuum worker process.
- *
- * This code is heavily based on pgarch.c, q.v.
- */
-int
-StartAutoVacWorker(void)
-{
-	pid_t		worker_pid;
+	sigjmp_buf	local_sigjmp_buf;
+	Oid			dbid;
 
-#ifdef EXEC_BACKEND
-	switch ((worker_pid = avworker_forkexec()))
-#else
-	switch ((worker_pid = fork_process()))
-#endif
+	/* Release postmaster's working memory context */
+	if (PostmasterContext)
 	{
-		case -1:
-			ereport(LOG,
-					(errmsg("could not fork autovacuum worker process: %m")));
-			return 0;
-
-#ifndef EXEC_BACKEND
-		case 0:
-			/* in postmaster child ... */
-			InitPostmasterChild();
-
-			/* Close the postmaster's sockets */
-			ClosePostmasterPorts(false);
-
-			AutoVacWorkerMain(0, NULL);
-			break;
-#endif
-		default:
-			return (int) worker_pid;
+		MemoryContextDelete(PostmasterContext);
+		PostmasterContext = NULL;
 	}
 
-	/* shouldn't get here */
-	return 0;
-}
-
-/*
- * AutoVacWorkerMain
- */
-NON_EXEC_STATIC void
-AutoVacWorkerMain(int argc, char *argv[])
-{
-	sigjmp_buf	local_sigjmp_buf;
-	Oid			dbid;
-
 	am_autovacuum_worker = true;
 
 	MyBackendType = B_AUTOVAC_WORKER;
@@ -1543,6 +1409,9 @@ AutoVacWorkerMain(int argc, char *argv[])
 	pqsignal(SIGFPE, FloatExceptionHandler);
 	pqsignal(SIGCHLD, SIG_DFL);
 
+	/* autovacuum needs this set before calling InitProcess */
+	am_autovacuum_worker = true;
+
 	/*
 	 * Create a per-backend PGPROC struct in shared memory.  We must do this
 	 * before we can use LWLocks or access any shared memory.
diff --git a/src/backend/postmaster/auxprocess.c b/src/backend/postmaster/auxprocess.c
index bae6f68c402..d4acd5146f4 100644
--- a/src/backend/postmaster/auxprocess.c
+++ b/src/backend/postmaster/auxprocess.c
@@ -46,45 +46,23 @@ AuxProcType MyAuxProcType = NotAnAuxProcess;	/* declared in miscadmin.h */
 
 
 /*
- *	 AuxiliaryProcessMain
+ *	 AuxiliaryProcessInit
  *
- *	 The main entry point for auxiliary processes, such as the bgwriter,
+ *	 Common initialization code for auxiliary processes, such as the bgwriter,
  *	 walwriter, walreceiver, bootstrapper and the shared memory checker code.
- *
- *	 This code is here just because of historical reasons.
  */
 void
-AuxiliaryProcessMain(AuxProcType auxtype)
+AuxiliaryProcessInit(void)
 {
-	Assert(IsUnderPostmaster);
-
-	MyAuxProcType = auxtype;
-
-	switch (MyAuxProcType)
+	/* Release postmaster's working memory context */
+	if (PostmasterContext)
 	{
-		case StartupProcess:
-			MyBackendType = B_STARTUP;
-			break;
-		case ArchiverProcess:
-			MyBackendType = B_ARCHIVER;
-			break;
-		case BgWriterProcess:
-			MyBackendType = B_BG_WRITER;
-			break;
-		case CheckpointerProcess:
-			MyBackendType = B_CHECKPOINTER;
-			break;
-		case WalWriterProcess:
-			MyBackendType = B_WAL_WRITER;
-			break;
-		case WalReceiverProcess:
-			MyBackendType = B_WAL_RECEIVER;
-			break;
-		default:
-			elog(PANIC, "unrecognized process type: %d", (int) MyAuxProcType);
-			MyBackendType = B_INVALID;
+		MemoryContextDelete(PostmasterContext);
+		PostmasterContext = NULL;
 	}
 
+	Assert(IsUnderPostmaster);
+
 	init_ps_display(NULL);
 
 	SetProcessingMode(BootstrapProcessing);
@@ -122,7 +100,6 @@ AuxiliaryProcessMain(AuxProcType auxtype)
 	 */
 	CreateAuxProcessResourceOwner();
 
-
 	/* Initialize backend status information */
 	pgstat_beinit();
 	pgstat_bestart();
@@ -131,37 +108,6 @@ AuxiliaryProcessMain(AuxProcType auxtype)
 	before_shmem_exit(ShutdownAuxiliaryProcess, 0);
 
 	SetProcessingMode(NormalProcessing);
-
-	switch (MyAuxProcType)
-	{
-		case StartupProcess:
-			StartupProcessMain();
-			proc_exit(1);
-
-		case ArchiverProcess:
-			PgArchiverMain();
-			proc_exit(1);
-
-		case BgWriterProcess:
-			BackgroundWriterMain();
-			proc_exit(1);
-
-		case CheckpointerProcess:
-			CheckpointerMain();
-			proc_exit(1);
-
-		case WalWriterProcess:
-			WalWriterMain();
-			proc_exit(1);
-
-		case WalReceiverProcess:
-			WalReceiverMain();
-			proc_exit(1);
-
-		default:
-			elog(PANIC, "unrecognized process type: %d", (int) MyAuxProcType);
-			proc_exit(1);
-	}
 }
 
 /*
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 3fbc8f503cb..a27a658c025 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -723,17 +723,27 @@ bgworker_die(SIGNAL_ARGS)
  * Main entry point for background worker processes.
  */
 void
-BackgroundWorkerMain(void)
+BackgroundWorkerMain(char *startup_data, size_t startup_data_len)
 {
 	sigjmp_buf	local_sigjmp_buf;
-	BackgroundWorker *worker = MyBgworkerEntry;
+	BackgroundWorker *worker;
 	bgworker_main_type entrypt;
 
+	/* Release postmaster's working memory context */
+	if (PostmasterContext)
+	{
+		MemoryContextDelete(PostmasterContext);
+		PostmasterContext = NULL;
+	}
+
+	Assert(startup_data_len == sizeof(BackgroundWorker));
+	worker = (BackgroundWorker *) startup_data;
 	if (worker == NULL)
 		elog(FATAL, "unable to find bgworker entry");
 
 	IsBackgroundWorker = true;
 
+	MyBgworkerEntry = worker;
 	MyBackendType = B_BG_WORKER;
 	init_ps_display(worker->bgw_name);
 
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index d02dc17b9c1..027590f824b 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -36,6 +36,7 @@
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
 #include "pgstat.h"
+#include "postmaster/auxprocess.h"
 #include "postmaster/bgwriter.h"
 #include "postmaster/interrupt.h"
 #include "storage/buf_internals.h"
@@ -88,13 +89,19 @@ static XLogRecPtr last_snapshot_lsn = InvalidXLogRecPtr;
  * basic execution environment, but not enabled signals yet.
  */
 void
-BackgroundWriterMain(void)
+BackgroundWriterMain(char *startup_data, size_t startup_data_len)
 {
 	sigjmp_buf	local_sigjmp_buf;
 	MemoryContext bgwriter_context;
 	bool		prev_hibernate;
 	WritebackContext wb_context;
 
+	Assert(startup_data_len == 0);
+
+	MyAuxProcType = BgWriterProcess;
+	MyBackendType = B_BG_WRITER;
+	AuxiliaryProcessInit();
+
 	/*
 	 * Properly accept or ignore signals that might be sent to us.
 	 */
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 42c807d3528..363c105cee6 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -42,6 +42,7 @@
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
 #include "pgstat.h"
+#include "postmaster/auxprocess.h"
 #include "postmaster/bgwriter.h"
 #include "postmaster/interrupt.h"
 #include "replication/syncrep.h"
@@ -169,11 +170,17 @@ static void ReqCheckpointHandler(SIGNAL_ARGS);
  * basic execution environment, but not enabled signals yet.
  */
 void
-CheckpointerMain(void)
+CheckpointerMain(char *startup_data, size_t startup_data_len)
 {
 	sigjmp_buf	local_sigjmp_buf;
 	MemoryContext checkpointer_context;
 
+	Assert(startup_data_len == 0);
+
+	MyAuxProcType = CheckpointerProcess;
+	MyBackendType = B_CHECKPOINTER;
+	AuxiliaryProcessInit();
+
 	CheckpointerShmem->checkpointer_pid = MyProcPid;
 
 	/*
diff --git a/src/backend/postmaster/fork_process.c b/src/backend/postmaster/fork_process.c
deleted file mode 100644
index 6f9c2765d68..00000000000
--- a/src/backend/postmaster/fork_process.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * fork_process.c
- *	 A simple wrapper on top of fork(). This does not handle the
- *	 EXEC_BACKEND case; it might be extended to do so, but it would be
- *	 considerably more complex.
- *
- * Copyright (c) 1996-2023, PostgreSQL Global Development Group
- *
- * IDENTIFICATION
- *	  src/backend/postmaster/fork_process.c
- */
-#include "postgres.h"
-
-#include <fcntl.h>
-#include <signal.h>
-#include <time.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <unistd.h>
-
-#include "libpq/pqsignal.h"
-#include "postmaster/fork_process.h"
-
-#ifndef WIN32
-/*
- * Wrapper for fork(). Return values are the same as those for fork():
- * -1 if the fork failed, 0 in the child process, and the PID of the
- * child in the parent process.  Signals are blocked while forking, so
- * the child must unblock.
- */
-pid_t
-fork_process(void)
-{
-	pid_t		result;
-	const char *oomfilename;
-	sigset_t	save_mask;
-
-#ifdef LINUX_PROFILE
-	struct itimerval prof_itimer;
-#endif
-
-	/*
-	 * Flush stdio channels just before fork, to avoid double-output problems.
-	 */
-	fflush(NULL);
-
-#ifdef LINUX_PROFILE
-
-	/*
-	 * Linux's fork() resets the profiling timer in the child process. If we
-	 * want to profile child processes then we need to save and restore the
-	 * timer setting.  This is a waste of time if not profiling, however, so
-	 * only do it if commanded by specific -DLINUX_PROFILE switch.
-	 */
-	getitimer(ITIMER_PROF, &prof_itimer);
-#endif
-
-	/*
-	 * We start postmaster children with signals blocked.  This allows them to
-	 * install their own handlers before unblocking, to avoid races where they
-	 * might run the postmaster's handler and miss an important control
-	 * signal. With more analysis this could potentially be relaxed.
-	 */
-	sigprocmask(SIG_SETMASK, &BlockSig, &save_mask);
-	result = fork();
-	if (result == 0)
-	{
-		/* fork succeeded, in child */
-#ifdef LINUX_PROFILE
-		setitimer(ITIMER_PROF, &prof_itimer, NULL);
-#endif
-
-		/*
-		 * By default, Linux tends to kill the postmaster in out-of-memory
-		 * situations, because it blames the postmaster for the sum of child
-		 * process sizes *including shared memory*.  (This is unbelievably
-		 * stupid, but the kernel hackers seem uninterested in improving it.)
-		 * Therefore it's often a good idea to protect the postmaster by
-		 * setting its OOM score adjustment negative (which has to be done in
-		 * a root-owned startup script).  Since the adjustment is inherited by
-		 * child processes, this would ordinarily mean that all the
-		 * postmaster's children are equally protected against OOM kill, which
-		 * is not such a good idea.  So we provide this code to allow the
-		 * children to change their OOM score adjustments again.  Both the
-		 * file name to write to and the value to write are controlled by
-		 * environment variables, which can be set by the same startup script
-		 * that did the original adjustment.
-		 */
-		oomfilename = getenv("PG_OOM_ADJUST_FILE");
-
-		if (oomfilename != NULL)
-		{
-			/*
-			 * Use open() not stdio, to ensure we control the open flags. Some
-			 * Linux security environments reject anything but O_WRONLY.
-			 */
-			int			fd = open(oomfilename, O_WRONLY, 0);
-
-			/* We ignore all errors */
-			if (fd >= 0)
-			{
-				const char *oomvalue = getenv("PG_OOM_ADJUST_VALUE");
-				int			rc;
-
-				if (oomvalue == NULL)	/* supply a useful default */
-					oomvalue = "0";
-
-				rc = write(fd, oomvalue, strlen(oomvalue));
-				(void) rc;
-				close(fd);
-			}
-		}
-
-		/* do post-fork initialization for random number generation */
-		pg_strong_random_init();
-	}
-	else
-	{
-		/* in parent, restore signal mask */
-		sigprocmask(SIG_SETMASK, &save_mask, NULL);
-	}
-
-	return result;
-}
-
-#endif							/* ! WIN32 */
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
new file mode 100644
index 00000000000..fac658ba687
--- /dev/null
+++ b/src/backend/postmaster/launch_backend.c
@@ -0,0 +1,1272 @@
+/*-------------------------------------------------------------------------
+ *
+ * launch_backend.c
+ *	  Functions for launching backends and other postmaster child
+ *	  processes.
+ *
+ * On Unix systems, a new child process is launched with fork().  It inherits
+ * all the global variables and data structures that had been initialized in
+ * the postmaster.  After forking, the child process closes the file
+ * descriptors that are not needed in the child process, and sets up the
+ * mechanism to detect death of the parent postmaster process, etc.  After
+ * that, it calls the right Main function depending on the kind of child
+ * process.
+ *
+ * In EXEC_BACKEND mode, which is used on Windows but can be enabled on other
+ * platforms for testing, the child process is launched by fork() + exec() (or
+ * CreateProcess() on Windows).  It does not inherit the state from the
+ * postmaster, so it needs to re-attach to the shared memory, re-initialize
+ * global variables, re-load the config file etc.
+ *
+ *
+ * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/postmaster/launch_backend.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include <unistd.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <netdb.h>
+#include <limits.h>
+
+#include "access/transam.h"
+#include "access/xlog.h"
+#include "common/file_utils.h"
+#include "libpq/libpq-be.h"
+#include "libpq/pqsignal.h"
+#include "miscadmin.h"
+#include "nodes/queryjumble.h"
+#include "port.h"
+#include "postmaster/autovacuum.h"
+#include "postmaster/auxprocess.h"
+#include "postmaster/bgworker_internals.h"
+#include "postmaster/bgwriter.h"
+#include "postmaster/interrupt.h"
+#include "postmaster/pgarch.h"
+#include "postmaster/postmaster.h"
+#include "postmaster/startup.h"
+#include "postmaster/syslogger.h"
+#include "postmaster/walwriter.h"
+#include "replication/walreceiver.h"
+#include "storage/fd.h"
+#include "storage/ipc.h"
+#include "storage/pg_shmem.h"
+#include "storage/pmsignal.h"
+#include "storage/proc.h"
+#include "tcop/tcopprot.h"
+#include "utils/builtins.h"
+#include "utils/datetime.h"
+#include "utils/guc.h"
+#include "utils/memutils.h"
+#include "utils/ps_status.h"
+#include "utils/timeout.h"
+#include "utils/timestamp.h"
+
+#ifdef EXEC_BACKEND
+#include "storage/spin.h"
+#endif
+
+
+/* Type for a socket that can be inherited to a client process */
+#ifdef WIN32
+typedef struct
+{
+	SOCKET		origsocket;		/* Original socket value, or PGINVALID_SOCKET
+								 * if not a socket */
+	WSAPROTOCOL_INFO wsainfo;
+} InheritableSocket;
+#else
+typedef int InheritableSocket;
+#endif
+
+#ifdef EXEC_BACKEND
+
+/*
+ * Structure contains all global variables passed to exec:ed backends
+ */
+typedef struct
+{
+	char		DataDir[MAXPGPATH];
+	int32		MyCancelKey;
+	int			MyPMChildSlot;
+#ifndef WIN32
+	unsigned long UsedShmemSegID;
+#else
+	void	   *ShmemProtectiveRegion;
+	HANDLE		UsedShmemSegID;
+#endif
+	void	   *UsedShmemSegAddr;
+	slock_t    *ShmemLock;
+	VariableCache ShmemVariableCache;
+	struct bkend *ShmemBackendArray;
+#ifndef HAVE_SPINLOCKS
+	PGSemaphore *SpinlockSemaArray;
+#endif
+	int			NamedLWLockTrancheRequests;
+	NamedLWLockTranche *NamedLWLockTrancheArray;
+	LWLockPadded *MainLWLockArray;
+	slock_t    *ProcStructLock;
+	PROC_HDR   *ProcGlobal;
+	PGPROC	   *AuxiliaryProcs;
+	PGPROC	   *PreparedXactProcs;
+	PMSignalData *PMSignalState;
+	pid_t		PostmasterPid;
+	TimestampTz PgStartTime;
+	TimestampTz PgReloadTime;
+	pg_time_t	first_syslogger_file_time;
+	bool		redirection_done;
+	bool		IsBinaryUpgrade;
+	bool		query_id_enabled;
+	int			max_safe_fds;
+	int			MaxBackends;
+#ifdef WIN32
+	HANDLE		PostmasterHandle;
+	HANDLE		initial_signal_pipe;
+	HANDLE		syslogPipe[2];
+#else
+	int			postmaster_alive_fds[2];
+	int			syslogPipe[2];
+#endif
+	char		my_exec_path[MAXPGPATH];
+	char		pkglib_path[MAXPGPATH];
+
+	/*
+	 * These are only used by backend processes, but it's here because passing
+	 * a socket needs some special handling on Windows. 'client_sock' is an
+	 * explicit argument to postmaster_child_launch, but is stored in
+	 * MyClientSocket in the child process.
+	 */
+	ClientSocket client_sock;
+	InheritableSocket inh_sock;
+
+	size_t		startup_data_len;
+	/* startup data follows */
+	char		startup_data[FLEXIBLE_ARRAY_MEMBER];
+} BackendParameters;
+
+#define SizeOfBackendParameters(startup_data_len) (offsetof(BackendParameters, startup_data) + startup_data_len)
+
+static void read_backend_variables(char *id, char **startup_data, size_t *startup_data_len);
+static void restore_backend_variables(BackendParameters *param);
+
+static bool save_backend_variables(BackendParameters *param, ClientSocket *client_sock,
+#ifdef WIN32
+								   HANDLE childProcess, pid_t childPid,
+#endif
+								   char *startup_data, size_t startup_data_len);
+
+#endif							/* EXEC_BACKEND */
+
+static void InitPostmasterChild(bool am_syslogger);
+
+
+#ifdef EXEC_BACKEND
+static pid_t internal_forkexec(const char *main_fn_name, char *startup_data, size_t startup_data_len, ClientSocket *client_sock);
+#endif							/* EXEC_BACKEND */
+
+#ifndef WIN32
+static pid_t fork_process(void);
+#endif
+
+/*
+ * Information needed to launch different kinds of child processes.
+ */
+static const struct
+{
+	const char *name;
+	void		(*main_fn) (char *startup_data, size_t startup_data_len);
+	bool		shmem_attach;
+}			entry_kinds[] = {
+	[PMC_BACKEND] = {"backend", BackendMain, true},
+
+	[PMC_AV_LAUNCHER] = {"autovacuum launcher", AutoVacLauncherMain, true},
+	[PMC_AV_WORKER] = {"autovacuum worker", AutoVacWorkerMain, true},
+	[PMC_BGWORKER] = {"bgworker", BackgroundWorkerMain, true},
+	[PMC_SYSLOGGER] = {"syslogger", SysLoggerMain, false},
+
+	[PMC_STARTUP] = {"startup", StartupProcessMain, true},
+	[PMC_BGWRITER] = {"bgwriter", BackgroundWriterMain, true},
+	[PMC_ARCHIVER] = {"archiver", PgArchiverMain, true},
+	[PMC_CHECKPOINTER] = {"checkpointer", CheckpointerMain, true},
+	[PMC_WAL_WRITER] = {"wal_writer", WalWriterMain, true},
+	[PMC_WAL_RECEIVER] = {"wal_receiver", WalReceiverMain, true},
+};
+
+const char *
+PostmasterChildName(PostmasterChildType child_type)
+{
+	Assert(child_type >= 0 && child_type < lengthof(entry_kinds));
+	return entry_kinds[child_type].name;
+}
+
+/*
+ * Start a new postmaster child process.
+ *
+ * The child process will be restored to roughly the same state, whether
+ * EXEC_BACKEND is used or not: it will be attached to shared memory, and fds
+ * and other resources that we've inherited from postmaster that are not
+ * needed in a child process have been closed.
+ *
+ * 'startup_data' is an optional contiguous chunk of data that is passed to
+ * the child process.
+ */
+pid_t
+postmaster_child_launch(PostmasterChildType child_type, char *startup_data, size_t startup_data_len, ClientSocket *client_sock)
+{
+	pid_t		pid;
+
+	Assert(child_type >= 0 && child_type < lengthof(entry_kinds));
+	Assert(IsPostmasterEnvironment && !IsUnderPostmaster);
+
+#ifdef EXEC_BACKEND
+	pid = internal_forkexec(entry_kinds[child_type].name, startup_data, startup_data_len, client_sock);
+	/* the child process will arrive in SubPostmasterMain */
+#else							/* !EXEC_BACKEND */
+	pid = fork_process();
+	if (pid == 0)				/* child */
+	{
+		/* Detangle from postmaster */
+		InitPostmasterChild(child_type == PMC_SYSLOGGER);
+
+		/*
+		 * Before blowing away PostmasterContext (in the Main function), save
+		 * the startup data.
+		 */
+		MemoryContextSwitchTo(TopMemoryContext);
+		if (startup_data != NULL)
+		{
+			char	   *cp = palloc(startup_data_len);
+
+			memcpy(cp, startup_data, startup_data_len);
+			startup_data = cp;
+		}
+
+		if (client_sock)
+		{
+			MyClientSocket = palloc(sizeof(ClientSocket));
+			memcpy(MyClientSocket, client_sock, sizeof(ClientSocket));
+		}
+
+		entry_kinds[child_type].main_fn(startup_data, startup_data_len);
+		Assert(false);
+	}
+#endif							/* EXEC_BACKEND */
+	return pid;
+}
+
+#ifndef WIN32
+/*
+ * Wrapper for fork().  Performs initialization steps that are the same in
+ * !EXEC_BACKEND and EXEC_BACKEND modes.
+ *
+ * Return values are the same as those for fork(): -1 if the fork failed, 0 in
+ * the child process, and the PID of the child in the parent process.  Signals
+ * are blocked while forking, so the child must unblock.
+ */
+static pid_t
+fork_process(void)
+{
+	pid_t		result;
+	const char *oomfilename;
+	sigset_t	save_mask;
+
+#ifdef LINUX_PROFILE
+	struct itimerval prof_itimer;
+#endif
+
+	/*
+	 * Flush stdio channels just before fork, to avoid double-output problems.
+	 */
+	fflush(NULL);
+
+#ifdef LINUX_PROFILE
+
+	/*
+	 * Linux's fork() resets the profiling timer in the child process. If we
+	 * want to profile child processes then we need to save and restore the
+	 * timer setting.  This is a waste of time if not profiling, however, so
+	 * only do it if commanded by specific -DLINUX_PROFILE switch.
+	 */
+	getitimer(ITIMER_PROF, &prof_itimer);
+#endif
+
+	/*
+	 * We start postmaster children with signals blocked.  This allows them to
+	 * install their own handlers before unblocking, to avoid races where they
+	 * might run the postmaster's handler and miss an important control
+	 * signal. With more analysis this could potentially be relaxed.
+	 */
+	sigprocmask(SIG_SETMASK, &BlockSig, &save_mask);
+	result = fork();
+	if (result == 0)
+	{
+		/* fork succeeded, in child */
+#ifdef LINUX_PROFILE
+		setitimer(ITIMER_PROF, &prof_itimer, NULL);
+#endif
+
+		/*
+		 * By default, Linux tends to kill the postmaster in out-of-memory
+		 * situations, because it blames the postmaster for the sum of child
+		 * process sizes *including shared memory*.  (This is unbelievably
+		 * stupid, but the kernel hackers seem uninterested in improving it.)
+		 * Therefore it's often a good idea to protect the postmaster by
+		 * setting its OOM score adjustment negative (which has to be done in
+		 * a root-owned startup script).  Since the adjustment is inherited by
+		 * child processes, this would ordinarily mean that all the
+		 * postmaster's children are equally protected against OOM kill, which
+		 * is not such a good idea.  So we provide this code to allow the
+		 * children to change their OOM score adjustments again.  Both the
+		 * file name to write to and the value to write are controlled by
+		 * environment variables, which can be set by the same startup script
+		 * that did the original adjustment.
+		 */
+		oomfilename = getenv("PG_OOM_ADJUST_FILE");
+
+		if (oomfilename != NULL)
+		{
+			/*
+			 * Use open() not stdio, to ensure we control the open flags. Some
+			 * Linux security environments reject anything but O_WRONLY.
+			 */
+			int			fd = open(oomfilename, O_WRONLY, 0);
+
+			/* We ignore all errors */
+			if (fd >= 0)
+			{
+				const char *oomvalue = getenv("PG_OOM_ADJUST_VALUE");
+				int			rc;
+
+				if (oomvalue == NULL)	/* supply a useful default */
+					oomvalue = "0";
+
+				rc = write(fd, oomvalue, strlen(oomvalue));
+				(void) rc;
+				close(fd);
+			}
+		}
+
+		/* do post-fork initialization for random number generation */
+		pg_strong_random_init();
+	}
+	else
+	{
+		/* in parent, restore signal mask */
+		sigprocmask(SIG_SETMASK, &save_mask, NULL);
+	}
+
+	return result;
+}
+
+#endif							/* !WIN32 */
+
+#ifdef EXEC_BACKEND
+#ifndef WIN32
+
+/*
+ * internal_forkexec non-win32 implementation
+ *
+ * - writes out backend variables to the parameter file
+ * - fork():s, and then exec():s the child process
+ */
+static pid_t
+internal_forkexec(const char *entry_name, char *startup_data, size_t startup_data_len, ClientSocket *client_sock)
+{
+	static unsigned long tmpBackendFileNum = 0;
+	pid_t		pid;
+	char		tmpfilename[MAXPGPATH];
+	size_t		paramsz;
+	BackendParameters *param;
+	FILE	   *fp;
+	char	   *argv[4];
+	char		forkav[MAXPGPATH];
+
+	paramsz = SizeOfBackendParameters(startup_data_len);
+	param = palloc(paramsz);
+	if (!save_backend_variables(param, client_sock, startup_data, startup_data_len))
+	{
+		pfree(param);
+		return -1;				/* log made by save_backend_variables */
+	}
+
+	/* Calculate name for temp file */
+	snprintf(tmpfilename, MAXPGPATH, "%s/%s.backend_var.%d.%lu",
+			 PG_TEMP_FILES_DIR, PG_TEMP_FILE_PREFIX,
+			 MyProcPid, ++tmpBackendFileNum);
+
+	/* Open file */
+	fp = AllocateFile(tmpfilename, PG_BINARY_W);
+	if (!fp)
+	{
+		/*
+		 * As in OpenTemporaryFileInTablespace, try to make the temp-file
+		 * directory, ignoring errors.
+		 */
+		(void) MakePGDirectory(PG_TEMP_FILES_DIR);
+
+		fp = AllocateFile(tmpfilename, PG_BINARY_W);
+		if (!fp)
+		{
+			ereport(LOG,
+					(errcode_for_file_access(),
+					 errmsg("could not create file \"%s\": %m",
+							tmpfilename)));
+			return -1;
+		}
+	}
+
+	if (fwrite(param, paramsz, 1, fp) != 1)
+	{
+		ereport(LOG,
+				(errcode_for_file_access(),
+				 errmsg("could not write to file \"%s\": %m", tmpfilename)));
+		FreeFile(fp);
+		return -1;
+	}
+
+	/* Release file */
+	if (FreeFile(fp))
+	{
+		ereport(LOG,
+				(errcode_for_file_access(),
+				 errmsg("could not write to file \"%s\": %m", tmpfilename)));
+		return -1;
+	}
+
+	/* set up argv properly */
+	argv[0] = "postgres";
+	snprintf(forkav, MAXPGPATH, "--forkchild=%s", entry_name);
+	argv[1] = forkav;
+	/* Insert temp file name after --forkchild argument */
+	argv[2] = tmpfilename;
+	argv[3] = NULL;
+
+	/* Fire off execv in child */
+	if ((pid = fork_process()) == 0)
+	{
+		if (execv(postgres_exec_path, argv) < 0)
+		{
+			ereport(LOG,
+					(errmsg("could not execute server process \"%s\": %m",
+							postgres_exec_path)));
+			/* We're already in the child process here, can't return */
+			exit(1);
+		}
+	}
+
+	return pid;					/* Parent returns pid, or -1 on fork failure */
+}
+#else							/* WIN32 */
+
+/*
+ * internal_forkexec win32 implementation
+ *
+ * - starts backend using CreateProcess(), in suspended state
+ * - writes out backend variables to the parameter file
+ *	- during this, duplicates handles and sockets required for
+ *	  inheritance into the new process
+ * - resumes execution of the new process once the backend parameter
+ *	 file is complete.
+ */
+static pid_t
+internal_forkexec(const char *entry_name, char *startup_data, size_t startup_data_len, ClientSocket *client_sock)
+{
+	int			retry_count = 0;
+	STARTUPINFO si;
+	PROCESS_INFORMATION pi;
+	char		cmdLine[MAXPGPATH * 2];
+	HANDLE		paramHandle;
+	BackendParameters *param;
+	SECURITY_ATTRIBUTES sa;
+	size_t		paramsz;
+	char		paramHandleStr[32];
+	int			l;
+
+	paramsz = SizeOfBackendParameters(startup_data_len);
+
+	/* Resume here if we need to retry */
+retry:
+
+	/* Set up shared memory for parameter passing */
+	ZeroMemory(&sa, sizeof(sa));
+	sa.nLength = sizeof(sa);
+	sa.bInheritHandle = TRUE;
+	paramHandle = CreateFileMapping(INVALID_HANDLE_VALUE,
+									&sa,
+									PAGE_READWRITE,
+									0,
+									paramsz,
+									NULL);
+	if (paramHandle == INVALID_HANDLE_VALUE)
+	{
+		ereport(LOG,
+				(errmsg("could not create backend parameter file mapping: error code %lu",
+						GetLastError())));
+		return -1;
+	}
+	param = MapViewOfFile(paramHandle, FILE_MAP_WRITE, 0, 0, paramsz);
+	if (!param)
+	{
+		ereport(LOG,
+				(errmsg("could not map backend parameter memory: error code %lu",
+						GetLastError())));
+		CloseHandle(paramHandle);
+		return -1;
+	}
+
+	/* Format the cmd line */
+#ifdef _WIN64
+	sprintf(paramHandleStr, "%llu", (LONG_PTR) paramHandle);
+#else
+	sprintf(paramHandleStr, "%lu", (DWORD) paramHandle);
+#endif
+	l = snprintf(cmdLine, sizeof(cmdLine) - 1, "\"%s\" --forkchild=\"%s\" %s",
+				 postgres_exec_path, entry_name, paramHandleStr);
+	if (l >= sizeof(cmdLine))
+	{
+		ereport(LOG,
+				(errmsg("subprocess command line too long")));
+		UnmapViewOfFile(param);
+		CloseHandle(paramHandle);
+		return -1;
+	}
+
+	memset(&pi, 0, sizeof(pi));
+	memset(&si, 0, sizeof(si));
+	si.cb = sizeof(si);
+
+	/*
+	 * Create the subprocess in a suspended state. This will be resumed later,
+	 * once we have written out the parameter file.
+	 */
+	if (!CreateProcess(NULL, cmdLine, NULL, NULL, TRUE, CREATE_SUSPENDED,
+					   NULL, NULL, &si, &pi))
+	{
+		ereport(LOG,
+				(errmsg("CreateProcess() call failed: %m (error code %lu)",
+						GetLastError())));
+		UnmapViewOfFile(param);
+		CloseHandle(paramHandle);
+		return -1;
+	}
+
+	if (!save_backend_variables(param, client_sock, pi.hProcess, pi.dwProcessId, startup_data, startup_data_len))
+	{
+		/*
+		 * log made by save_backend_variables, but we have to clean up the
+		 * mess with the half-started process
+		 */
+		if (!TerminateProcess(pi.hProcess, 255))
+			ereport(LOG,
+					(errmsg_internal("could not terminate unstarted process: error code %lu",
+									 GetLastError())));
+		CloseHandle(pi.hProcess);
+		CloseHandle(pi.hThread);
+		UnmapViewOfFile(param);
+		CloseHandle(paramHandle);
+		return -1;				/* log made by save_backend_variables */
+	}
+
+	/* Drop the parameter shared memory that is now inherited to the backend */
+	if (!UnmapViewOfFile(param))
+		ereport(LOG,
+				(errmsg("could not unmap view of backend parameter file: error code %lu",
+						GetLastError())));
+	if (!CloseHandle(paramHandle))
+		ereport(LOG,
+				(errmsg("could not close handle to backend parameter file: error code %lu",
+						GetLastError())));
+
+	/*
+	 * Reserve the memory region used by our main shared memory segment before
+	 * we resume the child process.  Normally this should succeed, but if ASLR
+	 * is active then it might sometimes fail due to the stack or heap having
+	 * gotten mapped into that range.  In that case, just terminate the
+	 * process and retry.
+	 */
+	if (!pgwin32_ReserveSharedMemoryRegion(pi.hProcess))
+	{
+		/* pgwin32_ReserveSharedMemoryRegion already made a log entry */
+		if (!TerminateProcess(pi.hProcess, 255))
+			ereport(LOG,
+					(errmsg_internal("could not terminate process that failed to reserve memory: error code %lu",
+									 GetLastError())));
+		CloseHandle(pi.hProcess);
+		CloseHandle(pi.hThread);
+		if (++retry_count < 100)
+			goto retry;
+		ereport(LOG,
+				(errmsg("giving up after too many tries to reserve shared memory"),
+				 errhint("This might be caused by ASLR or antivirus software.")));
+		return -1;
+	}
+
+	/*
+	 * Now that the backend variables are written out, we start the child
+	 * thread so it can start initializing while we set up the rest of the
+	 * parent state.
+	 */
+	if (ResumeThread(pi.hThread) == -1)
+	{
+		if (!TerminateProcess(pi.hProcess, 255))
+		{
+			ereport(LOG,
+					(errmsg_internal("could not terminate unstartable process: error code %lu",
+									 GetLastError())));
+			CloseHandle(pi.hProcess);
+			CloseHandle(pi.hThread);
+			return -1;
+		}
+		CloseHandle(pi.hProcess);
+		CloseHandle(pi.hThread);
+		ereport(LOG,
+				(errmsg_internal("could not resume thread of unstarted process: error code %lu",
+								 GetLastError())));
+		return -1;
+	}
+
+	pgwin32_register_deadchild_callback(pi.hProcess, pi.dwProcessId);
+
+	/* Don't close pi.hProcess here - waitpid() needs access to it */
+	CloseHandle(pi.hThread);
+
+	return pi.dwProcessId;
+}
+#endif							/* WIN32 */
+
+/*
+ * SubPostmasterMain -- Get the fork/exec'd process into a state equivalent
+ *			to what it would be if we'd simply forked on Unix, and then
+ *			dispatch to the appropriate place.
+ *
+ * The first two command line arguments are expected to be "--forkchild=<name>",
+ * where <name> indicates which postmaster child we are to become, and
+ * the name of a variables file that we can read to load data that would
+ * have been inherited by fork() on Unix.
+ */
+void
+SubPostmasterMain(int argc, char *argv[])
+{
+	PostmasterChildType child_type;
+	char	   *startup_data;
+	size_t		startup_data_len;
+	char	   *entry_name;
+	bool		found = false;
+
+	/* In EXEC_BACKEND case we will not have inherited these settings */
+	IsPostmasterEnvironment = true;
+	whereToSendOutput = DestNone;
+
+	/* Setup essential subsystems (to ensure elog() behaves sanely) */
+	InitializeGUCOptions();
+
+	/* Check we got appropriate args */
+	if (argc != 3)
+		elog(FATAL, "invalid subpostmaster invocation");
+
+	if (strncmp(argv[1], "--forkchild=", 12) != 0)
+		elog(FATAL, "invalid subpostmaster invocation (--forkchild argument missing)");
+	entry_name = argv[1] + 12;
+	found = false;
+	for (int idx = 0; idx < lengthof(entry_kinds); idx++)
+	{
+		if (strcmp(entry_kinds[idx].name, entry_name) == 0)
+		{
+			child_type = idx;
+			found = true;
+			break;
+		}
+	}
+	if (!found)
+		elog(ERROR, "unknown child kind %s", entry_name);
+
+	/* Read in the variables file */
+	read_backend_variables(argv[2], &startup_data, &startup_data_len);
+
+	/* Setup as postmaster child */
+	InitPostmasterChild(child_type == PMC_SYSLOGGER);
+
+	/*
+	 * If appropriate, physically re-attach to shared memory segment. We want
+	 * to do this before going any further to ensure that we can attach at the
+	 * same address the postmaster used.  On the other hand, if we choose not
+	 * to re-attach, we may have other cleanup to do.
+	 *
+	 * If testing EXEC_BACKEND on Linux, you should run this as root before
+	 * starting the postmaster:
+	 *
+	 * sysctl -w kernel.randomize_va_space=0
+	 *
+	 * This prevents using randomized stack and code addresses that cause the
+	 * child process's memory map to be different from the parent's, making it
+	 * sometimes impossible to attach to shared memory at the desired address.
+	 * Return the setting to its old value (usually '1' or '2') when finished.
+	 */
+	if (entry_kinds[child_type].shmem_attach)
+		PGSharedMemoryReAttach();
+	else
+		PGSharedMemoryNoReAttach();
+
+	/* Read in remaining GUC variables */
+	read_nondefault_variables();
+
+	/*
+	 * Check that the data directory looks valid, which will also check the
+	 * privileges on the data directory and update our umask and file/group
+	 * variables for creating files later.  Note: this should really be done
+	 * before we create any files or directories.
+	 */
+	checkDataDir();
+
+	/*
+	 * (re-)read control file, as it contains config. The postmaster will
+	 * already have read this, but this process doesn't know about that.
+	 */
+	LocalProcessControlFile(false);
+
+	/*
+	 * Reload any libraries that were preloaded by the postmaster.  Since we
+	 * exec'd this process, those libraries didn't come along with us; but we
+	 * should load them into all child processes to be consistent with the
+	 * non-EXEC_BACKEND behavior.
+	 */
+	process_shared_preload_libraries();
+
+	/* Restore basic shared memory pointers */
+	if (UsedShmemSegAddr != NULL)
+		InitShmemAccess(UsedShmemSegAddr);
+
+	/* Run backend or appropriate child */
+	entry_kinds[child_type].main_fn(startup_data, startup_data_len);
+
+	abort();					/* shouldn't get here */
+}
+#endif							/* EXEC_BACKEND */
+
+/* ----------------------------------------------------------------
+ *	common process startup code
+ * ----------------------------------------------------------------
+ */
+
+/*
+ * Initialize the basic environment for a postmaster child
+ *
+ * Should be called as early as possible after the child's startup. However,
+ * on EXEC_BACKEND builds it does need to be after read_backend_variables().
+ */
+static void
+InitPostmasterChild(bool am_syslogger)
+{
+	/* Close the postmaster's sockets (as soon as we know them) */
+	ClosePostmasterPorts(am_syslogger);
+
+	IsUnderPostmaster = true;	/* we are a postmaster subprocess now */
+
+	/*
+	 * Start our win32 signal implementation. This has to be done after we
+	 * read the backend variables, because we need to pick up the signal pipe
+	 * from the parent process.
+	 */
+#ifdef WIN32
+	pgwin32_signal_initialize();
+#endif
+
+	/*
+	 * Set reference point for stack-depth checking.  This might seem
+	 * redundant in !EXEC_BACKEND builds; but it's not because the postmaster
+	 * launches its children from signal handlers, so we might be running on
+	 * an alternative stack. XXX still true?
+	 */
+	(void) set_stack_base();
+
+	InitProcessGlobals();
+
+	/*
+	 * make sure stderr is in binary mode before anything can possibly be
+	 * written to it, in case it's actually the syslogger pipe, so the pipe
+	 * chunking protocol isn't disturbed. Non-logpipe data gets translated on
+	 * redirection (e.g. via pg_ctl -l) anyway.
+	 */
+#ifdef WIN32
+	_setmode(fileno(stderr), _O_BINARY);
+#endif
+
+	/* We don't want the postmaster's proc_exit() handlers */
+	on_exit_reset();
+
+	/* In EXEC_BACKEND case we will not have inherited BlockSig etc values */
+#ifdef EXEC_BACKEND
+	pqinitmask();
+#endif
+
+	/* Initialize process-local latch support */
+	InitializeLatchSupport();
+	InitProcessLocalLatch();
+	InitializeLatchWaitSet();
+
+	/*
+	 * If possible, make this process a group leader, so that the postmaster
+	 * can signal any child processes too. Not all processes will have
+	 * children, but for consistency we make all postmaster child processes do
+	 * this.
+	 */
+#ifdef HAVE_SETSID
+	if (setsid() < 0)
+		elog(FATAL, "setsid() failed: %m");
+#endif
+
+	/*
+	 * Every postmaster child process is expected to respond promptly to
+	 * SIGQUIT at all times.  Therefore we centrally remove SIGQUIT from
+	 * BlockSig and install a suitable signal handler.  (Client-facing
+	 * processes may choose to replace this default choice of handler with
+	 * quickdie().)  All other blockable signals remain blocked for now.
+	 */
+	pqsignal(SIGQUIT, SignalHandlerForCrashExit);
+
+	sigdelset(&BlockSig, SIGQUIT);
+	sigprocmask(SIG_SETMASK, &BlockSig, NULL);
+
+	/* Request a signal if the postmaster dies, if possible. */
+	PostmasterDeathSignalInit();
+
+	/* Don't give the pipe to subprograms that we execute. */
+#ifndef WIN32
+	if (fcntl(postmaster_alive_fds[POSTMASTER_FD_WATCH], F_SETFD, FD_CLOEXEC) < 0)
+		ereport(FATAL,
+				(errcode_for_socket_access(),
+				 errmsg_internal("could not set postmaster death monitoring pipe to FD_CLOEXEC mode: %m")));
+#endif
+}
+
+/*
+ * Initialize the basic environment for a standalone process.
+ *
+ * argv0 has to be suitable to find the program's executable.
+ */
+void
+InitStandaloneProcess(const char *argv0)
+{
+	Assert(!IsPostmasterEnvironment);
+
+	MyBackendType = B_STANDALONE_BACKEND;
+
+	/*
+	 * Start our win32 signal implementation
+	 */
+#ifdef WIN32
+	pgwin32_signal_initialize();
+#endif
+
+	InitProcessGlobals();
+
+	/* Initialize process-local latch support */
+	InitializeLatchSupport();
+	InitProcessLocalLatch();
+	InitializeLatchWaitSet();
+
+	/*
+	 * For consistency with InitPostmasterChild, initialize signal mask here.
+	 * But we don't unblock SIGQUIT or provide a default handler for it.
+	 */
+	pqinitmask();
+	sigprocmask(SIG_SETMASK, &BlockSig, NULL);
+
+	/* Compute paths, no postmaster to inherit from */
+	if (my_exec_path[0] == '\0')
+	{
+		if (find_my_exec(argv0, my_exec_path) < 0)
+			elog(FATAL, "%s: could not locate my own executable path",
+				 argv0);
+	}
+
+	if (pkglib_path[0] == '\0')
+		get_pkglib_path(my_exec_path, pkglib_path);
+}
+
+
+#ifdef EXEC_BACKEND
+
+/*
+ * The following need to be available to the save/restore_backend_variables
+ * functions.  They are marked NON_EXEC_STATIC in their home modules.
+ */
+extern slock_t *ShmemLock;
+extern slock_t *ProcStructLock;
+extern PGPROC *AuxiliaryProcs;
+extern PMSignalData *PMSignalState;
+extern pg_time_t first_syslogger_file_time;
+extern struct bkend *ShmemBackendArray;
+extern bool redirection_done;
+
+#ifndef WIN32
+#define write_inheritable_socket(dest, src, childpid) ((*(dest) = (src)), true)
+#define read_inheritable_socket(dest, src) (*(dest) = *(src))
+#else
+static bool write_duplicated_handle(HANDLE *dest, HANDLE src, HANDLE child);
+static bool write_inheritable_socket(InheritableSocket *dest, SOCKET src,
+									 pid_t childPid);
+static void read_inheritable_socket(SOCKET *dest, InheritableSocket *src);
+#endif
+
+
+/* Save critical backend variables into the BackendParameters struct */
+static bool
+save_backend_variables(BackendParameters *param, ClientSocket *client_sock,
+#ifdef WIN32
+					   HANDLE childProcess, pid_t childPid,
+#endif
+					   char *startup_data, size_t startup_data_len)
+{
+	if (client_sock)
+		memcpy(&param->client_sock, client_sock, sizeof(ClientSocket));
+	else
+		memset(&param->client_sock, 0, sizeof(ClientSocket));
+	if (!write_inheritable_socket(&param->inh_sock,
+								  client_sock ? client_sock->sock : PGINVALID_SOCKET,
+								  childPid))
+		return false;
+
+	strlcpy(param->DataDir, DataDir, MAXPGPATH);
+
+	param->MyCancelKey = MyCancelKey;
+	param->MyPMChildSlot = MyPMChildSlot;
+
+#ifdef WIN32
+	param->ShmemProtectiveRegion = ShmemProtectiveRegion;
+#endif
+	param->UsedShmemSegID = UsedShmemSegID;
+	param->UsedShmemSegAddr = UsedShmemSegAddr;
+
+	param->ShmemLock = ShmemLock;
+	param->ShmemVariableCache = ShmemVariableCache;
+	param->ShmemBackendArray = ShmemBackendArray;
+
+#ifndef HAVE_SPINLOCKS
+	param->SpinlockSemaArray = SpinlockSemaArray;
+#endif
+	param->NamedLWLockTrancheRequests = NamedLWLockTrancheRequests;
+	param->NamedLWLockTrancheArray = NamedLWLockTrancheArray;
+	param->MainLWLockArray = MainLWLockArray;
+	param->ProcStructLock = ProcStructLock;
+	param->ProcGlobal = ProcGlobal;
+	param->AuxiliaryProcs = AuxiliaryProcs;
+	param->PreparedXactProcs = PreparedXactProcs;
+	param->PMSignalState = PMSignalState;
+
+	param->PostmasterPid = PostmasterPid;
+	param->PgStartTime = PgStartTime;
+	param->PgReloadTime = PgReloadTime;
+	param->first_syslogger_file_time = first_syslogger_file_time;
+
+	param->redirection_done = redirection_done;
+	param->IsBinaryUpgrade = IsBinaryUpgrade;
+	param->query_id_enabled = query_id_enabled;
+	param->max_safe_fds = max_safe_fds;
+
+	param->MaxBackends = MaxBackends;
+
+#ifdef WIN32
+	param->PostmasterHandle = PostmasterHandle;
+	if (!write_duplicated_handle(&param->initial_signal_pipe,
+								 pgwin32_create_signal_listener(childPid),
+								 childProcess))
+		return false;
+#else
+	memcpy(&param->postmaster_alive_fds, &postmaster_alive_fds,
+		   sizeof(postmaster_alive_fds));
+#endif
+
+	memcpy(&param->syslogPipe, &syslogPipe, sizeof(syslogPipe));
+
+	strlcpy(param->my_exec_path, my_exec_path, MAXPGPATH);
+
+	strlcpy(param->pkglib_path, pkglib_path, MAXPGPATH);
+
+	param->startup_data_len = startup_data_len;
+	memcpy(param->startup_data, startup_data, startup_data_len);
+
+	return true;
+}
+
+
+#ifdef WIN32
+/*
+ * Duplicate a handle for usage in a child process, and write the child
+ * process instance of the handle to the parameter file.
+ */
+static bool
+write_duplicated_handle(HANDLE *dest, HANDLE src, HANDLE childProcess)
+{
+	HANDLE		hChild = INVALID_HANDLE_VALUE;
+
+	if (!DuplicateHandle(GetCurrentProcess(),
+						 src,
+						 childProcess,
+						 &hChild,
+						 0,
+						 TRUE,
+						 DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS))
+	{
+		ereport(LOG,
+				(errmsg_internal("could not duplicate handle to be written to backend parameter file: error code %lu",
+								 GetLastError())));
+		return false;
+	}
+
+	*dest = hChild;
+	return true;
+}
+
+/*
+ * Duplicate a socket for usage in a child process, and write the resulting
+ * structure to the parameter file.
+ * This is required because a number of LSPs (Layered Service Providers) very
+ * common on Windows (antivirus, firewalls, download managers etc) break
+ * straight socket inheritance.
+ */
+static bool
+write_inheritable_socket(InheritableSocket *dest, SOCKET src, pid_t childpid)
+{
+	dest->origsocket = src;
+	if (src != 0 && src != PGINVALID_SOCKET)
+	{
+		/* Actual socket */
+		if (WSADuplicateSocket(src, childpid, &dest->wsainfo) != 0)
+		{
+			ereport(LOG,
+					(errmsg("could not duplicate socket %d for use in backend: error code %d",
+							(int) src, WSAGetLastError())));
+			return false;
+		}
+	}
+	return true;
+}
+
+/*
+ * Read a duplicate socket structure back, and get the socket descriptor.
+ */
+static void
+read_inheritable_socket(SOCKET *dest, InheritableSocket *src)
+{
+	SOCKET		s;
+
+	if (src->origsocket == PGINVALID_SOCKET || src->origsocket == 0)
+	{
+		/* Not a real socket! */
+		*dest = src->origsocket;
+	}
+	else
+	{
+		/* Actual socket, so create from structure */
+		s = WSASocket(FROM_PROTOCOL_INFO,
+					  FROM_PROTOCOL_INFO,
+					  FROM_PROTOCOL_INFO,
+					  &src->wsainfo,
+					  0,
+					  0);
+		if (s == INVALID_SOCKET)
+		{
+			write_stderr("could not create inherited socket: error code %d\n",
+						 WSAGetLastError());
+			exit(1);
+		}
+		*dest = s;
+
+		/*
+		 * To make sure we don't get two references to the same socket, close
+		 * the original one. (This would happen when inheritance actually
+		 * works..
+		 */
+		closesocket(src->origsocket);
+	}
+}
+#endif
+
+static void
+read_backend_variables(char *id, char **startup_data, size_t *startup_data_len)
+{
+	BackendParameters param;
+
+#ifndef WIN32
+	/* Non-win32 implementation reads from file */
+	FILE	   *fp;
+
+	/* Open file */
+	fp = AllocateFile(id, PG_BINARY_R);
+	if (!fp)
+	{
+		write_stderr("could not open backend variables file \"%s\": %s\n",
+					 id, strerror(errno));
+		exit(1);
+	}
+
+	if (fread(&param, sizeof(param), 1, fp) != 1)
+	{
+		write_stderr("could not read from backend variables file \"%s\": %s\n",
+					 id, strerror(errno));
+		exit(1);
+	}
+
+	/* read startup data */
+	*startup_data_len = param.startup_data_len;
+	if (param.startup_data_len > 0)
+	{
+		*startup_data = palloc(*startup_data_len);
+		if (fread(*startup_data, *startup_data_len, 1, fp) != 1)
+		{
+			write_stderr("could not read startup data from backend variables file \"%s\": %s\n",
+						 id, strerror(errno));
+			exit(1);
+		}
+	}
+	else
+		*startup_data = NULL;
+
+	/* Release file */
+	FreeFile(fp);
+	if (unlink(id) != 0)
+	{
+		write_stderr("could not remove file \"%s\": %s\n",
+					 id, strerror(errno));
+		exit(1);
+	}
+#else
+	/* Win32 version uses mapped file */
+	HANDLE		paramHandle;
+	BackendParameters *filep;
+
+#ifdef _WIN64
+	paramHandle = (HANDLE) _atoi64(id);
+#else
+	paramHandle = (HANDLE) atol(id);
+#endif
+	filep = MapViewOfFile(paramHandle, FILE_MAP_READ, 0, 0, 0);
+	if (!filep)
+	{
+		write_stderr("could not map view of backend variables: error code %lu\n",
+					 GetLastError());
+		exit(1);
+	}
+
+	memcpy(&param, filep, sizeof(BackendParameters));
+
+	/* read startup data */
+	*startup_data_len = param.startup_data_len;
+	if (param.startup_data_len > 0)
+	{
+		*startup_data = palloc(param.startup_data_len);
+		memcpy(*startup_data, filep->startup_data, param.startup_data_len);
+	}
+	else
+		*startup_data = NULL;
+
+	if (!UnmapViewOfFile(filep))
+	{
+		write_stderr("could not unmap view of backend variables: error code %lu\n",
+					 GetLastError());
+		exit(1);
+	}
+
+	if (!CloseHandle(paramHandle))
+	{
+		write_stderr("could not close handle to backend parameter variables: error code %lu\n",
+					 GetLastError());
+		exit(1);
+	}
+#endif
+
+	restore_backend_variables(&param);
+}
+
+/* Restore critical backend variables from the BackendParameters struct */
+static void
+restore_backend_variables(BackendParameters *param)
+{
+	if (param->client_sock.sock != PGINVALID_SOCKET)
+	{
+		MyClientSocket = MemoryContextAlloc(TopMemoryContext, sizeof(ClientSocket));
+		memcpy(MyClientSocket, &param->client_sock, sizeof(ClientSocket));
+		read_inheritable_socket(&MyClientSocket->sock, &param->inh_sock);
+	}
+
+	SetDataDir(param->DataDir);
+
+	MyCancelKey = param->MyCancelKey;
+	MyPMChildSlot = param->MyPMChildSlot;
+
+#ifdef WIN32
+	ShmemProtectiveRegion = param->ShmemProtectiveRegion;
+#endif
+	UsedShmemSegID = param->UsedShmemSegID;
+	UsedShmemSegAddr = param->UsedShmemSegAddr;
+
+	ShmemLock = param->ShmemLock;
+	ShmemVariableCache = param->ShmemVariableCache;
+	ShmemBackendArray = param->ShmemBackendArray;
+
+#ifndef HAVE_SPINLOCKS
+	SpinlockSemaArray = param->SpinlockSemaArray;
+#endif
+	NamedLWLockTrancheRequests = param->NamedLWLockTrancheRequests;
+	NamedLWLockTrancheArray = param->NamedLWLockTrancheArray;
+	MainLWLockArray = param->MainLWLockArray;
+	ProcStructLock = param->ProcStructLock;
+	ProcGlobal = param->ProcGlobal;
+	AuxiliaryProcs = param->AuxiliaryProcs;
+	PreparedXactProcs = param->PreparedXactProcs;
+	PMSignalState = param->PMSignalState;
+
+	PostmasterPid = param->PostmasterPid;
+	PgStartTime = param->PgStartTime;
+	PgReloadTime = param->PgReloadTime;
+	first_syslogger_file_time = param->first_syslogger_file_time;
+
+	redirection_done = param->redirection_done;
+	IsBinaryUpgrade = param->IsBinaryUpgrade;
+	query_id_enabled = param->query_id_enabled;
+	max_safe_fds = param->max_safe_fds;
+
+	MaxBackends = param->MaxBackends;
+
+#ifdef WIN32
+	PostmasterHandle = param->PostmasterHandle;
+	pgwin32_initial_signal_pipe = param->initial_signal_pipe;
+#else
+	memcpy(&postmaster_alive_fds, &param->postmaster_alive_fds,
+		   sizeof(postmaster_alive_fds));
+#endif
+
+	memcpy(&syslogPipe, &param->syslogPipe, sizeof(syslogPipe));
+
+	strlcpy(my_exec_path, param->my_exec_path, MAXPGPATH);
+
+	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
+
+	/*
+	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
+	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
+	 * BackendInitialize will handle this for client_sock->sock.)
+	 */
+#ifndef WIN32
+	if (postmaster_alive_fds[0] >= 0)
+		ReserveExternalFD();
+	if (postmaster_alive_fds[1] >= 0)
+		ReserveExternalFD();
+#endif
+}
+
+#endif							/* EXEC_BACKEND */
diff --git a/src/backend/postmaster/meson.build b/src/backend/postmaster/meson.build
index cda921fd10b..89ff11beb0a 100644
--- a/src/backend/postmaster/meson.build
+++ b/src/backend/postmaster/meson.build
@@ -6,8 +6,8 @@ backend_sources += files(
   'bgworker.c',
   'bgwriter.c',
   'checkpointer.c',
-  'fork_process.c',
   'interrupt.c',
+  'launch_backend.c',
   'pgarch.c',
   'postmaster.c',
   'startup.c',
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 46af3495644..b82d66fab52 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -36,6 +36,7 @@
 #include "lib/binaryheap.h"
 #include "libpq/pqsignal.h"
 #include "pgstat.h"
+#include "postmaster/auxprocess.h"
 #include "postmaster/interrupt.h"
 #include "postmaster/pgarch.h"
 #include "storage/fd.h"
@@ -211,8 +212,14 @@ PgArchCanRestart(void)
 
 /* Main entry point for archiver process */
 void
-PgArchiverMain(void)
+PgArchiverMain(char *startup_data, size_t startup_data_len)
 {
+	Assert(startup_data_len == 0);
+
+	MyAuxProcType = ArchiverProcess;
+	MyBackendType = B_ARCHIVER;
+	AuxiliaryProcessInit();
+
 	/*
 	 * Ignore all signals usually bound to some action in the postmaster,
 	 * except for SIGHUP, SIGTERM, SIGUSR1, SIGUSR2, and SIGQUIT.
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 550136009a8..60ea739f7aa 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -2,9 +2,9 @@
  *
  * postmaster.c
  *	  This program acts as a clearing house for requests to the
- *	  POSTGRES system.  Frontend programs send a startup message
- *	  to the Postmaster and the postmaster uses the info in the
- *	  message to setup a backend process.
+ *	  POSTGRES system.  Frontend programs connect to the Postmaster,
+ *	  and postmaster forks a new backend process to handle the
+ *	  connection.
  *
  *	  The postmaster also manages system-wide operations such as
  *	  startup and shutdown. The postmaster itself doesn't do those
@@ -110,7 +110,6 @@
 #include "postmaster/autovacuum.h"
 #include "postmaster/auxprocess.h"
 #include "postmaster/bgworker_internals.h"
-#include "postmaster/fork_process.h"
 #include "postmaster/interrupt.h"
 #include "postmaster/pgarch.h"
 #include "postmaster/postmaster.h"
@@ -132,10 +131,6 @@
 #include "utils/timestamp.h"
 #include "utils/varlena.h"
 
-#ifdef EXEC_BACKEND
-#include "storage/spin.h"
-#endif
-
 
 /*
  * Possible types of a backend. Beyond being the possible bkend_type values in
@@ -188,7 +183,7 @@ typedef struct bkend
 static dlist_head BackendList = DLIST_STATIC_INIT(BackendList);
 
 #ifdef EXEC_BACKEND
-static Backend *ShmemBackendArray;
+Backend    *ShmemBackendArray;
 #endif
 
 BackgroundWorker *MyBgworkerEntry = NULL;
@@ -413,7 +408,7 @@ static void StartupPacketTimeoutHandler(void);
 static void CleanupBackend(int pid, int exitstatus);
 static bool CleanupBackgroundWorker(int pid, int exitstatus);
 static void HandleChildCrash(int pid, int exitstatus, const char *procname);
-static void LogChildExit(int lev, const char *procname,
+static void LogChildExit(int lev, const char *procnamBackendInitializee,
 						 int pid, int exitstatus);
 static void PostmasterStateMachine(void);
 
@@ -428,7 +423,6 @@ typedef enum CAC_state
 } CAC_state;
 
 static void BackendInitialize(ClientSocket *client_sock, CAC_state cac);
-static void BackendRun(void) pg_attribute_noreturn();
 static void ExitPostmaster(int status) pg_attribute_noreturn();
 static int	ServerLoop(void);
 static int	BackendStartup(ClientSocket *port);
@@ -449,7 +443,7 @@ static int	CountChildren(int target);
 static bool assign_backendlist_entry(RegisteredBgWorker *rw);
 static void maybe_start_bgworkers(void);
 static bool CreateOptsFile(int argc, char *argv[], char *fullprogname);
-static pid_t StartChildProcess(AuxProcType type);
+static pid_t StartChildProcess(PostmasterChildType type);
 static void StartAutovacuumWorker(void);
 static void MaybeStartWalReceiver(void);
 static void InitPostmasterDeathWatchHandle(void);
@@ -484,95 +478,19 @@ typedef struct
 } win32_deadchild_waitinfo;
 #endif							/* WIN32 */
 
-static pid_t backend_forkexec(ClientSocket *client_sock, CAC_state cac);
-static pid_t internal_forkexec(int argc, char *argv[], ClientSocket *client_sock);
-
-/* Type for a socket that can be inherited to a client process */
-#ifdef WIN32
-typedef struct
-{
-	SOCKET		origsocket;		/* Original socket value, or PGINVALID_SOCKET
-								 * if not a socket */
-	WSAPROTOCOL_INFO wsainfo;
-} InheritableSocket;
-#else
-typedef int InheritableSocket;
-#endif
-
-/*
- * Structure contains all variables passed to exec:ed backends
- */
-typedef struct
-{
-	ClientSocket client_sock;
-	InheritableSocket serialized_sock;
-	char		DataDir[MAXPGPATH];
-	int32		MyCancelKey;
-	int			MyPMChildSlot;
-#ifndef WIN32
-	unsigned long UsedShmemSegID;
-#else
-	void	   *ShmemProtectiveRegion;
-	HANDLE		UsedShmemSegID;
-#endif
-	void	   *UsedShmemSegAddr;
-	slock_t    *ShmemLock;
-	VariableCache ShmemVariableCache;
-	Backend    *ShmemBackendArray;
-#ifndef HAVE_SPINLOCKS
-	PGSemaphore *SpinlockSemaArray;
-#endif
-	int			NamedLWLockTrancheRequests;
-	NamedLWLockTranche *NamedLWLockTrancheArray;
-	LWLockPadded *MainLWLockArray;
-	slock_t    *ProcStructLock;
-	PROC_HDR   *ProcGlobal;
-	PGPROC	   *AuxiliaryProcs;
-	PGPROC	   *PreparedXactProcs;
-	PMSignalData *PMSignalState;
-	pid_t		PostmasterPid;
-	TimestampTz PgStartTime;
-	TimestampTz PgReloadTime;
-	pg_time_t	first_syslogger_file_time;
-	bool		redirection_done;
-	bool		IsBinaryUpgrade;
-	bool		query_id_enabled;
-	int			max_safe_fds;
-	int			MaxBackends;
-#ifdef WIN32
-	HANDLE		PostmasterHandle;
-	HANDLE		initial_signal_pipe;
-	HANDLE		syslogPipe[2];
-#else
-	int			postmaster_alive_fds[2];
-	int			syslogPipe[2];
-#endif
-	char		my_exec_path[MAXPGPATH];
-	char		pkglib_path[MAXPGPATH];
-
-	BackgroundWorker MyBgworkerEntry;
-} BackendParameters;
-
-static void read_backend_variables(char *id, ClientSocket *client_sock);
-static void restore_backend_variables(BackendParameters *param, ClientSocket *client_sock);
-
-#ifndef WIN32
-static bool save_backend_variables(BackendParameters *param, ClientSocket *client_sock);
-#else
-static bool save_backend_variables(BackendParameters *param, ClientSocket *client_sock,
-								   HANDLE childProcess, pid_t childPid);
-#endif
-
 static void ShmemBackendArrayAdd(Backend *bn);
 static void ShmemBackendArrayRemove(Backend *bn);
 #endif							/* EXEC_BACKEND */
 
-#define StartupDataBase()		StartChildProcess(StartupProcess)
-#define StartArchiver()			StartChildProcess(ArchiverProcess)
-#define StartBackgroundWriter() StartChildProcess(BgWriterProcess)
-#define StartCheckpointer()		StartChildProcess(CheckpointerProcess)
-#define StartWalWriter()		StartChildProcess(WalWriterProcess)
-#define StartWalReceiver()		StartChildProcess(WalReceiverProcess)
+#define StartupDataBase()		StartChildProcess(PMC_STARTUP)
+#define StartArchiver()			StartChildProcess(PMC_ARCHIVER)
+#define StartBackgroundWriter() StartChildProcess(PMC_BGWRITER)
+#define StartCheckpointer()		StartChildProcess(PMC_CHECKPOINTER)
+#define StartWalWriter()		StartChildProcess(PMC_WAL_WRITER)
+#define StartWalReceiver()		StartChildProcess(PMC_WAL_RECEIVER)
+
+#define StartAutoVacLauncher()	StartChildProcess(PMC_AV_LAUNCHER);
+#define StartAutoVacWorker()	StartChildProcess(PMC_AV_WORKER);
 
 /* Macros to check exit status of a child process */
 #define EXIT_STATUS_0(st)  ((st) == 0)
@@ -1115,11 +1033,11 @@ PostmasterMain(int argc, char *argv[])
 
 	/*
 	 * Clean out the temp directory used to transmit parameters to child
-	 * processes (see internal_forkexec, below).  We must do this before
-	 * launching any child processes, else we have a race condition: we could
-	 * remove a parameter file before the child can read it.  It should be
-	 * safe to do so now, because we verified earlier that there are no
-	 * conflicting Postgres processes in this data directory.
+	 * processes (see internal_forkexec).  We must do this before launching
+	 * any child processes, else we have a race condition: we could remove a
+	 * parameter file before the child can read it.  It should be safe to do
+	 * so now, because we verified earlier that there are no conflicting
+	 * Postgres processes in this data directory.
 	 */
 	RemovePgTempFilesInDir(PG_TEMP_FILES_DIR, true, false);
 #endif
@@ -3989,6 +3907,11 @@ TerminateChildren(int signal)
 		signal_child(PgArchPID, signal);
 }
 
+typedef struct BackendStartupInfo
+{
+	CAC_state	canAcceptConnections;
+}			BackendStartupInfo;
+
 /*
  * BackendStartup -- start backend process
  *
@@ -4001,7 +3924,7 @@ BackendStartup(ClientSocket *client_sock)
 {
 	Backend    *bn;				/* for backend cleanup */
 	pid_t		pid;
-	CAC_state	cac;
+	BackendStartupInfo info;
 
 	/*
 	 * Create backend data structure.  Better before the fork() so we can
@@ -4030,11 +3953,10 @@ BackendStartup(ClientSocket *client_sock)
 		return STATUS_ERROR;
 	}
 
-	bn->cancel_key = MyCancelKey;
-
 	/* Pass down canAcceptConnections state */
-	cac = canAcceptConnections(BACKEND_TYPE_NORMAL);
-	bn->dead_end = (cac != CAC_OK);
+	info.canAcceptConnections = canAcceptConnections(BACKEND_TYPE_NORMAL);
+	bn->dead_end = (info.canAcceptConnections != CAC_OK);
+	bn->cancel_key = MyCancelKey;
 
 	/*
 	 * Unless it's a dead_end child, assign it a child slot number
@@ -4047,26 +3969,7 @@ BackendStartup(ClientSocket *client_sock)
 	/* Hasn't asked to be notified about any bgworkers yet */
 	bn->bgworker_notify = false;
 
-#ifdef EXEC_BACKEND
-	pid = backend_forkexec(client_sock, cac);
-#else							/* !EXEC_BACKEND */
-	pid = fork_process();
-	if (pid == 0)				/* child */
-	{
-		/* Detangle from postmaster */
-		InitPostmasterChild();
-
-		/* Close the postmaster's sockets */
-		ClosePostmasterPorts(false);
-
-		/* Perform additional initialization and collect startup packet */
-		BackendInitialize(client_sock, cac);
-
-		/* And run the backend */
-		BackendRun();
-	}
-#endif							/* EXEC_BACKEND */
-
+	pid = postmaster_child_launch(PMC_BACKEND, (char *) &info, sizeof(info), client_sock);
 	if (pid < 0)
 	{
 		/* in parent, fork failed */
@@ -4103,6 +4006,58 @@ BackendStartup(ClientSocket *client_sock)
 	return STATUS_OK;
 }
 
+void
+BackendMain(char *startup_data, size_t startup_data_len)
+{
+	BackendStartupInfo *info = (BackendStartupInfo *) startup_data;
+
+	Assert(startup_data_len == sizeof(BackendStartupInfo));
+	Assert(MyClientSocket != NULL);
+
+#ifdef EXEC_BACKEND
+
+	/*
+	 * Need to reinitialize the SSL library in the backend, since the context
+	 * structures contain function pointers and cannot be passed through the
+	 * parameter file.
+	 *
+	 * If for some reason reload fails (maybe the user installed broken key
+	 * files), soldier on without SSL; that's better than all connections
+	 * becoming impossible.
+	 *
+	 * XXX should we do this in all child processes?  For the moment it's
+	 * enough to do it in backend children.
+	 */
+#ifdef USE_SSL
+	if (EnableSSL)
+	{
+		if (secure_initialize(false) == 0)
+			LoadedSSL = true;
+		else
+			ereport(LOG,
+					(errmsg("SSL configuration could not be loaded in child process")));
+	}
+#endif
+#endif
+
+	/* Perform additional initialization and collect startup packet */
+	BackendInitialize(MyClientSocket, info->canAcceptConnections);
+
+	/*
+	 * Create a per-backend PGPROC struct in shared memory.  We must do this
+	 * before we can use LWLocks or access any shared memory.
+	 */
+	InitProcess();
+
+	/*
+	 * Make sure we aren't in PostmasterContext anymore.  (We can't delete it
+	 * just yet, though, because InitPostgres will need the HBA data.)
+	 */
+	MemoryContextSwitchTo(TopMemoryContext);
+
+	PostgresMain(MyProcPort->database_name, MyProcPort->user_name);
+}
+
 /*
  * Try to report backend fork() failure to client before we close the
  * connection.  Since we do not care to risk blocking the postmaster on
@@ -4378,729 +4333,161 @@ BackendInitialize(ClientSocket *client_sock, CAC_state cac)
 
 
 /*
- * BackendRun -- set up the backend's argument list and invoke PostgresMain()
+ * ExitPostmaster -- cleanup
  *
- * returns:
- *		Doesn't return at all.
+ * Do NOT call exit() directly --- always go through here!
  */
 static void
-BackendRun(void)
+ExitPostmaster(int status)
 {
-	/*
-	 * Create a per-backend PGPROC struct in shared memory.  We must do this
-	 * before we can use LWLocks or access any shared memory.
-	 */
-	InitProcess();
+#ifdef HAVE_PTHREAD_IS_THREADED_NP
 
 	/*
-	 * Make sure we aren't in PostmasterContext anymore.  (We can't delete it
-	 * just yet, though, because InitPostgres will need the HBA data.)
+	 * There is no known cause for a postmaster to become multithreaded after
+	 * startup.  Recheck to account for the possibility of unknown causes.
+	 * This message uses LOG level, because an unclean shutdown at this point
+	 * would usually not look much different from a clean shutdown.
 	 */
-	MemoryContextSwitchTo(TopMemoryContext);
-
-	PostgresMain(MyProcPort->database_name, MyProcPort->user_name);
-}
-
-
-#ifdef EXEC_BACKEND
-
-/*
- * postmaster_forkexec -- fork and exec a postmaster subprocess
- *
- * The caller must have set up the argv array already, except for argv[2]
- * which will be filled with the name of the temp variable file.
- *
- * Returns the child process PID, or -1 on fork failure (a suitable error
- * message has been logged on failure).
- *
- * All uses of this routine will dispatch to SubPostmasterMain in the
- * child process.
- */
-pid_t
-postmaster_forkexec(int argc, char *argv[])
-{
-	ClientSocket client_sock;
-
-	/* This entry point doesn't pass a client socket */
-	memset(&client_sock, 0, sizeof(ClientSocket));
-	return internal_forkexec(argc, argv, &client_sock);
-}
-
-/*
- * backend_forkexec -- fork/exec off a backend process
- *
- * Some operating systems (WIN32) don't have fork() so we have to simulate
- * it by storing parameters that need to be passed to the child and
- * then create a new child process.
- *
- * returns the pid of the fork/exec'd process, or -1 on failure
- */
-static pid_t
-backend_forkexec(ClientSocket *client_sock, CAC_state cac)
-{
-	char	   *av[5];
-	int			ac = 0;
-	char		cacbuf[10];
-
-	av[ac++] = "postgres";
-	av[ac++] = "--forkbackend";
-	av[ac++] = NULL;			/* filled in by internal_forkexec */
+	if (pthread_is_threaded_np() != 0)
+		ereport(LOG,
+				(errcode(ERRCODE_INTERNAL_ERROR),
+				 errmsg_internal("postmaster became multithreaded"),
+				 errdetail("Please report this to <%s>.", PACKAGE_BUGREPORT)));
+#endif
 
-	snprintf(cacbuf, sizeof(cacbuf), "%d", (int) cac);
-	av[ac++] = cacbuf;
+	/* should cleanup shared memory and kill all backends */
 
-	av[ac] = NULL;
-	Assert(ac < lengthof(av));
+	/*
+	 * Not sure of the semantics here.  When the Postmaster dies, should the
+	 * backends all be killed? probably not.
+	 *
+	 * MUST		-- vadim 05-10-1999
+	 */
 
-	return internal_forkexec(ac, av, client_sock);
+	proc_exit(status);
 }
 
-#ifndef WIN32
-
 /*
- * internal_forkexec non-win32 implementation
- *
- * - writes out backend variables to the parameter file
- * - fork():s, and then exec():s the child process
+ * Handle pmsignal conditions representing requests from backends,
+ * and check for promote and logrotate requests from pg_ctl.
  */
-static pid_t
-internal_forkexec(int argc, char *argv[], ClientSocket *client_sock)
+static void
+process_pm_pmsignal(void)
 {
-	static unsigned long tmpBackendFileNum = 0;
-	pid_t		pid;
-	char		tmpfilename[MAXPGPATH];
-	BackendParameters param;
-	FILE	   *fp;
-
-	if (!save_backend_variables(&param, client_sock))
-		return -1;				/* log made by save_backend_variables */
+	pending_pm_pmsignal = false;
 
-	/* Calculate name for temp file */
-	snprintf(tmpfilename, MAXPGPATH, "%s/%s.backend_var.%d.%lu",
-			 PG_TEMP_FILES_DIR, PG_TEMP_FILE_PREFIX,
-			 MyProcPid, ++tmpBackendFileNum);
+	ereport(DEBUG2,
+			(errmsg_internal("postmaster received pmsignal signal")));
 
-	/* Open file */
-	fp = AllocateFile(tmpfilename, PG_BINARY_W);
-	if (!fp)
+	/*
+	 * RECOVERY_STARTED and BEGIN_HOT_STANDBY signals are ignored in
+	 * unexpected states. If the startup process quickly starts up, completes
+	 * recovery, exits, we might process the death of the startup process
+	 * first. We don't want to go back to recovery in that case.
+	 */
+	if (CheckPostmasterSignal(PMSIGNAL_RECOVERY_STARTED) &&
+		pmState == PM_STARTUP && Shutdown == NoShutdown)
 	{
+		/* WAL redo has started. We're out of reinitialization. */
+		FatalError = false;
+		AbortStartTime = 0;
+
 		/*
-		 * As in OpenTemporaryFileInTablespace, try to make the temp-file
-		 * directory, ignoring errors.
+		 * Start the archiver if we're responsible for (re-)archiving received
+		 * files.
 		 */
-		(void) MakePGDirectory(PG_TEMP_FILES_DIR);
+		Assert(PgArchPID == 0);
+		if (XLogArchivingAlways())
+			PgArchPID = StartArchiver();
 
-		fp = AllocateFile(tmpfilename, PG_BINARY_W);
-		if (!fp)
+		/*
+		 * If we aren't planning to enter hot standby mode later, treat
+		 * RECOVERY_STARTED as meaning we're out of startup, and report status
+		 * accordingly.
+		 */
+		if (!EnableHotStandby)
 		{
-			ereport(LOG,
-					(errcode_for_file_access(),
-					 errmsg("could not create file \"%s\": %m",
-							tmpfilename)));
-			return -1;
+			AddToDataDirLockFile(LOCK_FILE_LINE_PM_STATUS, PM_STATUS_STANDBY);
+#ifdef USE_SYSTEMD
+			sd_notify(0, "READY=1");
+#endif
 		}
+
+		pmState = PM_RECOVERY;
 	}
 
-	if (fwrite(&param, sizeof(param), 1, fp) != 1)
+	if (CheckPostmasterSignal(PMSIGNAL_BEGIN_HOT_STANDBY) &&
+		pmState == PM_RECOVERY && Shutdown == NoShutdown)
 	{
 		ereport(LOG,
-				(errcode_for_file_access(),
-				 errmsg("could not write to file \"%s\": %m", tmpfilename)));
-		FreeFile(fp);
-		return -1;
+				(errmsg("database system is ready to accept read-only connections")));
+
+		/* Report status */
+		AddToDataDirLockFile(LOCK_FILE_LINE_PM_STATUS, PM_STATUS_READY);
+#ifdef USE_SYSTEMD
+		sd_notify(0, "READY=1");
+#endif
+
+		pmState = PM_HOT_STANDBY;
+		connsAllowed = true;
+
+		/* Some workers may be scheduled to start now */
+		StartWorkerNeeded = true;
 	}
 
-	/* Release file */
-	if (FreeFile(fp))
+	/* Process background worker state changes. */
+	if (CheckPostmasterSignal(PMSIGNAL_BACKGROUND_WORKER_CHANGE))
 	{
-		ereport(LOG,
-				(errcode_for_file_access(),
-				 errmsg("could not write to file \"%s\": %m", tmpfilename)));
-		return -1;
+		/* Accept new worker requests only if not stopping. */
+		BackgroundWorkerStateChange(pmState < PM_STOP_BACKENDS);
+		StartWorkerNeeded = true;
 	}
 
-	/* Make sure caller set up argv properly */
-	Assert(argc >= 3);
-	Assert(argv[argc] == NULL);
-	Assert(strncmp(argv[1], "--fork", 6) == 0);
-	Assert(argv[2] == NULL);
-
-	/* Insert temp file name after --fork argument */
-	argv[2] = tmpfilename;
+	if (StartWorkerNeeded || HaveCrashedWorker)
+		maybe_start_bgworkers();
 
-	/* Fire off execv in child */
-	if ((pid = fork_process()) == 0)
+	/* Tell syslogger to rotate logfile if requested */
+	if (SysLoggerPID != 0)
 	{
-		if (execv(postgres_exec_path, argv) < 0)
+		if (CheckLogrotateSignal())
 		{
-			ereport(LOG,
-					(errmsg("could not execute server process \"%s\": %m",
-							postgres_exec_path)));
-			/* We're already in the child process here, can't return */
-			exit(1);
+			signal_child(SysLoggerPID, SIGUSR1);
+			RemoveLogrotateSignalFiles();
+		}
+		else if (CheckPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE))
+		{
+			signal_child(SysLoggerPID, SIGUSR1);
 		}
 	}
 
-	return pid;					/* Parent returns pid, or -1 on fork failure */
-}
-#else							/* WIN32 */
-
-/*
- * internal_forkexec win32 implementation
- *
- * - starts backend using CreateProcess(), in suspended state
- * - writes out backend variables to the parameter file
- *	- during this, duplicates handles and sockets required for
- *	  inheritance into the new process
- * - resumes execution of the new process once the backend parameter
- *	 file is complete.
- */
-static pid_t
-internal_forkexec(int argc, char *argv[], Port *port)
-{
-	int			retry_count = 0;
-	STARTUPINFO si;
-	PROCESS_INFORMATION pi;
-	int			i;
-	int			j;
-	char		cmdLine[MAXPGPATH * 2];
-	HANDLE		paramHandle;
-	BackendParameters *param;
-	SECURITY_ATTRIBUTES sa;
-	char		paramHandleStr[32];
-	win32_deadchild_waitinfo *childinfo;
-
-	/* Make sure caller set up argv properly */
-	Assert(argc >= 3);
-	Assert(argv[argc] == NULL);
-	Assert(strncmp(argv[1], "--fork", 6) == 0);
-	Assert(argv[2] == NULL);
-
-	/* Resume here if we need to retry */
-retry:
-
-	/* Set up shared memory for parameter passing */
-	ZeroMemory(&sa, sizeof(sa));
-	sa.nLength = sizeof(sa);
-	sa.bInheritHandle = TRUE;
-	paramHandle = CreateFileMapping(INVALID_HANDLE_VALUE,
-									&sa,
-									PAGE_READWRITE,
-									0,
-									sizeof(BackendParameters),
-									NULL);
-	if (paramHandle == INVALID_HANDLE_VALUE)
+	if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER) &&
+		Shutdown <= SmartShutdown && pmState < PM_STOP_BACKENDS)
 	{
-		ereport(LOG,
-				(errmsg("could not create backend parameter file mapping: error code %lu",
-						GetLastError())));
-		return -1;
+		/*
+		 * Start one iteration of the autovacuum daemon, even if autovacuuming
+		 * is nominally not enabled.  This is so we can have an active defense
+		 * against transaction ID wraparound.  We set a flag for the main loop
+		 * to do it rather than trying to do it here --- this is because the
+		 * autovac process itself may send the signal, and we want to handle
+		 * that by launching another iteration as soon as the current one
+		 * completes.
+		 */
+		start_autovac_launcher = true;
 	}
 
-	param = MapViewOfFile(paramHandle, FILE_MAP_WRITE, 0, 0, sizeof(BackendParameters));
-	if (!param)
+	if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC_WORKER) &&
+		Shutdown <= SmartShutdown && pmState < PM_STOP_BACKENDS)
 	{
-		ereport(LOG,
-				(errmsg("could not map backend parameter memory: error code %lu",
-						GetLastError())));
-		CloseHandle(paramHandle);
-		return -1;
+		/* The autovacuum launcher wants us to start a worker process. */
+		StartAutovacuumWorker();
 	}
 
-	/* Insert temp file name after --fork argument */
-#ifdef _WIN64
-	sprintf(paramHandleStr, "%llu", (LONG_PTR) paramHandle);
-#else
-	sprintf(paramHandleStr, "%lu", (DWORD) paramHandle);
-#endif
-	argv[2] = paramHandleStr;
-
-	/* Format the cmd line */
-	cmdLine[sizeof(cmdLine) - 1] = '\0';
-	cmdLine[sizeof(cmdLine) - 2] = '\0';
-	snprintf(cmdLine, sizeof(cmdLine) - 1, "\"%s\"", postgres_exec_path);
-	i = 0;
-	while (argv[++i] != NULL)
-	{
-		j = strlen(cmdLine);
-		snprintf(cmdLine + j, sizeof(cmdLine) - 1 - j, " \"%s\"", argv[i]);
-	}
-	if (cmdLine[sizeof(cmdLine) - 2] != '\0')
+	if (CheckPostmasterSignal(PMSIGNAL_START_WALRECEIVER))
 	{
-		ereport(LOG,
-				(errmsg("subprocess command line too long")));
-		UnmapViewOfFile(param);
-		CloseHandle(paramHandle);
-		return -1;
-	}
-
-	memset(&pi, 0, sizeof(pi));
-	memset(&si, 0, sizeof(si));
-	si.cb = sizeof(si);
-
-	/*
-	 * Create the subprocess in a suspended state. This will be resumed later,
-	 * once we have written out the parameter file.
-	 */
-	if (!CreateProcess(NULL, cmdLine, NULL, NULL, TRUE, CREATE_SUSPENDED,
-					   NULL, NULL, &si, &pi))
-	{
-		ereport(LOG,
-				(errmsg("CreateProcess() call failed: %m (error code %lu)",
-						GetLastError())));
-		UnmapViewOfFile(param);
-		CloseHandle(paramHandle);
-		return -1;
-	}
-
-	if (!save_backend_variables(param, port, pi.hProcess, pi.dwProcessId))
-	{
-		/*
-		 * log made by save_backend_variables, but we have to clean up the
-		 * mess with the half-started process
-		 */
-		if (!TerminateProcess(pi.hProcess, 255))
-			ereport(LOG,
-					(errmsg_internal("could not terminate unstarted process: error code %lu",
-									 GetLastError())));
-		CloseHandle(pi.hProcess);
-		CloseHandle(pi.hThread);
-		UnmapViewOfFile(param);
-		CloseHandle(paramHandle);
-		return -1;				/* log made by save_backend_variables */
-	}
-
-	/* Drop the parameter shared memory that is now inherited to the backend */
-	if (!UnmapViewOfFile(param))
-		ereport(LOG,
-				(errmsg("could not unmap view of backend parameter file: error code %lu",
-						GetLastError())));
-	if (!CloseHandle(paramHandle))
-		ereport(LOG,
-				(errmsg("could not close handle to backend parameter file: error code %lu",
-						GetLastError())));
-
-	/*
-	 * Reserve the memory region used by our main shared memory segment before
-	 * we resume the child process.  Normally this should succeed, but if ASLR
-	 * is active then it might sometimes fail due to the stack or heap having
-	 * gotten mapped into that range.  In that case, just terminate the
-	 * process and retry.
-	 */
-	if (!pgwin32_ReserveSharedMemoryRegion(pi.hProcess))
-	{
-		/* pgwin32_ReserveSharedMemoryRegion already made a log entry */
-		if (!TerminateProcess(pi.hProcess, 255))
-			ereport(LOG,
-					(errmsg_internal("could not terminate process that failed to reserve memory: error code %lu",
-									 GetLastError())));
-		CloseHandle(pi.hProcess);
-		CloseHandle(pi.hThread);
-		if (++retry_count < 100)
-			goto retry;
-		ereport(LOG,
-				(errmsg("giving up after too many tries to reserve shared memory"),
-				 errhint("This might be caused by ASLR or antivirus software.")));
-		return -1;
-	}
-
-	/*
-	 * Now that the backend variables are written out, we start the child
-	 * thread so it can start initializing while we set up the rest of the
-	 * parent state.
-	 */
-	if (ResumeThread(pi.hThread) == -1)
-	{
-		if (!TerminateProcess(pi.hProcess, 255))
-		{
-			ereport(LOG,
-					(errmsg_internal("could not terminate unstartable process: error code %lu",
-									 GetLastError())));
-			CloseHandle(pi.hProcess);
-			CloseHandle(pi.hThread);
-			return -1;
-		}
-		CloseHandle(pi.hProcess);
-		CloseHandle(pi.hThread);
-		ereport(LOG,
-				(errmsg_internal("could not resume thread of unstarted process: error code %lu",
-								 GetLastError())));
-		return -1;
-	}
-
-	/*
-	 * Queue a waiter to signal when this child dies. The wait will be handled
-	 * automatically by an operating system thread pool.  The memory will be
-	 * freed by a later call to waitpid().
-	 */
-	childinfo = palloc(sizeof(win32_deadchild_waitinfo));
-	childinfo->procHandle = pi.hProcess;
-	childinfo->procId = pi.dwProcessId;
-
-	if (!RegisterWaitForSingleObject(&childinfo->waitHandle,
-									 pi.hProcess,
-									 pgwin32_deadchild_callback,
-									 childinfo,
-									 INFINITE,
-									 WT_EXECUTEONLYONCE | WT_EXECUTEINWAITTHREAD))
-		ereport(FATAL,
-				(errmsg_internal("could not register process for wait: error code %lu",
-								 GetLastError())));
-
-	/* Don't close pi.hProcess here - waitpid() needs access to it */
-
-	CloseHandle(pi.hThread);
-
-	return pi.dwProcessId;
-}
-#endif							/* WIN32 */
-
-
-/*
- * SubPostmasterMain -- Get the fork/exec'd process into a state equivalent
- *			to what it would be if we'd simply forked on Unix, and then
- *			dispatch to the appropriate place.
- *
- * The first two command line arguments are expected to be "--forkFOO"
- * (where FOO indicates which postmaster child we are to become), and
- * the name of a variables file that we can read to load data that would
- * have been inherited by fork() on Unix.  Remaining arguments go to the
- * subprocess FooMain() routine.
- */
-void
-SubPostmasterMain(int argc, char *argv[])
-{
-	ClientSocket client_sock;
-
-	/* In EXEC_BACKEND case we will not have inherited these settings */
-	IsPostmasterEnvironment = true;
-	whereToSendOutput = DestNone;
-
-	/* Setup essential subsystems (to ensure elog() behaves sanely) */
-	InitializeGUCOptions();
-
-	/* Check we got appropriate args */
-	if (argc < 3)
-		elog(FATAL, "invalid subpostmaster invocation");
-
-	/* Read in the variables file */
-	memset(&client_sock, 0, sizeof(ClientSocket));
-	read_backend_variables(argv[2], &client_sock);
-
-	/* Close the postmaster's sockets (as soon as we know them) */
-	ClosePostmasterPorts(strcmp(argv[1], "--forklog") == 0);
-
-	/* Setup as postmaster child */
-	InitPostmasterChild();
-
-	/*
-	 * If appropriate, physically re-attach to shared memory segment. We want
-	 * to do this before going any further to ensure that we can attach at the
-	 * same address the postmaster used.  On the other hand, if we choose not
-	 * to re-attach, we may have other cleanup to do.
-	 *
-	 * If testing EXEC_BACKEND on Linux, you should run this as root before
-	 * starting the postmaster:
-	 *
-	 * sysctl -w kernel.randomize_va_space=0
-	 *
-	 * This prevents using randomized stack and code addresses that cause the
-	 * child process's memory map to be different from the parent's, making it
-	 * sometimes impossible to attach to shared memory at the desired address.
-	 * Return the setting to its old value (usually '1' or '2') when finished.
-	 */
-	if (strcmp(argv[1], "--forkbackend") == 0 ||
-		strcmp(argv[1], "--forkavlauncher") == 0 ||
-		strcmp(argv[1], "--forkavworker") == 0 ||
-		strcmp(argv[1], "--forkaux") == 0 ||
-		strncmp(argv[1], "--forkbgworker", 14) == 0)
-		PGSharedMemoryReAttach();
-	else
-		PGSharedMemoryNoReAttach();
-
-	/* autovacuum needs this set before calling InitProcess */
-	if (strcmp(argv[1], "--forkavlauncher") == 0)
-		AutovacuumLauncherIAm();
-	if (strcmp(argv[1], "--forkavworker") == 0)
-		AutovacuumWorkerIAm();
-
-	/* Read in remaining GUC variables */
-	read_nondefault_variables();
-
-	/*
-	 * Check that the data directory looks valid, which will also check the
-	 * privileges on the data directory and update our umask and file/group
-	 * variables for creating files later.  Note: this should really be done
-	 * before we create any files or directories.
-	 */
-	checkDataDir();
-
-	/*
-	 * (re-)read control file, as it contains config. The postmaster will
-	 * already have read this, but this process doesn't know about that.
-	 */
-	LocalProcessControlFile(false);
-
-	/*
-	 * Reload any libraries that were preloaded by the postmaster.  Since we
-	 * exec'd this process, those libraries didn't come along with us; but we
-	 * should load them into all child processes to be consistent with the
-	 * non-EXEC_BACKEND behavior.
-	 */
-	process_shared_preload_libraries();
-
-	/* Run backend or appropriate child */
-	if (strcmp(argv[1], "--forkbackend") == 0)
-	{
-		CAC_state	cac;
-
-		Assert(argc == 4);
-		cac = (CAC_state) atoi(argv[3]);
-
-		/*
-		 * Need to reinitialize the SSL library in the backend, since the
-		 * context structures contain function pointers and cannot be passed
-		 * through the parameter file.
-		 *
-		 * If for some reason reload fails (maybe the user installed broken
-		 * key files), soldier on without SSL; that's better than all
-		 * connections becoming impossible.
-		 *
-		 * XXX should we do this in all child processes?  For the moment it's
-		 * enough to do it in backend children.
-		 */
-#ifdef USE_SSL
-		if (EnableSSL)
-		{
-			if (secure_initialize(false) == 0)
-				LoadedSSL = true;
-			else
-				ereport(LOG,
-						(errmsg("SSL configuration could not be loaded in child process")));
-		}
-#endif
-
-		/*
-		 * Perform additional initialization and collect startup packet.
-		 *
-		 * We want to do this before InitProcess() for a couple of reasons: 1.
-		 * so that we aren't eating up a PGPROC slot while waiting on the
-		 * client. 2. so that if InitProcess() fails due to being out of
-		 * PGPROC slots, we have already initialized libpq and are able to
-		 * report the error to the client.
-		 */
-		BackendInitialize(&client_sock, cac);
-
-		/* Restore basic shared memory pointers */
-		InitShmemAccess(UsedShmemSegAddr);
-
-		/* And run the backend */
-		BackendRun();			/* does not return */
-	}
-	if (strcmp(argv[1], "--forkaux") == 0)
-	{
-		AuxProcType auxtype;
-
-		Assert(argc == 4);
-
-		/* Restore basic shared memory pointers */
-		InitShmemAccess(UsedShmemSegAddr);
-
-		auxtype = atoi(argv[3]);
-		AuxiliaryProcessMain(auxtype);	/* does not return */
-	}
-	if (strcmp(argv[1], "--forkavlauncher") == 0)
-	{
-		/* Restore basic shared memory pointers */
-		InitShmemAccess(UsedShmemSegAddr);
-
-		AutoVacLauncherMain(argc - 2, argv + 2);	/* does not return */
-	}
-	if (strcmp(argv[1], "--forkavworker") == 0)
-	{
-		/* Restore basic shared memory pointers */
-		InitShmemAccess(UsedShmemSegAddr);
-
-		AutoVacWorkerMain(argc - 2, argv + 2);	/* does not return */
-	}
-	if (strncmp(argv[1], "--forkbgworker", 14) == 0)
-	{
-		/* do this as early as possible; in particular, before InitProcess() */
-		IsBackgroundWorker = true;
-
-		/* Restore basic shared memory pointers */
-		InitShmemAccess(UsedShmemSegAddr);
-
-		BackgroundWorkerMain();
-	}
-	if (strcmp(argv[1], "--forklog") == 0)
-	{
-		/* Do not want to attach to shared memory */
-
-		SysLoggerMain(argc, argv);	/* does not return */
-	}
-
-	abort();					/* shouldn't get here */
-}
-#endif							/* EXEC_BACKEND */
-
-
-/*
- * ExitPostmaster -- cleanup
- *
- * Do NOT call exit() directly --- always go through here!
- */
-static void
-ExitPostmaster(int status)
-{
-#ifdef HAVE_PTHREAD_IS_THREADED_NP
-
-	/*
-	 * There is no known cause for a postmaster to become multithreaded after
-	 * startup.  Recheck to account for the possibility of unknown causes.
-	 * This message uses LOG level, because an unclean shutdown at this point
-	 * would usually not look much different from a clean shutdown.
-	 */
-	if (pthread_is_threaded_np() != 0)
-		ereport(LOG,
-				(errcode(ERRCODE_INTERNAL_ERROR),
-				 errmsg_internal("postmaster became multithreaded"),
-				 errdetail("Please report this to <%s>.", PACKAGE_BUGREPORT)));
-#endif
-
-	/* should cleanup shared memory and kill all backends */
-
-	/*
-	 * Not sure of the semantics here.  When the Postmaster dies, should the
-	 * backends all be killed? probably not.
-	 *
-	 * MUST		-- vadim 05-10-1999
-	 */
-
-	proc_exit(status);
-}
-
-/*
- * Handle pmsignal conditions representing requests from backends,
- * and check for promote and logrotate requests from pg_ctl.
- */
-static void
-process_pm_pmsignal(void)
-{
-	pending_pm_pmsignal = false;
-
-	ereport(DEBUG2,
-			(errmsg_internal("postmaster received pmsignal signal")));
-
-	/*
-	 * RECOVERY_STARTED and BEGIN_HOT_STANDBY signals are ignored in
-	 * unexpected states. If the startup process quickly starts up, completes
-	 * recovery, exits, we might process the death of the startup process
-	 * first. We don't want to go back to recovery in that case.
-	 */
-	if (CheckPostmasterSignal(PMSIGNAL_RECOVERY_STARTED) &&
-		pmState == PM_STARTUP && Shutdown == NoShutdown)
-	{
-		/* WAL redo has started. We're out of reinitialization. */
-		FatalError = false;
-		AbortStartTime = 0;
-
-		/*
-		 * Start the archiver if we're responsible for (re-)archiving received
-		 * files.
-		 */
-		Assert(PgArchPID == 0);
-		if (XLogArchivingAlways())
-			PgArchPID = StartArchiver();
-
-		/*
-		 * If we aren't planning to enter hot standby mode later, treat
-		 * RECOVERY_STARTED as meaning we're out of startup, and report status
-		 * accordingly.
-		 */
-		if (!EnableHotStandby)
-		{
-			AddToDataDirLockFile(LOCK_FILE_LINE_PM_STATUS, PM_STATUS_STANDBY);
-#ifdef USE_SYSTEMD
-			sd_notify(0, "READY=1");
-#endif
-		}
-
-		pmState = PM_RECOVERY;
-	}
-
-	if (CheckPostmasterSignal(PMSIGNAL_BEGIN_HOT_STANDBY) &&
-		pmState == PM_RECOVERY && Shutdown == NoShutdown)
-	{
-		ereport(LOG,
-				(errmsg("database system is ready to accept read-only connections")));
-
-		/* Report status */
-		AddToDataDirLockFile(LOCK_FILE_LINE_PM_STATUS, PM_STATUS_READY);
-#ifdef USE_SYSTEMD
-		sd_notify(0, "READY=1");
-#endif
-
-		pmState = PM_HOT_STANDBY;
-		connsAllowed = true;
-
-		/* Some workers may be scheduled to start now */
-		StartWorkerNeeded = true;
-	}
-
-	/* Process background worker state changes. */
-	if (CheckPostmasterSignal(PMSIGNAL_BACKGROUND_WORKER_CHANGE))
-	{
-		/* Accept new worker requests only if not stopping. */
-		BackgroundWorkerStateChange(pmState < PM_STOP_BACKENDS);
-		StartWorkerNeeded = true;
-	}
-
-	if (StartWorkerNeeded || HaveCrashedWorker)
-		maybe_start_bgworkers();
-
-	/* Tell syslogger to rotate logfile if requested */
-	if (SysLoggerPID != 0)
-	{
-		if (CheckLogrotateSignal())
-		{
-			signal_child(SysLoggerPID, SIGUSR1);
-			RemoveLogrotateSignalFiles();
-		}
-		else if (CheckPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE))
-		{
-			signal_child(SysLoggerPID, SIGUSR1);
-		}
-	}
-
-	if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER) &&
-		Shutdown <= SmartShutdown && pmState < PM_STOP_BACKENDS)
-	{
-		/*
-		 * Start one iteration of the autovacuum daemon, even if autovacuuming
-		 * is nominally not enabled.  This is so we can have an active defense
-		 * against transaction ID wraparound.  We set a flag for the main loop
-		 * to do it rather than trying to do it here --- this is because the
-		 * autovac process itself may send the signal, and we want to handle
-		 * that by launching another iteration as soon as the current one
-		 * completes.
-		 */
-		start_autovac_launcher = true;
-	}
-
-	if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC_WORKER) &&
-		Shutdown <= SmartShutdown && pmState < PM_STOP_BACKENDS)
-	{
-		/* The autovacuum launcher wants us to start a worker process. */
-		StartAutovacuumWorker();
-	}
-
-	if (CheckPostmasterSignal(PMSIGNAL_START_WALRECEIVER))
-	{
-		/* Startup Process wants us to start the walreceiver process. */
-		/* Start immediately if possible, else remember request for later. */
-		WalReceiverRequested = true;
-		MaybeStartWalReceiver();
+		/* Startup Process wants us to start the walreceiver process. */
+		/* Start immediately if possible, else remember request for later. */
+		WalReceiverRequested = true;
+		MaybeStartWalReceiver();
 	}
 
 	/*
@@ -5237,93 +4624,23 @@ CountChildren(int target)
  * to start subprocess.
  */
 static pid_t
-StartChildProcess(AuxProcType type)
+StartChildProcess(PostmasterChildType type)
 {
 	pid_t		pid;
 
-#ifdef EXEC_BACKEND
-	{
-		char	   *av[10];
-		int			ac = 0;
-		char		typebuf[32];
-
-		/*
-		 * Set up command-line arguments for subprocess
-		 */
-		av[ac++] = "postgres";
-		av[ac++] = "--forkaux";
-		av[ac++] = NULL;		/* filled in by postmaster_forkexec */
-
-		snprintf(typebuf, sizeof(typebuf), "%d", type);
-		av[ac++] = typebuf;
-
-		av[ac] = NULL;
-		Assert(ac < lengthof(av));
-
-		pid = postmaster_forkexec(ac, av);
-	}
-#else							/* !EXEC_BACKEND */
-	pid = fork_process();
-
-	if (pid == 0)				/* child */
-	{
-		InitPostmasterChild();
-
-		/* Close the postmaster's sockets */
-		ClosePostmasterPorts(false);
-
-		/* Release postmaster's working memory context */
-		MemoryContextSwitchTo(TopMemoryContext);
-		MemoryContextDelete(PostmasterContext);
-		PostmasterContext = NULL;
-
-		AuxiliaryProcessMain(type); /* does not return */
-	}
-#endif							/* EXEC_BACKEND */
-
+	pid = postmaster_child_launch(type, NULL, 0, NULL);
 	if (pid < 0)
 	{
 		/* in parent, fork failed */
-		int			save_errno = errno;
-
-		errno = save_errno;
-		switch (type)
-		{
-			case StartupProcess:
-				ereport(LOG,
-						(errmsg("could not fork startup process: %m")));
-				break;
-			case ArchiverProcess:
-				ereport(LOG,
-						(errmsg("could not fork archiver process: %m")));
-				break;
-			case BgWriterProcess:
-				ereport(LOG,
-						(errmsg("could not fork background writer process: %m")));
-				break;
-			case CheckpointerProcess:
-				ereport(LOG,
-						(errmsg("could not fork checkpointer process: %m")));
-				break;
-			case WalWriterProcess:
-				ereport(LOG,
-						(errmsg("could not fork WAL writer process: %m")));
-				break;
-			case WalReceiverProcess:
-				ereport(LOG,
-						(errmsg("could not fork WAL receiver process: %m")));
-				break;
-			default:
-				ereport(LOG,
-						(errmsg("could not fork process: %m")));
-				break;
-		}
+		/* XXX: translation? */
+		ereport(LOG,
+				(errmsg("could not fork %s process: %m", PostmasterChildName(type))));
 
 		/*
 		 * fork failure is fatal during startup, but there's no need to choke
 		 * immediately if starting other child types fails.
 		 */
-		if (type == StartupProcess)
+		if (type == PMC_STARTUP)
 			ExitPostmaster(1);
 		return 0;
 	}
@@ -5587,32 +4904,6 @@ BackgroundWorkerUnblockSignals(void)
 	sigprocmask(SIG_SETMASK, &UnBlockSig, NULL);
 }
 
-#ifdef EXEC_BACKEND
-static pid_t
-bgworker_forkexec(BackgroundWorker *worker)
-{
-	char	   *av[10];
-	int			ac = 0;
-	char		forkav[MAXPGPATH];
-	pid_t		result;
-
-	snprintf(forkav, MAXPGPATH, "--forkbgworker");
-
-	av[ac++] = "postgres";
-	av[ac++] = forkav;
-	av[ac++] = NULL;			/* filled in by postmaster_forkexec */
-	av[ac] = NULL;
-
-	Assert(ac < lengthof(av));
-
-	MyBgworkerEntry = worker;
-	result = postmaster_forkexec(ac, av);
-	MyBgworkerEntry = NULL;
-
-	return result;
-}
-#endif
-
 /*
  * Start a new bgworker.
  * Starting time conditions must have been checked already.
@@ -5649,65 +4940,32 @@ do_start_bgworker(RegisteredBgWorker *rw)
 			(errmsg_internal("starting background worker process \"%s\"",
 							 rw->rw_worker.bgw_name)));
 
-#ifdef EXEC_BACKEND
-	switch ((worker_pid = bgworker_forkexec(&rw->rw_worker)))
-#else
-	switch ((worker_pid = fork_process()))
-#endif
+	worker_pid = postmaster_child_launch(PMC_BGWORKER, (char *) &rw->rw_worker, sizeof(BackgroundWorker), NULL);
+	if (worker_pid == -1)
 	{
-		case -1:
-			/* in postmaster, fork failed ... */
-			ereport(LOG,
-					(errmsg("could not fork worker process: %m")));
-			/* undo what assign_backendlist_entry did */
-			ReleasePostmasterChildSlot(rw->rw_child_slot);
-			rw->rw_child_slot = 0;
-			pfree(rw->rw_backend);
-			rw->rw_backend = NULL;
-			/* mark entry as crashed, so we'll try again later */
-			rw->rw_crashed_at = GetCurrentTimestamp();
-			break;
-
-#ifndef EXEC_BACKEND
-		case 0:
-			/* in postmaster child ... */
-			InitPostmasterChild();
-
-			/* Close the postmaster's sockets */
-			ClosePostmasterPorts(false);
-
-			/*
-			 * Before blowing away PostmasterContext, save this bgworker's
-			 * data where it can find it.
-			 */
-			MyBgworkerEntry = (BackgroundWorker *)
-				MemoryContextAlloc(TopMemoryContext, sizeof(BackgroundWorker));
-			memcpy(MyBgworkerEntry, &rw->rw_worker, sizeof(BackgroundWorker));
-
-			/* Release postmaster's working memory context */
-			MemoryContextSwitchTo(TopMemoryContext);
-			MemoryContextDelete(PostmasterContext);
-			PostmasterContext = NULL;
-
-			BackgroundWorkerMain();
+		/* in postmaster, fork failed ... */
+		ereport(LOG,
+				(errmsg("could not fork worker process: %m")));
+		/* undo what assign_backendlist_entry did */
+		ReleasePostmasterChildSlot(rw->rw_child_slot);
+		rw->rw_child_slot = 0;
+		pfree(rw->rw_backend);
+		rw->rw_backend = NULL;
+		/* mark entry as crashed, so we'll try again later */
+		rw->rw_crashed_at = GetCurrentTimestamp();
+		return false;
+	}
 
-			exit(1);			/* should not get here */
-			break;
-#endif
-		default:
-			/* in postmaster, fork successful ... */
-			rw->rw_pid = worker_pid;
-			rw->rw_backend->pid = rw->rw_pid;
-			ReportBackgroundWorkerPID(rw);
-			/* add new worker to lists of backends */
-			dlist_push_head(&BackendList, &rw->rw_backend->elem);
+	/* in postmaster, fork successful ... */
+	rw->rw_pid = worker_pid;
+	rw->rw_backend->pid = rw->rw_pid;
+	ReportBackgroundWorkerPID(rw);
+	/* add new worker to lists of backends */
+	dlist_push_head(&BackendList, &rw->rw_backend->elem);
 #ifdef EXEC_BACKEND
-			ShmemBackendArrayAdd(rw->rw_backend);
+	ShmemBackendArrayAdd(rw->rw_backend);
 #endif
-			return true;
-	}
-
-	return false;
+	return true;
 }
 
 /*
@@ -5959,351 +5217,6 @@ PostmasterMarkPIDForWorkerNotify(int pid)
 
 #ifdef EXEC_BACKEND
 
-/*
- * The following need to be available to the save/restore_backend_variables
- * functions.  They are marked NON_EXEC_STATIC in their home modules.
- */
-extern slock_t *ShmemLock;
-extern slock_t *ProcStructLock;
-extern PGPROC *AuxiliaryProcs;
-extern PMSignalData *PMSignalState;
-extern pg_time_t first_syslogger_file_time;
-
-#ifndef WIN32
-#define write_inheritable_socket(dest, src, childpid) ((*(dest) = (src)), true)
-#define read_inheritable_socket(dest, src) (*(dest) = *(src))
-#else
-static bool write_duplicated_handle(HANDLE *dest, HANDLE src, HANDLE child);
-static bool write_inheritable_socket(InheritableSocket *dest, SOCKET src,
-									 pid_t childPid);
-static void read_inheritable_socket(SOCKET *dest, InheritableSocket *src);
-#endif
-
-
-/* Save critical backend variables into the BackendParameters struct */
-#ifndef WIN32
-static bool
-save_backend_variables(BackendParameters *param, ClientSocket *client_sock)
-#else
-static bool
-save_backend_variables(BackendParameters *param, ClientSocket *client_sock,
-					   HANDLE childProcess, pid_t childPid)
-#endif
-{
-	memcpy(&param->client_sock, client_sock, sizeof(ClientSocket));
-	if (!write_inheritable_socket(&param->serialized_sock, client_sock->sock, childPid))
-		return false;
-
-	strlcpy(param->DataDir, DataDir, MAXPGPATH);
-
-	param->MyCancelKey = MyCancelKey;
-	param->MyPMChildSlot = MyPMChildSlot;
-
-#ifdef WIN32
-	param->ShmemProtectiveRegion = ShmemProtectiveRegion;
-#endif
-	param->UsedShmemSegID = UsedShmemSegID;
-	param->UsedShmemSegAddr = UsedShmemSegAddr;
-
-	param->ShmemLock = ShmemLock;
-	param->ShmemVariableCache = ShmemVariableCache;
-	param->ShmemBackendArray = ShmemBackendArray;
-
-#ifndef HAVE_SPINLOCKS
-	param->SpinlockSemaArray = SpinlockSemaArray;
-#endif
-	param->NamedLWLockTrancheRequests = NamedLWLockTrancheRequests;
-	param->NamedLWLockTrancheArray = NamedLWLockTrancheArray;
-	param->MainLWLockArray = MainLWLockArray;
-	param->ProcStructLock = ProcStructLock;
-	param->ProcGlobal = ProcGlobal;
-	param->AuxiliaryProcs = AuxiliaryProcs;
-	param->PreparedXactProcs = PreparedXactProcs;
-	param->PMSignalState = PMSignalState;
-
-	param->PostmasterPid = PostmasterPid;
-	param->PgStartTime = PgStartTime;
-	param->PgReloadTime = PgReloadTime;
-	param->first_syslogger_file_time = first_syslogger_file_time;
-
-	param->redirection_done = redirection_done;
-	param->IsBinaryUpgrade = IsBinaryUpgrade;
-	param->query_id_enabled = query_id_enabled;
-	param->max_safe_fds = max_safe_fds;
-
-	param->MaxBackends = MaxBackends;
-
-#ifdef WIN32
-	param->PostmasterHandle = PostmasterHandle;
-	if (!write_duplicated_handle(&param->initial_signal_pipe,
-								 pgwin32_create_signal_listener(childPid),
-								 childProcess))
-		return false;
-#else
-	memcpy(&param->postmaster_alive_fds, &postmaster_alive_fds,
-		   sizeof(postmaster_alive_fds));
-#endif
-
-	memcpy(&param->syslogPipe, &syslogPipe, sizeof(syslogPipe));
-
-	strlcpy(param->my_exec_path, my_exec_path, MAXPGPATH);
-
-	strlcpy(param->pkglib_path, pkglib_path, MAXPGPATH);
-
-	if (MyBgworkerEntry)
-		memcpy(&param->MyBgworkerEntry, MyBgworkerEntry, sizeof(BackgroundWorker));
-	else
-		memset(&param->MyBgworkerEntry, 0, sizeof(BackgroundWorker));
-
-	return true;
-}
-
-
-#ifdef WIN32
-/*
- * Duplicate a handle for usage in a child process, and write the child
- * process instance of the handle to the parameter file.
- */
-static bool
-write_duplicated_handle(HANDLE *dest, HANDLE src, HANDLE childProcess)
-{
-	HANDLE		hChild = INVALID_HANDLE_VALUE;
-
-	if (!DuplicateHandle(GetCurrentProcess(),
-						 src,
-						 childProcess,
-						 &hChild,
-						 0,
-						 TRUE,
-						 DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS))
-	{
-		ereport(LOG,
-				(errmsg_internal("could not duplicate handle to be written to backend parameter file: error code %lu",
-								 GetLastError())));
-		return false;
-	}
-
-	*dest = hChild;
-	return true;
-}
-
-/*
- * Duplicate a socket for usage in a child process, and write the resulting
- * structure to the parameter file.
- * This is required because a number of LSPs (Layered Service Providers) very
- * common on Windows (antivirus, firewalls, download managers etc) break
- * straight socket inheritance.
- */
-static bool
-write_inheritable_socket(InheritableSocket *dest, SOCKET src, pid_t childpid)
-{
-	dest->origsocket = src;
-	if (src != 0 && src != PGINVALID_SOCKET)
-	{
-		/* Actual socket */
-		if (WSADuplicateSocket(src, childpid, &dest->wsainfo) != 0)
-		{
-			ereport(LOG,
-					(errmsg("could not duplicate socket %d for use in backend: error code %d",
-							(int) src, WSAGetLastError())));
-			return false;
-		}
-	}
-	return true;
-}
-
-/*
- * Read a duplicate socket structure back, and get the socket descriptor.
- */
-static void
-read_inheritable_socket(SOCKET *dest, InheritableSocket *src)
-{
-	SOCKET		s;
-
-	if (src->origsocket == PGINVALID_SOCKET || src->origsocket == 0)
-	{
-		/* Not a real socket! */
-		*dest = src->origsocket;
-	}
-	else
-	{
-		/* Actual socket, so create from structure */
-		s = WSASocket(FROM_PROTOCOL_INFO,
-					  FROM_PROTOCOL_INFO,
-					  FROM_PROTOCOL_INFO,
-					  &src->wsainfo,
-					  0,
-					  0);
-		if (s == INVALID_SOCKET)
-		{
-			write_stderr("could not create inherited socket: error code %d\n",
-						 WSAGetLastError());
-			exit(1);
-		}
-		*dest = s;
-
-		/*
-		 * To make sure we don't get two references to the same socket, close
-		 * the original one. (This would happen when inheritance actually
-		 * works..
-		 */
-		closesocket(src->origsocket);
-	}
-}
-#endif
-
-static void
-read_backend_variables(char *id, ClientSocket *client_sock)
-{
-	BackendParameters param;
-
-#ifndef WIN32
-	/* Non-win32 implementation reads from file */
-	FILE	   *fp;
-
-	/* Open file */
-	fp = AllocateFile(id, PG_BINARY_R);
-	if (!fp)
-	{
-		write_stderr("could not open backend variables file \"%s\": %s\n",
-					 id, strerror(errno));
-		exit(1);
-	}
-
-	if (fread(&param, sizeof(param), 1, fp) != 1)
-	{
-		write_stderr("could not read from backend variables file \"%s\": %s\n",
-					 id, strerror(errno));
-		exit(1);
-	}
-
-	/* Release file */
-	FreeFile(fp);
-	if (unlink(id) != 0)
-	{
-		write_stderr("could not remove file \"%s\": %s\n",
-					 id, strerror(errno));
-		exit(1);
-	}
-#else
-	/* Win32 version uses mapped file */
-	HANDLE		paramHandle;
-	BackendParameters *paramp;
-
-#ifdef _WIN64
-	paramHandle = (HANDLE) _atoi64(id);
-#else
-	paramHandle = (HANDLE) atol(id);
-#endif
-	paramp = MapViewOfFile(paramHandle, FILE_MAP_READ, 0, 0, 0);
-	if (!paramp)
-	{
-		write_stderr("could not map view of backend variables: error code %lu\n",
-					 GetLastError());
-		exit(1);
-	}
-
-	memcpy(&param, paramp, sizeof(BackendParameters));
-
-	if (!UnmapViewOfFile(paramp))
-	{
-		write_stderr("could not unmap view of backend variables: error code %lu\n",
-					 GetLastError());
-		exit(1);
-	}
-
-	if (!CloseHandle(paramHandle))
-	{
-		write_stderr("could not close handle to backend parameter variables: error code %lu\n",
-					 GetLastError());
-		exit(1);
-	}
-#endif
-
-	restore_backend_variables(&param, client_sock);
-}
-
-/* Restore critical backend variables from the BackendParameters struct */
-static void
-restore_backend_variables(BackendParameters *param, ClientSocket *client_sock)
-{
-	memcpy(client_sock, &param->client_sock, sizeof(ClientSocket));
-	read_inheritable_socket(&client_sock->sock, &param->serialized_sock);
-
-	SetDataDir(param->DataDir);
-
-	MyCancelKey = param->MyCancelKey;
-	MyPMChildSlot = param->MyPMChildSlot;
-
-#ifdef WIN32
-	ShmemProtectiveRegion = param->ShmemProtectiveRegion;
-#endif
-	UsedShmemSegID = param->UsedShmemSegID;
-	UsedShmemSegAddr = param->UsedShmemSegAddr;
-
-	ShmemLock = param->ShmemLock;
-	ShmemVariableCache = param->ShmemVariableCache;
-	ShmemBackendArray = param->ShmemBackendArray;
-
-#ifndef HAVE_SPINLOCKS
-	SpinlockSemaArray = param->SpinlockSemaArray;
-#endif
-	NamedLWLockTrancheRequests = param->NamedLWLockTrancheRequests;
-	NamedLWLockTrancheArray = param->NamedLWLockTrancheArray;
-	MainLWLockArray = param->MainLWLockArray;
-	ProcStructLock = param->ProcStructLock;
-	ProcGlobal = param->ProcGlobal;
-	AuxiliaryProcs = param->AuxiliaryProcs;
-	PreparedXactProcs = param->PreparedXactProcs;
-	PMSignalState = param->PMSignalState;
-
-	PostmasterPid = param->PostmasterPid;
-	PgStartTime = param->PgStartTime;
-	PgReloadTime = param->PgReloadTime;
-	first_syslogger_file_time = param->first_syslogger_file_time;
-
-	redirection_done = param->redirection_done;
-	IsBinaryUpgrade = param->IsBinaryUpgrade;
-	query_id_enabled = param->query_id_enabled;
-	max_safe_fds = param->max_safe_fds;
-
-	MaxBackends = param->MaxBackends;
-
-#ifdef WIN32
-	PostmasterHandle = param->PostmasterHandle;
-	pgwin32_initial_signal_pipe = param->initial_signal_pipe;
-#else
-	memcpy(&postmaster_alive_fds, &param->postmaster_alive_fds,
-		   sizeof(postmaster_alive_fds));
-#endif
-
-	memcpy(&syslogPipe, &param->syslogPipe, sizeof(syslogPipe));
-
-	strlcpy(my_exec_path, param->my_exec_path, MAXPGPATH);
-
-	strlcpy(pkglib_path, param->pkglib_path, MAXPGPATH);
-
-	if (param->MyBgworkerEntry.bgw_name[0] != '\0')
-	{
-		MyBgworkerEntry = (BackgroundWorker *)
-			MemoryContextAlloc(TopMemoryContext, sizeof(BackgroundWorker));
-		memcpy(MyBgworkerEntry, &param->MyBgworkerEntry, sizeof(BackgroundWorker));
-	}
-
-	/*
-	 * We need to restore fd.c's counts of externally-opened FDs; to avoid
-	 * confusion, be sure to do this after restoring max_safe_fds.  (Note:
-	 * BackendInitialize will handle this for port->sock.)
-	 */
-#ifndef WIN32
-	if (postmaster_alive_fds[0] >= 0)
-		ReserveExternalFD();
-	if (postmaster_alive_fds[1] >= 0)
-		ReserveExternalFD();
-#endif
-}
-
-
 Size
 ShmemBackendArraySize(void)
 {
@@ -6425,6 +5338,32 @@ pgwin32_deadchild_callback(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
 	/* Queue SIGCHLD signal. */
 	pg_queue_signal(SIGCHLD);
 }
+
+/*
+ * Queue a waiter to signal when this child dies. The wait will be handled
+ * automatically by an operating system thread pool.  The memory will be
+ * freed by a later call to waitpid().
+ */
+void
+pgwin32_register_deadchild_callback(HANDLE procHandle, DWORD procId)
+{
+	win32_deadchild_waitinfo *childinfo;
+
+	childinfo = palloc(sizeof(win32_deadchild_waitinfo));
+	childinfo->procHandle = procHandle;
+	childinfo->procId = procId;
+
+	if (!RegisterWaitForSingleObject(&childinfo->waitHandle,
+									 procHandle,
+									 pgwin32_deadchild_callback,
+									 childinfo,
+									 INFINITE,
+									 WT_EXECUTEONLYONCE | WT_EXECUTEINWAITTHREAD))
+		ereport(FATAL,
+				(errmsg_internal("could not register process for wait: error code %lu",
+								 GetLastError())));
+}
+
 #endif							/* WIN32 */
 
 /*
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index 0e7de26bc28..6b38acf922a 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -27,6 +27,7 @@
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
 #include "pgstat.h"
+#include "postmaster/auxprocess.h"
 #include "postmaster/interrupt.h"
 #include "postmaster/startup.h"
 #include "storage/ipc.h"
@@ -242,8 +243,14 @@ StartupProcExit(int code, Datum arg)
  * ----------------------------------
  */
 void
-StartupProcessMain(void)
+StartupProcessMain(char *startup_data, size_t startup_data_len)
 {
+	Assert(startup_data_len == 0);
+
+	MyAuxProcType = StartupProcess;
+	MyBackendType = B_STARTUP;
+	AuxiliaryProcessInit();
+
 	/* Arrange to clean up at startup process exit */
 	on_shmem_exit(StartupProcExit, 0);
 
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index 96dd03d9e06..c923843532f 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -39,7 +39,6 @@
 #include "pgstat.h"
 #include "pgtime.h"
 #include "port/pg_bitutils.h"
-#include "postmaster/fork_process.h"
 #include "postmaster/interrupt.h"
 #include "postmaster/postmaster.h"
 #include "postmaster/syslogger.h"
@@ -50,6 +49,7 @@
 #include "storage/pg_shmem.h"
 #include "tcop/tcopprot.h"
 #include "utils/guc.h"
+#include "utils/memutils.h"
 #include "utils/ps_status.h"
 #include "utils/timestamp.h"
 
@@ -134,10 +134,7 @@ static volatile sig_atomic_t rotation_requested = false;
 #ifdef EXEC_BACKEND
 static int	syslogger_fdget(FILE *file);
 static FILE *syslogger_fdopen(int fd);
-static pid_t syslogger_forkexec(void);
-static void syslogger_parseArgs(int argc, char *argv[]);
 #endif
-NON_EXEC_STATIC void SysLoggerMain(int argc, char *argv[]) pg_attribute_noreturn();
 static void process_pipe_input(char *logbuffer, int *bytes_in_logbuffer);
 static void flush_pipe_input(char *logbuffer, int *bytes_in_logbuffer);
 static FILE *logfile_open(const char *filename, const char *mode,
@@ -156,13 +153,19 @@ static void set_next_rotation_time(void);
 static void sigUsr1Handler(SIGNAL_ARGS);
 static void update_metainfo_datafile(void);
 
+typedef struct
+{
+	int			syslogFile;
+	int			csvlogFile;
+	int			jsonlogFile;
+} syslogger_startup_data;
 
 /*
  * Main entry point for syslogger process
  * argc/argv parameters are valid only in EXEC_BACKEND case.
  */
-NON_EXEC_STATIC void
-SysLoggerMain(int argc, char *argv[])
+void
+SysLoggerMain(char *startup_data, size_t startup_data_len)
 {
 #ifndef WIN32
 	char		logbuffer[READ_BUF_SIZE];
@@ -174,11 +177,34 @@ SysLoggerMain(int argc, char *argv[])
 	pg_time_t	now;
 	WaitEventSet *wes;
 
-	now = MyStartTime;
+	/* Release postmaster's working memory context */
+	if (PostmasterContext)
+	{
+		MemoryContextDelete(PostmasterContext);
+		PostmasterContext = NULL;
+	}
 
+	/*
+	 * Re-open the error output files that were opened by SysLogger_Start().
+	 *
+	 * We expect this will always succeed, which is too optimistic, but if it
+	 * fails there's not a lot we can do to report the problem anyway.  As
+	 * coded, we'll just crash on a null pointer dereference after failure...
+	 */
 #ifdef EXEC_BACKEND
-	syslogger_parseArgs(argc, argv);
-#endif							/* EXEC_BACKEND */
+	{
+		syslogger_startup_data *info = (syslogger_startup_data *) startup_data;
+
+		Assert(startup_data_len == sizeof(*info));
+		syslogFile = syslogger_fdopen(info->syslogFile);
+		csvlogFile = syslogger_fdopen(info->csvlogFile);
+		jsonlogFile = syslogger_fdopen(info->jsonlogFile);
+	}
+#else
+	Assert(startup_data_len == 0);
+#endif
+
+	now = MyStartTime;
 
 	MyBackendType = B_LOGGER;
 	init_ps_display(NULL);
@@ -568,6 +594,9 @@ SysLogger_Start(void)
 {
 	pid_t		sysloggerPid;
 	char	   *filename;
+#ifdef EXEC_BACKEND
+	syslogger_startup_data startup_data;
+#endif							/* EXEC_BACKEND */
 
 	if (!Logging_collector)
 		return 0;
@@ -667,112 +696,95 @@ SysLogger_Start(void)
 	}
 
 #ifdef EXEC_BACKEND
-	switch ((sysloggerPid = syslogger_forkexec()))
+	startup_data.syslogFile = syslogger_fdget(syslogFile);
+	startup_data.csvlogFile = syslogger_fdget(csvlogFile);
+	startup_data.jsonlogFile = syslogger_fdget(jsonlogFile);
+	sysloggerPid = postmaster_child_launch(PMC_SYSLOGGER, (char *) &startup_data, sizeof(startup_data), NULL);
 #else
-	switch ((sysloggerPid = fork_process()))
-#endif
-	{
-		case -1:
-			ereport(LOG,
-					(errmsg("could not fork system logger: %m")));
-			return 0;
-
-#ifndef EXEC_BACKEND
-		case 0:
-			/* in postmaster child ... */
-			InitPostmasterChild();
-
-			/* Close the postmaster's sockets */
-			ClosePostmasterPorts(true);
-
-			/* Drop our connection to postmaster's shared memory, as well */
-			dsm_detach_all();
-			PGSharedMemoryDetach();
+	sysloggerPid = postmaster_child_launch(PMC_SYSLOGGER, NULL, 0, NULL);
+#endif							/* EXEC_BACKEND */
 
-			/* do the work */
-			SysLoggerMain(0, NULL);
-			break;
-#endif
+	if (sysloggerPid == -1)
+	{
+		ereport(LOG,
+				(errmsg("could not fork system logger: %m")));
+		return 0;
+	}
 
-		default:
-			/* success, in postmaster */
+	/* success, in postmaster */
 
-			/* now we redirect stderr, if not done already */
-			if (!redirection_done)
-			{
+	/* now we redirect stderr, if not done already */
+	if (!redirection_done)
+	{
 #ifdef WIN32
-				int			fd;
+		int			fd;
 #endif
 
-				/*
-				 * Leave a breadcrumb trail when redirecting, in case the user
-				 * forgets that redirection is active and looks only at the
-				 * original stderr target file.
-				 */
-				ereport(LOG,
-						(errmsg("redirecting log output to logging collector process"),
-						 errhint("Future log output will appear in directory \"%s\".",
-								 Log_directory)));
+		/*
+		 * Leave a breadcrumb trail when redirecting, in case the user forgets
+		 * that redirection is active and looks only at the original stderr
+		 * target file.
+		 */
+		ereport(LOG,
+				(errmsg("redirecting log output to logging collector process"),
+				 errhint("Future log output will appear in directory \"%s\".",
+						 Log_directory)));
 
 #ifndef WIN32
-				fflush(stdout);
-				if (dup2(syslogPipe[1], STDOUT_FILENO) < 0)
-					ereport(FATAL,
-							(errcode_for_file_access(),
-							 errmsg("could not redirect stdout: %m")));
-				fflush(stderr);
-				if (dup2(syslogPipe[1], STDERR_FILENO) < 0)
-					ereport(FATAL,
-							(errcode_for_file_access(),
-							 errmsg("could not redirect stderr: %m")));
-				/* Now we are done with the write end of the pipe. */
-				close(syslogPipe[1]);
-				syslogPipe[1] = -1;
+		fflush(stdout);
+		if (dup2(syslogPipe[1], STDOUT_FILENO) < 0)
+			ereport(FATAL,
+					(errcode_for_file_access(),
+					 errmsg("could not redirect stdout: %m")));
+		fflush(stderr);
+		if (dup2(syslogPipe[1], STDERR_FILENO) < 0)
+			ereport(FATAL,
+					(errcode_for_file_access(),
+					 errmsg("could not redirect stderr: %m")));
+		/* Now we are done with the write end of the pipe. */
+		close(syslogPipe[1]);
+		syslogPipe[1] = -1;
 #else
 
-				/*
-				 * open the pipe in binary mode and make sure stderr is binary
-				 * after it's been dup'ed into, to avoid disturbing the pipe
-				 * chunking protocol.
-				 */
-				fflush(stderr);
-				fd = _open_osfhandle((intptr_t) syslogPipe[1],
-									 _O_APPEND | _O_BINARY);
-				if (dup2(fd, STDERR_FILENO) < 0)
-					ereport(FATAL,
-							(errcode_for_file_access(),
-							 errmsg("could not redirect stderr: %m")));
-				close(fd);
-				_setmode(STDERR_FILENO, _O_BINARY);
+		/*
+		 * open the pipe in binary mode and make sure stderr is binary after
+		 * it's been dup'ed into, to avoid disturbing the pipe chunking
+		 * protocol.
+		 */
+		fflush(stderr);
+		fd = _open_osfhandle((intptr_t) syslogPipe[1],
+							 _O_APPEND | _O_BINARY);
+		if (dup2(fd, STDERR_FILENO) < 0)
+			ereport(FATAL,
+					(errcode_for_file_access(),
+					 errmsg("could not redirect stderr: %m")));
+		close(fd);
+		_setmode(STDERR_FILENO, _O_BINARY);
 
-				/*
-				 * Now we are done with the write end of the pipe.
-				 * CloseHandle() must not be called because the preceding
-				 * close() closes the underlying handle.
-				 */
-				syslogPipe[1] = 0;
+		/*
+		 * Now we are done with the write end of the pipe.  CloseHandle() must
+		 * not be called because the preceding close() closes the underlying
+		 * handle.
+		 */
+		syslogPipe[1] = 0;
 #endif
-				redirection_done = true;
-			}
-
-			/* postmaster will never write the file(s); close 'em */
-			fclose(syslogFile);
-			syslogFile = NULL;
-			if (csvlogFile != NULL)
-			{
-				fclose(csvlogFile);
-				csvlogFile = NULL;
-			}
-			if (jsonlogFile != NULL)
-			{
-				fclose(jsonlogFile);
-				jsonlogFile = NULL;
-			}
-			return (int) sysloggerPid;
+		redirection_done = true;
 	}
 
-	/* we should never reach here */
-	return 0;
+	/* postmaster will never write the file(s); close 'em */
+	fclose(syslogFile);
+	syslogFile = NULL;
+	if (csvlogFile != NULL)
+	{
+		fclose(csvlogFile);
+		csvlogFile = NULL;
+	}
+	if (jsonlogFile != NULL)
+	{
+		fclose(jsonlogFile);
+		jsonlogFile = NULL;
+	}
+	return (int) sysloggerPid;
 }
 
 
@@ -831,69 +843,6 @@ syslogger_fdopen(int fd)
 
 	return file;
 }
-
-/*
- * syslogger_forkexec() -
- *
- * Format up the arglist for, then fork and exec, a syslogger process
- */
-static pid_t
-syslogger_forkexec(void)
-{
-	char	   *av[10];
-	int			ac = 0;
-	char		filenobuf[32];
-	char		csvfilenobuf[32];
-	char		jsonfilenobuf[32];
-
-	av[ac++] = "postgres";
-	av[ac++] = "--forklog";
-	av[ac++] = NULL;			/* filled in by postmaster_forkexec */
-
-	/* static variables (those not passed by write_backend_variables) */
-	snprintf(filenobuf, sizeof(filenobuf), "%d",
-			 syslogger_fdget(syslogFile));
-	av[ac++] = filenobuf;
-	snprintf(csvfilenobuf, sizeof(csvfilenobuf), "%d",
-			 syslogger_fdget(csvlogFile));
-	av[ac++] = csvfilenobuf;
-	snprintf(jsonfilenobuf, sizeof(jsonfilenobuf), "%d",
-			 syslogger_fdget(jsonlogFile));
-	av[ac++] = jsonfilenobuf;
-
-	av[ac] = NULL;
-	Assert(ac < lengthof(av));
-
-	return postmaster_forkexec(ac, av);
-}
-
-/*
- * syslogger_parseArgs() -
- *
- * Extract data from the arglist for exec'ed syslogger process
- */
-static void
-syslogger_parseArgs(int argc, char *argv[])
-{
-	int			fd;
-
-	Assert(argc == 6);
-	argv += 3;
-
-	/*
-	 * Re-open the error output files that were opened by SysLogger_Start().
-	 *
-	 * We expect this will always succeed, which is too optimistic, but if it
-	 * fails there's not a lot we can do to report the problem anyway.  As
-	 * coded, we'll just crash on a null pointer dereference after failure...
-	 */
-	fd = atoi(*argv++);
-	syslogFile = syslogger_fdopen(fd);
-	fd = atoi(*argv++);
-	csvlogFile = syslogger_fdopen(fd);
-	fd = atoi(*argv++);
-	jsonlogFile = syslogger_fdopen(fd);
-}
 #endif							/* EXEC_BACKEND */
 
 
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 48bc92205b5..89950350aee 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -48,6 +48,7 @@
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
 #include "pgstat.h"
+#include "postmaster/auxprocess.h"
 #include "postmaster/interrupt.h"
 #include "postmaster/walwriter.h"
 #include "storage/bufmgr.h"
@@ -88,13 +89,19 @@ static void HandleWalWriterInterrupts(void);
  * basic execution environment, but not enabled signals yet.
  */
 void
-WalWriterMain(void)
+WalWriterMain(char *startup_data, size_t startup_data_len)
 {
 	sigjmp_buf	local_sigjmp_buf;
 	MemoryContext walwriter_context;
 	int			left_till_hibernate;
 	bool		hibernating;
 
+	Assert(startup_data_len == 0);
+
+	MyAuxProcType = WalWriterProcess;
+	MyBackendType = B_WAL_WRITER;
+	AuxiliaryProcessInit();
+
 	/*
 	 * Properly accept or ignore signals the postmaster might send us
 	 *
diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c
index 501910b4454..a4098c23b2b 100644
--- a/src/backend/replication/logical/launcher.c
+++ b/src/backend/replication/logical/launcher.c
@@ -30,7 +30,6 @@
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "postmaster/bgworker.h"
-#include "postmaster/fork_process.h"
 #include "postmaster/interrupt.h"
 #include "postmaster/postmaster.h"
 #include "replication/logicallauncher.h"
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 26ded928a71..9b01132704b 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -65,6 +65,7 @@
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
 #include "pgstat.h"
+#include "postmaster/auxprocess.h"
 #include "postmaster/interrupt.h"
 #include "replication/walreceiver.h"
 #include "replication/walsender.h"
@@ -183,7 +184,7 @@ ProcessWalRcvInterrupts(void)
 
 /* Main entry point for walreceiver process */
 void
-WalReceiverMain(void)
+WalReceiverMain(char *startup_data, size_t startup_data_len)
 {
 	char		conninfo[MAXCONNINFO];
 	char	   *tmp_conninfo;
@@ -199,6 +200,12 @@ WalReceiverMain(void)
 	char	   *sender_host = NULL;
 	int			sender_port = 0;
 
+	Assert(startup_data_len == 0);
+
+	MyAuxProcType = WalReceiverProcess;
+	MyBackendType = B_WAL_RECEIVER;
+	AuxiliaryProcessInit();
+
 	/*
 	 * WalRcv should be set up already (if we are a backend, we inherit this
 	 * by fork() or EXEC_BACKEND mechanism from the postmaster).
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 5465fa19646..d65d461340c 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -144,6 +144,8 @@ InitShmemAllocation(void)
 	/*
 	 * Initialize ShmemVariableCache for transaction manager. (This doesn't
 	 * really belong here, but not worth moving.)
+	 *
+	 * XXX: we really should move this
 	 */
 	ShmemVariableCache = (VariableCache)
 		ShmemAlloc(sizeof(*ShmemVariableCache));
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 60bc1217fb4..b6c3055027e 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -44,6 +44,7 @@ volatile uint32 CritSectionCount = 0;
 int			MyProcPid;
 pg_time_t	MyStartTime;
 TimestampTz MyStartTimestamp;
+struct ClientSocket *MyClientSocket;
 struct Port *MyProcPort;
 int32		MyCancelKey;
 int			MyPMChildSlot;
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index cfc5afaa6fd..0cec9e54861 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -85,139 +85,6 @@ bool		IgnoreSystemIndexes = false;
  * ----------------------------------------------------------------
  */
 
-/*
- * Initialize the basic environment for a postmaster child
- *
- * Should be called as early as possible after the child's startup. However,
- * on EXEC_BACKEND builds it does need to be after read_backend_variables().
- */
-void
-InitPostmasterChild(void)
-{
-	IsUnderPostmaster = true;	/* we are a postmaster subprocess now */
-
-	/*
-	 * Start our win32 signal implementation. This has to be done after we
-	 * read the backend variables, because we need to pick up the signal pipe
-	 * from the parent process.
-	 */
-#ifdef WIN32
-	pgwin32_signal_initialize();
-#endif
-
-	/*
-	 * Set reference point for stack-depth checking.  This might seem
-	 * redundant in !EXEC_BACKEND builds; but it's not because the postmaster
-	 * launches its children from signal handlers, so we might be running on
-	 * an alternative stack.
-	 */
-	(void) set_stack_base();
-
-	InitProcessGlobals();
-
-	/*
-	 * make sure stderr is in binary mode before anything can possibly be
-	 * written to it, in case it's actually the syslogger pipe, so the pipe
-	 * chunking protocol isn't disturbed. Non-logpipe data gets translated on
-	 * redirection (e.g. via pg_ctl -l) anyway.
-	 */
-#ifdef WIN32
-	_setmode(fileno(stderr), _O_BINARY);
-#endif
-
-	/* We don't want the postmaster's proc_exit() handlers */
-	on_exit_reset();
-
-	/* In EXEC_BACKEND case we will not have inherited BlockSig etc values */
-#ifdef EXEC_BACKEND
-	pqinitmask();
-#endif
-
-	/* Initialize process-local latch support */
-	InitializeLatchSupport();
-	InitProcessLocalLatch();
-	InitializeLatchWaitSet();
-
-	/*
-	 * If possible, make this process a group leader, so that the postmaster
-	 * can signal any child processes too. Not all processes will have
-	 * children, but for consistency we make all postmaster child processes do
-	 * this.
-	 */
-#ifdef HAVE_SETSID
-	if (setsid() < 0)
-		elog(FATAL, "setsid() failed: %m");
-#endif
-
-	/*
-	 * Every postmaster child process is expected to respond promptly to
-	 * SIGQUIT at all times.  Therefore we centrally remove SIGQUIT from
-	 * BlockSig and install a suitable signal handler.  (Client-facing
-	 * processes may choose to replace this default choice of handler with
-	 * quickdie().)  All other blockable signals remain blocked for now.
-	 */
-	pqsignal(SIGQUIT, SignalHandlerForCrashExit);
-
-	sigdelset(&BlockSig, SIGQUIT);
-	sigprocmask(SIG_SETMASK, &BlockSig, NULL);
-
-	/* Request a signal if the postmaster dies, if possible. */
-	PostmasterDeathSignalInit();
-
-	/* Don't give the pipe to subprograms that we execute. */
-#ifndef WIN32
-	if (fcntl(postmaster_alive_fds[POSTMASTER_FD_WATCH], F_SETFD, FD_CLOEXEC) < 0)
-		ereport(FATAL,
-				(errcode_for_socket_access(),
-				 errmsg_internal("could not set postmaster death monitoring pipe to FD_CLOEXEC mode: %m")));
-#endif
-}
-
-/*
- * Initialize the basic environment for a standalone process.
- *
- * argv0 has to be suitable to find the program's executable.
- */
-void
-InitStandaloneProcess(const char *argv0)
-{
-	Assert(!IsPostmasterEnvironment);
-
-	MyBackendType = B_STANDALONE_BACKEND;
-
-	/*
-	 * Start our win32 signal implementation
-	 */
-#ifdef WIN32
-	pgwin32_signal_initialize();
-#endif
-
-	InitProcessGlobals();
-
-	/* Initialize process-local latch support */
-	InitializeLatchSupport();
-	InitProcessLocalLatch();
-	InitializeLatchWaitSet();
-
-	/*
-	 * For consistency with InitPostmasterChild, initialize signal mask here.
-	 * But we don't unblock SIGQUIT or provide a default handler for it.
-	 */
-	pqinitmask();
-	sigprocmask(SIG_SETMASK, &BlockSig, NULL);
-
-	/* Compute paths, no postmaster to inherit from */
-	if (my_exec_path[0] == '\0')
-	{
-		if (find_my_exec(argv0, my_exec_path) < 0)
-			elog(FATAL, "%s: could not locate my own executable path",
-				 argv0);
-	}
-
-	if (pkglib_path[0] == '\0')
-		get_pkglib_path(my_exec_path, pkglib_path);
-}
-
 void
 SwitchToSharedLatch(void)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 9b6d8fc5571..959feeba025 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -132,10 +132,11 @@ typedef struct ClientConnectionInfo
 typedef struct Port
 {
 	pgsocket	sock;			/* File descriptor */
-	bool		noblock;		/* is the socket in non-blocking mode? */
-	ProtocolVersion proto;		/* FE/BE protocol version */
 	SockAddr	laddr;			/* local addr (postmaster) */
 	SockAddr	raddr;			/* remote addr (client) */
+
+	bool		noblock;		/* is the socket in non-blocking mode? */
+	ProtocolVersion proto;		/* FE/BE protocol version */
 	char	   *remote_host;	/* name (or ip addr) of remote host */
 	char	   *remote_hostname;	/* name (not ip addr) of remote host, if
 									 * available */
@@ -218,11 +219,13 @@ typedef struct Port
  * ClientSocket holds a socket for an accepted connection, along with the
  * information about the endpoints.
  */
-typedef struct ClientSocket {
+struct ClientSocket
+{
 	pgsocket	sock;			/* File descriptor */
 	SockAddr	laddr;			/* local addr (postmaster) */
 	SockAddr	raddr;			/* remote addr (client) */
-} ClientSocket;
+};
+typedef struct ClientSocket ClientSocket;
 
 #ifdef USE_SSL
 /*
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index f0cc651435c..a179cb39c40 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -319,7 +319,6 @@ extern int	trace_recovery(int trace_level);
 extern PGDLLIMPORT char *DatabasePath;
 
 /* now in utils/init/miscinit.c */
-extern void InitPostmasterChild(void);
 extern void InitStandaloneProcess(const char *argv0);
 extern void InitProcessLocalLatch(void);
 extern void SwitchToSharedLatch(void);
diff --git a/src/include/postmaster/autovacuum.h b/src/include/postmaster/autovacuum.h
index b553e858ad4..ee12e477f11 100644
--- a/src/include/postmaster/autovacuum.h
+++ b/src/include/postmaster/autovacuum.h
@@ -55,20 +55,14 @@ extern bool IsAutoVacuumWorkerProcess(void);
 #define IsAnyAutoVacuumProcess() \
 	(IsAutoVacuumLauncherProcess() || IsAutoVacuumWorkerProcess())
 
-/* Functions to start autovacuum process, called from postmaster */
+/* called from postmaster at server startup */
 extern void autovac_init(void);
-extern int	StartAutoVacLauncher(void);
-extern int	StartAutoVacWorker(void);
 
 /* called from postmaster when a worker could not be forked */
 extern void AutoVacWorkerFailed(void);
 
-#ifdef EXEC_BACKEND
-extern void AutoVacLauncherMain(int argc, char *argv[]) pg_attribute_noreturn();
-extern void AutoVacWorkerMain(int argc, char *argv[]) pg_attribute_noreturn();
-extern void AutovacuumWorkerIAm(void);
-extern void AutovacuumLauncherIAm(void);
-#endif
+extern void AutoVacLauncherMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
+extern void AutoVacWorkerMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
 
 extern bool AutoVacuumRequestWork(AutoVacuumWorkItemType type,
 								  Oid relationId, BlockNumber blkno);
diff --git a/src/include/postmaster/auxprocess.h b/src/include/postmaster/auxprocess.h
index 5c2d6527ff6..75394ca0155 100644
--- a/src/include/postmaster/auxprocess.h
+++ b/src/include/postmaster/auxprocess.h
@@ -13,8 +13,6 @@
 #ifndef AUXPROCESS_H
 #define AUXPROCESS_H
 
-#include "miscadmin.h"
-
-extern void AuxiliaryProcessMain(AuxProcType auxtype) pg_attribute_noreturn();
+extern void AuxiliaryProcessInit(void);
 
 #endif							/* AUXPROCESS_H */
diff --git a/src/include/postmaster/bgworker_internals.h b/src/include/postmaster/bgworker_internals.h
index 323f1e07291..4055d2f5626 100644
--- a/src/include/postmaster/bgworker_internals.h
+++ b/src/include/postmaster/bgworker_internals.h
@@ -55,6 +55,6 @@ extern void ForgetUnstartedBackgroundWorkers(void);
 extern void ResetBackgroundWorkerCrashTimes(void);
 
 /* Entry point for background worker processes */
-extern void BackgroundWorkerMain(void) pg_attribute_noreturn();
+extern void BackgroundWorkerMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
 
 #endif							/* BGWORKER_INTERNALS_H */
diff --git a/src/include/postmaster/bgwriter.h b/src/include/postmaster/bgwriter.h
index a66722873f4..ee54fc401ef 100644
--- a/src/include/postmaster/bgwriter.h
+++ b/src/include/postmaster/bgwriter.h
@@ -27,8 +27,8 @@ extern PGDLLIMPORT int CheckPointTimeout;
 extern PGDLLIMPORT int CheckPointWarning;
 extern PGDLLIMPORT double CheckPointCompletionTarget;
 
-extern void BackgroundWriterMain(void) pg_attribute_noreturn();
-extern void CheckpointerMain(void) pg_attribute_noreturn();
+extern void BackgroundWriterMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
+extern void CheckpointerMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
 
 extern void RequestCheckpoint(int flags);
 extern void CheckpointWriteDelay(int flags, double progress);
diff --git a/src/include/postmaster/fork_process.h b/src/include/postmaster/fork_process.h
deleted file mode 100644
index 12decc8133b..00000000000
--- a/src/include/postmaster/fork_process.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * fork_process.h
- *	  Exports from postmaster/fork_process.c.
- *
- * Copyright (c) 1996-2023, PostgreSQL Global Development Group
- *
- * src/include/postmaster/fork_process.h
- *
- *-------------------------------------------------------------------------
- */
-#ifndef FORK_PROCESS_H
-#define FORK_PROCESS_H
-
-extern pid_t fork_process(void);
-
-#endif							/* FORK_PROCESS_H */
diff --git a/src/include/postmaster/pgarch.h b/src/include/postmaster/pgarch.h
index 3bd4fac71e5..577fc14e1d0 100644
--- a/src/include/postmaster/pgarch.h
+++ b/src/include/postmaster/pgarch.h
@@ -29,7 +29,7 @@
 extern Size PgArchShmemSize(void);
 extern void PgArchShmemInit(void);
 extern bool PgArchCanRestart(void);
-extern void PgArchiverMain(void) pg_attribute_noreturn();
+extern void PgArchiverMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
 extern void PgArchWakeup(void);
 extern void PgArchForceDirScan(void);
 
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index 3b3889c58c0..1dbb5c991f5 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -58,14 +58,52 @@ extern int	MaxLivePostmasterChildren(void);
 
 extern bool PostmasterMarkPIDForWorkerNotify(int);
 
-#ifdef EXEC_BACKEND
-extern pid_t postmaster_forkexec(int argc, char *argv[]);
-extern void SubPostmasterMain(int argc, char *argv[]) pg_attribute_noreturn();
+extern void BackendMain(char *startup_data, size_t startup_data_len);
 
+#ifdef EXEC_BACKEND
 extern Size ShmemBackendArraySize(void);
 extern void ShmemBackendArrayAllocation(void);
 #endif
 
+/* in launch_backend.c */
+
+/* this better match the list in launch_backend.c */
+typedef enum PostmasterChildType
+{
+	PMC_BACKEND = 0,
+	PMC_AV_LAUNCHER,
+	PMC_AV_WORKER,
+	PMC_BGWORKER,
+	PMC_SYSLOGGER,
+
+	/*
+	 * so-called "aux processes".  These access shared memory, but are not
+	 * attached to any particular database.  Only one of each of these can be
+	 * running at a time.
+	 */
+	PMC_STARTUP,
+	PMC_BGWRITER,
+	PMC_ARCHIVER,
+	PMC_CHECKPOINTER,
+	PMC_WAL_WRITER,
+	PMC_WAL_RECEIVER,
+} PostmasterChildType;
+
+/* defined in libpq-be.h */
+extern struct ClientSocket *MyClientSocket;
+
+extern pid_t postmaster_child_launch(PostmasterChildType child_type, char *startup_data, size_t startup_data_len, struct ClientSocket *sock);
+
+#ifdef EXEC_BACKEND
+extern void SubPostmasterMain(int argc, char *argv[]) pg_attribute_noreturn();
+#endif
+
+#ifdef WIN32
+extern void pgwin32_register_deadchild_callback(HANDLE procHandle, DWORD procId);
+#endif
+
+const char *PostmasterChildName(PostmasterChildType child_type);
+
 /*
  * Note: MAX_BACKENDS is limited to 2^18-1 because that's the width reserved
  * for buffer references in buf_internals.h.  This limitation could be lifted
diff --git a/src/include/postmaster/startup.h b/src/include/postmaster/startup.h
index 6a2e4c4526b..ec885063aab 100644
--- a/src/include/postmaster/startup.h
+++ b/src/include/postmaster/startup.h
@@ -26,7 +26,7 @@
 extern PGDLLIMPORT int log_startup_progress_interval;
 
 extern void HandleStartupProcInterrupts(void);
-extern void StartupProcessMain(void) pg_attribute_noreturn();
+extern void StartupProcessMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
 extern void PreRestoreCommand(void);
 extern void PostRestoreCommand(void);
 extern bool IsPromoteSignaled(void);
diff --git a/src/include/postmaster/syslogger.h b/src/include/postmaster/syslogger.h
index 34da778f1ef..7dc41b30e7c 100644
--- a/src/include/postmaster/syslogger.h
+++ b/src/include/postmaster/syslogger.h
@@ -86,9 +86,7 @@ extern int	SysLogger_Start(void);
 
 extern void write_syslogger_file(const char *buffer, int count, int destination);
 
-#ifdef EXEC_BACKEND
-extern void SysLoggerMain(int argc, char *argv[]) pg_attribute_noreturn();
-#endif
+extern void SysLoggerMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
 
 extern bool CheckLogrotateSignal(void);
 extern void RemoveLogrotateSignalFiles(void);
diff --git a/src/include/postmaster/walwriter.h b/src/include/postmaster/walwriter.h
index 6eba7ad79cf..99b7cc07fb2 100644
--- a/src/include/postmaster/walwriter.h
+++ b/src/include/postmaster/walwriter.h
@@ -18,6 +18,6 @@
 extern PGDLLIMPORT int WalWriterDelay;
 extern PGDLLIMPORT int WalWriterFlushAfter;
 
-extern void WalWriterMain(void) pg_attribute_noreturn();
+extern void WalWriterMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
 
 #endif							/* _WALWRITER_H */
diff --git a/src/include/replication/walreceiver.h b/src/include/replication/walreceiver.h
index 04b439dc503..4b76062255b 100644
--- a/src/include/replication/walreceiver.h
+++ b/src/include/replication/walreceiver.h
@@ -457,7 +457,7 @@ walrcv_clear_result(WalRcvExecResult *walres)
 }
 
 /* prototypes for functions in walreceiver.c */
-extern void WalReceiverMain(void) pg_attribute_noreturn();
+extern void WalReceiverMain(char *startup_data, size_t startup_data_len) pg_attribute_noreturn();
 extern void ProcessWalRcvInterrupts(void);
 extern void WalRcvForceReply(void);
 
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 00649608e46..cfc3f259c95 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2121,6 +2121,7 @@ PortalStrategy
 PostParseColumnRefHook
 PostgresPollingStatusType
 PostingItem
+PostmasterChildType
 PreParseColumnRefHook
 PredClass
 PredIterInfo
@@ -3793,6 +3794,7 @@ substitute_actual_parameters_context
 substitute_actual_srf_parameters_context
 substitute_phv_relids_context
 symbol
+syslogger_startup_data
 tablespaceinfo
 teSection
 temp_tablespaces_extra
-- 
2.39.2

