On Sat, Feb 29, 2020 at 9:51 PM Mike Palmiotto
<mike.palmio...@crunchydata.com> wrote:
>
> On Sat, Nov 30, 2019 at 9:15 PM Michael Paquier <mich...@paquier.xyz> wrote:
> >
> > On Thu, Oct 03, 2019 at 11:39:37AM -0700, Andres Freund wrote:
> > > Color me unconvinced.
> >
> > The latest comments of the thread have not been addressed yet. so I am
> > marking the patch as returned with feedback.  If you think that's not
> > correct, please feel free to update the status of the patch.  If you
> > do so, please provide at the same time a rebased version of the patch,
> > as it is failing to apply on HEAD.
>
> I've finally had some time to update the patchset with an attempt at
> addressing Andres' concerns. Note that the current spin has a bug
> which does not allow syslogger to run properly. Additionally, it is
> squashed into one patch again.  I intend to split the patch out into
> separate functional patches and fix the syslogger issue, but wanted to
> get this on the ticket for the open CommitFest before it closes. I'll
> go ahead and re-add it and will be pushing updates as I have them.

Okay, here is an updated and rebased patch that passes all regression
tests with and without EXEC_BACKEND. This also treats more of the
issues raised by Andres.

I still need to do the following:
- split giant patch out into separate functional commits
- add translated process descriptions
- fix some variable names here and there (notably the PgSubprocess
struct ".progname" member -- that's a misnomer.
- address some valgrind findings

I should be able to split this out into smaller commits sometime today
and will continue iterating to scratch the other items off the list.

Thanks,
-- 
Mike Palmiotto
https://crunchydata.com
From a497a8905fa7f25d41f831e83174548315fa658f Mon Sep 17 00:00:00 2001
From: Mike Palmiotto <mike.palmio...@crunchydata.com>
Date: Tue, 19 Feb 2019 15:29:33 +0000
Subject: [PATCH 2/2] Add a hook to allow extensions to set worker metadata

This patch implements a hook that allows extensions to modify a worker
process' metadata in backends.

Additional work done by Yuli Khodorkovskiy <y...@crunchydata.com>
Original discussion here: https://www.postgresql.org/message-id/flat/CAMN686FE0OdZKp9YPO%3DhtC6LnA6aW4r-%2Bjq%3D3Q5RAoFQgW8EtA%40mail.gmail.com
---
 src/backend/postmaster/fork_process.c | 11 +++++++++++
 src/include/postmaster/fork_process.h |  4 ++++
 2 files changed, 15 insertions(+)

diff --git a/src/backend/postmaster/fork_process.c b/src/backend/postmaster/fork_process.c
index def3cee37e..e392f5207e 100644
--- a/src/backend/postmaster/fork_process.c
+++ b/src/backend/postmaster/fork_process.c
@@ -22,6 +22,14 @@
 
 #include "postmaster/fork_process.h"
 
+/*
+ * This hook allows plugins to get control of the child process following
+ * a successful fork_process(). It can be used to perform some action in
+ * extensions to set metadata in backends (including special backends) upon
+ * setting of the session user.
+ */
+ForkProcess_post_hook_type ForkProcess_post_hook = NULL;
+
 #ifndef WIN32
 /*
  * Wrapper for fork(). Return values are the same as those for fork():
@@ -114,6 +122,9 @@ fork_process(void)
 #ifdef USE_OPENSSL
 		RAND_cleanup();
 #endif
+
+		if (ForkProcess_post_hook)
+			(*ForkProcess_post_hook) ();
 	}
 
 	return result;
diff --git a/src/include/postmaster/fork_process.h b/src/include/postmaster/fork_process.h
index a42f859a08..b4d1560b72 100644
--- a/src/include/postmaster/fork_process.h
+++ b/src/include/postmaster/fork_process.h
@@ -20,4 +20,8 @@ extern pid_t fork_process(void);
 extern pid_t postmaster_forkexec(int argc, char *argvp[]);
 #endif
 
+/* Hook for plugins to get control after a successful fork_process() */
+typedef void (*ForkProcess_post_hook_type) ();
+extern PGDLLIMPORT ForkProcess_post_hook_type ForkProcess_post_hook;
+
 #endif							/* FORK_PROCESS_H */
-- 
2.21.0

From 682aaa548249be06a6693839f1877946de8ca927 Mon Sep 17 00:00:00 2001
From: Mike Palmiotto <mike.palmio...@crunchydata.com>
Date: Fri, 27 Sep 2019 12:28:19 -0400
Subject: [PATCH 1/2] Introduce subprocess infrastructure

This is an initial attempt at coalescing all of the postmaster
subprocess startups into one central framework. The patchset introduces
a MySubprocess global, which contains an PgSubprocess entry from the
process_types array. Each entry has defined entrypoints, cleanup
functions, prep functions, and a few other control-oriented variables.

This is a rework of https://commitfest.postgresql.org/25/2259/, which
attempts to address the feedback from Andres Freund.
---
 src/backend/bootstrap/bootstrap.c           | 103 +--
 src/backend/main/main.c                     |   1 +
 src/backend/postmaster/Makefile             |   1 +
 src/backend/postmaster/autovacuum.c         | 170 +---
 src/backend/postmaster/bgworker.c           | 157 +++-
 src/backend/postmaster/bgwriter.c           |   2 +-
 src/backend/postmaster/checkpointer.c       |   2 +-
 src/backend/postmaster/pgarch.c             |  83 +-
 src/backend/postmaster/pgstat.c             | 195 +----
 src/backend/postmaster/postmaster.c         | 890 +++++++-------------
 src/backend/postmaster/startup.c            |   8 +-
 src/backend/postmaster/subprocess.c         | 208 +++++
 src/backend/postmaster/syslogger.c          | 277 +++---
 src/backend/postmaster/walwriter.c          |   2 +-
 src/backend/replication/walreceiver.c       |   2 +-
 src/backend/utils/adt/pgstatfuncs.c         |   9 +-
 src/backend/utils/init/globals.c            |   3 +-
 src/backend/utils/init/postinit.c           |   2 +-
 src/include/bootstrap/bootstrap.h           |   4 +-
 src/include/miscadmin.h                     |   1 +
 src/include/pgstat.h                        |   8 +-
 src/include/postmaster/autovacuum.h         |  13 +-
 src/include/postmaster/bgworker_internals.h |   7 +-
 src/include/postmaster/bgwriter.h           |   4 +-
 src/include/postmaster/fork_process.h       |   6 +
 src/include/postmaster/pgarch.h             |   9 +-
 src/include/postmaster/postmaster.h         |  62 +-
 src/include/postmaster/startup.h            |   5 +-
 src/include/postmaster/subprocess.h         |  66 ++
 src/include/postmaster/syslogger.h          |   8 +-
 src/include/postmaster/walwriter.h          |   2 +-
 src/include/replication/walreceiver.h       |   2 +-
 32 files changed, 1041 insertions(+), 1271 deletions(-)
 create mode 100644 src/backend/postmaster/subprocess.c
 create mode 100644 src/include/postmaster/subprocess.h

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index bfc629c753..76a84a15f5 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -35,6 +35,7 @@
 #include "pgstat.h"
 #include "postmaster/bgwriter.h"
 #include "postmaster/startup.h"
+#include "postmaster/subprocess.h"
 #include "postmaster/walwriter.h"
 #include "replication/walreceiver.h"
 #include "storage/bufmgr.h"
@@ -56,8 +57,6 @@ uint32		bootstrap_data_checksum_version = 0;	/* No checksum */
 #define ALLOC(t, c) \
 	((t *) MemoryContextAllocZero(TopMemoryContext, (unsigned)(c) * sizeof(t)))
 
-static void CheckerModeMain(void);
-static void BootstrapModeMain(void);
 static void bootstrap_signals(void);
 static void ShutdownAuxiliaryProcess(int code, Datum arg);
 static Form_pg_attribute AllocateAttribute(void);
@@ -314,35 +313,20 @@ AuxiliaryProcessMain(int argc, char *argv[])
 		proc_exit(1);
 	}
 
+	/*
+	 * At this point, we should know what kind of process we are. If the
+	 * MySubprocess global has not been initialized, just cast and pass along the
+	 * MyAuxProcType enum
+	 */
+	if (MySubprocess == NULL)
+		InitializeMySubprocess((SubprocessType)MyAuxProcType);
+
 	/*
 	 * Identify myself via ps
 	 */
 	if (IsUnderPostmaster)
 	{
-		const char *statmsg;
-
-		switch (MyAuxProcType)
-		{
-			case StartupProcess:
-				statmsg = pgstat_get_backend_desc(B_STARTUP);
-				break;
-			case BgWriterProcess:
-				statmsg = pgstat_get_backend_desc(B_BG_WRITER);
-				break;
-			case CheckpointerProcess:
-				statmsg = pgstat_get_backend_desc(B_CHECKPOINTER);
-				break;
-			case WalWriterProcess:
-				statmsg = pgstat_get_backend_desc(B_WAL_WRITER);
-				break;
-			case WalReceiverProcess:
-				statmsg = pgstat_get_backend_desc(B_WAL_RECEIVER);
-				break;
-			default:
-				statmsg = "??? process";
-				break;
-		}
-		init_ps_display(statmsg, "", "", "");
+		init_ps_display(MySubprocess->desc, "", "", "");
 	}
 
 	/* Acquire configuration parameters, unless inherited from postmaster */
@@ -426,56 +410,10 @@ AuxiliaryProcessMain(int argc, char *argv[])
 	 */
 	SetProcessingMode(NormalProcessing);
 
-	switch (MyAuxProcType)
-	{
-		case CheckerProcess:
-			/* don't set signals, they're useless here */
-			CheckerModeMain();
-			proc_exit(1);		/* should never return */
-
-		case BootstrapProcess:
-
-			/*
-			 * There was a brief instant during which mode was Normal; this is
-			 * okay.  We need to be in bootstrap mode during BootStrapXLOG for
-			 * the sake of multixact initialization.
-			 */
-			SetProcessingMode(BootstrapProcessing);
-			bootstrap_signals();
-			BootStrapXLOG();
-			BootstrapModeMain();
-			proc_exit(1);		/* should never return */
-
-		case StartupProcess:
-			/* don't set signals, startup process has its own agenda */
-			StartupProcessMain();
-			proc_exit(1);		/* should never return */
-
-		case BgWriterProcess:
-			/* don't set signals, bgwriter has its own agenda */
-			BackgroundWriterMain();
-			proc_exit(1);		/* should never return */
-
-		case CheckpointerProcess:
-			/* don't set signals, checkpointer has its own agenda */
-			CheckpointerMain();
-			proc_exit(1);		/* should never return */
-
-		case WalWriterProcess:
-			/* don't set signals, walwriter has its own agenda */
-			InitXLOGAccess();
-			WalWriterMain();
-			proc_exit(1);		/* should never return */
-
-		case WalReceiverProcess:
-			/* don't set signals, walreceiver has its own agenda */
-			WalReceiverMain();
-			proc_exit(1);		/* should never return */
-
-		default:
-			elog(PANIC, "unrecognized process type: %d", (int) MyAuxProcType);
-			proc_exit(1);
-	}
+	/* Now jump into the subprocess main function and never look back! */
+	MySubprocess->entrypoint(argc, argv);
+
+	proc_exit(1);		/* should never return */
 }
 
 /*
@@ -484,8 +422,8 @@ AuxiliaryProcessMain(int argc, char *argv[])
  * settings).  Since, in fact, that was already done by BaseInit(),
  * we have nothing more to do here.
  */
-static void
-CheckerModeMain(void)
+void
+CheckerModeMain(int argc, char *argv[])
 {
 	proc_exit(0);
 }
@@ -497,11 +435,15 @@ CheckerModeMain(void)
  *	 The bootstrap backend doesn't speak SQL, but instead expects
  *	 commands in a special bootstrap language.
  */
-static void
-BootstrapModeMain(void)
+void
+BootstrapModeMain(int argc, char *argv[])
 {
 	int			i;
 
+	SetProcessingMode(BootstrapProcessing);
+	bootstrap_signals();
+	BootStrapXLOG();
+
 	Assert(!IsUnderPostmaster);
 	Assert(IsBootstrapProcessingMode());
 
@@ -516,7 +458,6 @@ BootstrapModeMain(void)
 	 * Do backend-like initialization for bootstrap mode
 	 */
 	InitProcess();
-
 	InitPostgres(NULL, InvalidOid, NULL, InvalidOid, NULL, false);
 
 	/* Initialize stuff for bootstrap-file processing */
diff --git a/src/backend/main/main.c b/src/backend/main/main.c
index da3dae9e25..68b8b3a918 100644
--- a/src/backend/main/main.c
+++ b/src/backend/main/main.c
@@ -35,6 +35,7 @@
 #include "common/username.h"
 #include "port/atomics.h"
 #include "postmaster/postmaster.h"
+#include "postmaster/subprocess.h"
 #include "storage/s_lock.h"
 #include "storage/spin.h"
 #include "tcop/tcopprot.h"
diff --git a/src/backend/postmaster/Makefile b/src/backend/postmaster/Makefile
index bfdf6a833d..52b4c2142c 100644
--- a/src/backend/postmaster/Makefile
+++ b/src/backend/postmaster/Makefile
@@ -23,6 +23,7 @@ OBJS = \
 	pgstat.o \
 	postmaster.o \
 	startup.o \
+	subprocess.o \
 	syslogger.o \
 	walwriter.o
 
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index e3a43d3296..78a4031b40 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -83,7 +83,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"
@@ -95,6 +94,7 @@
 #include "storage/procsignal.h"
 #include "storage/sinvaladt.h"
 #include "storage/smgr.h"
+#include "postmaster/subprocess.h"
 #include "tcop/tcopprot.h"
 #include "utils/fmgroids.h"
 #include "utils/fmgrprotos.h"
@@ -135,8 +135,8 @@ int			Log_autovacuum_min_duration = -1;
 #define MAX_AUTOVAC_SLEEPTIME 300	/* seconds */
 
 /* Flags to tell if we are in an autovacuum process */
-static bool am_autovacuum_launcher = false;
-static bool am_autovacuum_worker = false;
+bool am_autovacuum_launcher = false;
+bool am_autovacuum_worker = false;
 
 /* Flags set by signal handlers */
 static volatile sig_atomic_t got_SIGUSR2 = false;
@@ -302,13 +302,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();
@@ -346,95 +339,22 @@ static void autovac_report_workitem(AutoVacuumWorkItem *workitem,
 static void avl_sigusr2_handler(SIGNAL_ARGS);
 static void autovac_refresh_stats(void);
 
-
-
 /********************************************************************
  *					  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
- */
-void
-AutovacuumLauncherIAm(void)
-{
-	am_autovacuum_launcher = true;
-}
-#endif
-
-/*
- * Main entry point for autovacuum launcher process, to be called from the
- * postmaster.
- */
-int
-StartAutoVacLauncher(void)
-{
-	pid_t		AutoVacPID;
-
-#ifdef EXEC_BACKEND
-	switch ((AutoVacPID = avlauncher_forkexec()))
-#else
-	switch ((AutoVacPID = fork_process()))
-#endif
-	{
-		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;
-	}
-
-	/* shouldn't get here */
-	return 0;
-}
-
 /*
  * Main loop for the autovacuum launcher process.
  */
-NON_EXEC_STATIC void
-AutoVacLauncherMain(int argc, char *argv[])
+void
+AutoVacLauncherMain(pg_attribute_unused() int argc, pg_attribute_unused() char *argv[])
 {
 	sigjmp_buf	local_sigjmp_buf;
 
 	am_autovacuum_launcher = true;
 
 	/* Identify myself via ps */
-	init_ps_display(pgstat_get_backend_desc(B_AUTOVAC_LAUNCHER), "", "", "");
+	init_ps_display(MySubprocess->desc, "", "", "");
 
 	ereport(DEBUG1,
 			(errmsg("autovacuum launcher started")));
@@ -1423,83 +1343,11 @@ 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
- */
-void
-AutovacuumWorkerIAm(void)
-{
-	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;
-
-#ifdef EXEC_BACKEND
-	switch ((worker_pid = avworker_forkexec()))
-#else
-	switch ((worker_pid = fork_process()))
-#endif
-	{
-		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;
-	}
-
-	/* shouldn't get here */
-	return 0;
-}
-
 /*
  * AutoVacWorkerMain
  */
-NON_EXEC_STATIC void
-AutoVacWorkerMain(int argc, char *argv[])
+void
+AutoVacWorkerMain(pg_attribute_unused() int argc, pg_attribute_unused() char *argv[])
 {
 	sigjmp_buf	local_sigjmp_buf;
 	Oid			dbid;
@@ -1507,7 +1355,7 @@ AutoVacWorkerMain(int argc, char *argv[])
 	am_autovacuum_worker = true;
 
 	/* Identify myself via ps */
-	init_ps_display(pgstat_get_backend_desc(B_AUTOVAC_WORKER), "", "", "");
+	init_ps_display(MySubprocess->desc, "", "", "");
 
 	SetProcessingMode(InitProcessing);
 
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 75fc0d5d33..7993a0e360 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -33,8 +33,10 @@
 #include "storage/shmem.h"
 #include "tcop/tcopprot.h"
 #include "utils/ascii.h"
+#include "utils/memutils.h"
 #include "utils/ps_status.h"
 #include "utils/timeout.h"
+#include "utils/timestamp.h"
 
 /*
  * The postmaster's list of registered background workers, in private memory.
@@ -133,7 +135,106 @@ static const struct
 
 /* Private functions. */
 static bgworker_main_type LookupBackgroundWorkerFunction(const char *libraryname, const char *funcname);
+static bool assign_backendlist_entry(RegisteredBgWorker *rw);
 
+bool BackgroundWorkerCleanup(int argc, char *argv[]);
+void BackgroundWorkerParentMain(int argc, char *argv[]);
+
+/*
+ * Allocate the Backend struct for a connected background worker, but don't
+ * add it to the list of backends just yet.
+ *
+ * On failure, return false without changing any worker state.
+ *
+ * Some info from the Backend is copied into the passed rw.
+ */
+static bool
+assign_backendlist_entry(RegisteredBgWorker *rw)
+{
+	Backend    *bn;
+
+	/*
+	 * Compute the cancel key that will be assigned to this session. We
+	 * probably don't need cancel keys for background workers, but we'd better
+	 * have something random in the field to prevent unfriendly people from
+	 * sending cancels to them.
+	 */
+	if (!RandomCancelKey(&MyCancelKey))
+	{
+		ereport(LOG,
+				(errcode(ERRCODE_INTERNAL_ERROR),
+				 errmsg("could not generate random cancel key")));
+		return false;
+	}
+
+	bn = malloc(sizeof(Backend));
+	if (bn == NULL)
+	{
+		ereport(LOG,
+				(errcode(ERRCODE_OUT_OF_MEMORY),
+				 errmsg("out of memory")));
+		return false;
+	}
+
+	bn->cancel_key = MyCancelKey;
+	bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
+	bn->bkend_type = BACKEND_TYPE_BGWORKER;
+	bn->dead_end = false;
+	bn->bgworker_notify = false;
+
+	rw->rw_backend = bn;
+	rw->rw_child_slot = bn->child_slot;
+
+	return true;
+}
+
+/*
+ * BgWorkerPrep
+ *
+ *  Postmaster subroutine to prepare a bgworker subprocess.
+ *
+ *	Returns 0 on success or -1 on failure.
+ *
+ *	Note: if fail, we will be called again from the postmaster main loop.
+ */
+int
+BgWorkerPrep(int argc, char *argv[])
+{
+	RegisteredBgWorker *rw = CurrentBgWorker;
+#ifdef EXEC_BACKEND
+	char				forkav[MAXPGPATH];
+#endif
+
+	Assert(CurrentBgWorker);
+	Assert(rw->rw_pid == 0);
+
+	/*
+	 * Allocate and assign the Backend element.  Note we must do this before
+	 * forking, so that we can handle out of memory properly.
+	 *
+	 * Treat failure as though the worker had crashed.  That way, the
+	 * postmaster will wait a bit before attempting to start it again; if it
+	 * tried again right away, most likely it'd find itself repeating the
+	 * out-of-memory or fork failure condition.
+	 */
+	if (!assign_backendlist_entry(rw))
+	{
+		rw->rw_crashed_at = GetCurrentTimestamp();
+		return -1;
+	}
+
+	ereport(DEBUG1,
+			(errmsg("starting background worker process \"%s\"",
+					rw->rw_worker.bgw_name)));
+
+#ifdef EXEC_BACKEND
+	/* Rewrite bgworker fork parameter with shmem_slot */
+	snprintf(forkav, MAXPGPATH, "%s=%d", argv[1], rw->rw_shmem_slot);
+	argv[1] = forkav;
+#endif
+
+	return 0;
+}
 
 /*
  * Calculate shared memory needed.
@@ -677,12 +778,28 @@ bgworker_sigusr1_handler(SIGNAL_ARGS)
  * postmaster.
  */
 void
-StartBackgroundWorker(void)
+BackgroundWorkerMain(pg_attribute_unused() int argc, pg_attribute_unused() char *argv[])
 {
 	sigjmp_buf	local_sigjmp_buf;
-	BackgroundWorker *worker = MyBgworkerEntry;
+	BackgroundWorker *worker;
 	bgworker_main_type entrypt;
 
+#ifndef EXEC_BACKEND
+	/*
+	 * Before blowing away PostmasterContext, save this bgworker's
+	 * data where it can find it.
+	 */
+	MyBgworkerEntry = (BackgroundWorker *)
+						MemoryContextAlloc(TopMemoryContext, sizeof(BackgroundWorker));
+	memcpy(MyBgworkerEntry, &(CurrentBgWorker->rw_worker), sizeof(BackgroundWorker));
+
+	/* Now we can release postmaster's working memory context */
+	MemoryContextSwitchTo(TopMemoryContext);
+	MemoryContextDelete(PostmasterContext);
+	PostmasterContext = NULL;
+#endif
+
+	worker = MyBgworkerEntry;
 	if (worker == NULL)
 		elog(FATAL, "unable to find bgworker entry");
 
@@ -816,6 +933,42 @@ StartBackgroundWorker(void)
 	proc_exit(0);
 }
 
+bool
+BackgroundWorkerCleanup(pg_attribute_unused() int argc, pg_attribute_unused() char *argv[])
+{
+	RegisteredBgWorker *rw = CurrentBgWorker;
+
+	/* 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;
+	free(rw->rw_backend);
+	rw->rw_backend = NULL;
+	/* mark entry as crashed, so we'll try again later */
+	rw->rw_crashed_at = GetCurrentTimestamp();
+
+	/* Don't panic. */
+	return false;
+}
+
+void
+BackgroundWorkerParentMain(pg_attribute_unused() int argc, pg_attribute_unused() char *argv[])
+{
+	RegisteredBgWorker *rw = CurrentBgWorker;
+
+	rw->rw_pid = MyChildProcPid;
+	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);
+#endif
+}
+
 /*
  * Register a new static background worker.
  *
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index 069e27e427..b2170938f3 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -91,7 +91,7 @@ static XLogRecPtr last_snapshot_lsn = InvalidXLogRecPtr;
  * basic execution environment, but not enabled signals yet.
  */
 void
-BackgroundWriterMain(void)
+BackgroundWriterMain(int argc, char *argv[])
 {
 	sigjmp_buf	local_sigjmp_buf;
 	MemoryContext bgwriter_context;
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index e354a78725..83f7be1b1e 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -180,7 +180,7 @@ static void ReqCheckpointHandler(SIGNAL_ARGS);
  * basic execution environment, but not enabled signals yet.
  */
 void
-CheckpointerMain(void)
+CheckpointerMain(int argc, char *argv[])
 {
 	sigjmp_buf	local_sigjmp_buf;
 	MemoryContext checkpointer_context;
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 3ca30badb2..02c98d5d9f 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -91,11 +91,7 @@ static volatile sig_atomic_t ready_to_stop = false;
  * Local function forward declarations
  * ----------
  */
-#ifdef EXEC_BACKEND
-static pid_t pgarch_forkexec(void);
-#endif
-
-NON_EXEC_STATIC void PgArchiverMain(int argc, char *argv[]) pg_attribute_noreturn();
+void PgArchiverMain(int argc, char *argv[]) pg_attribute_noreturn();
 static void pgarch_exit(SIGNAL_ARGS);
 static void pgarch_waken(SIGNAL_ARGS);
 static void pgarch_waken_stop(SIGNAL_ARGS);
@@ -112,7 +108,7 @@ static void pgarch_archiveDone(char *xlog);
  */
 
 /*
- * pgarch_start
+ * PgArchiverPrep
  *
  *	Called from postmaster at startup or after an existing archiver
  *	died.  Attempt to fire up a fresh archiver process.
@@ -122,10 +118,9 @@ static void pgarch_archiveDone(char *xlog);
  *	Note: if fail, we will be called again from the postmaster main loop.
  */
 int
-pgarch_start(void)
+PgArchiverPrep(int argc, char *argv[])
 {
 	time_t		curtime;
-	pid_t		pgArchPid;
 
 	/*
 	 * Do nothing if no archiver needed
@@ -140,43 +135,10 @@ pgarch_start(void)
 	 * the postmaster main loop, we will get another chance later.
 	 */
 	curtime = time(NULL);
-	if ((unsigned int) (curtime - last_pgarch_start_time) <
+	if ((unsigned int) (curtime - last_pgarch_start_time) >=
 		(unsigned int) PGARCH_RESTART_INTERVAL)
-		return 0;
-	last_pgarch_start_time = curtime;
-
-#ifdef EXEC_BACKEND
-	switch ((pgArchPid = pgarch_forkexec()))
-#else
-	switch ((pgArchPid = fork_process()))
-#endif
-	{
-		case -1:
-			ereport(LOG,
-					(errmsg("could not fork archiver: %m")));
-			return 0;
-
-#ifndef EXEC_BACKEND
-		case 0:
-			/* in postmaster child ... */
-			InitPostmasterChild();
-
-			/* Close the postmaster's sockets */
-			ClosePostmasterPorts(false);
-
-			/* Drop our connection to postmaster's shared memory, as well */
-			dsm_detach_all();
-			PGSharedMemoryDetach();
-
-			PgArchiverMain(0, NULL);
-			break;
-#endif
-
-		default:
-			return (int) pgArchPid;
-	}
+		last_pgarch_start_time = curtime;
 
-	/* shouldn't get here */
 	return 0;
 }
 
@@ -186,42 +148,19 @@ pgarch_start(void)
  */
 
 
-#ifdef EXEC_BACKEND
-
-/*
- * pgarch_forkexec() -
- *
- * Format up the arglist for, then fork and exec, archive process
- */
-static pid_t
-pgarch_forkexec(void)
-{
-	char	   *av[10];
-	int			ac = 0;
-
-	av[ac++] = "postgres";
-
-	av[ac++] = "--forkarch";
-
-	av[ac++] = NULL;			/* filled in by postmaster_forkexec */
-
-	av[ac] = NULL;
-	Assert(ac < lengthof(av));
-
-	return postmaster_forkexec(ac, av);
-}
-#endif							/* EXEC_BACKEND */
-
-
 /*
  * PgArchiverMain
  *
  *	The argc/argv parameters are valid only in EXEC_BACKEND case.  However,
  *	since we don't use 'em, it hardly matters...
  */
-NON_EXEC_STATIC void
-PgArchiverMain(int argc, char *argv[])
+void
+PgArchiverMain(pg_attribute_unused() int argc, pg_attribute_unused() char *arg[])
 {
+	/* Drop our connection to postmaster's shared memory, as well */
+	dsm_detach_all();
+	PGSharedMemoryDetach();
+
 	/*
 	 * 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/pgstat.c b/src/backend/postmaster/pgstat.c
index 462b4d7e06..1df3d83f3f 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -61,6 +61,7 @@
 #include "storage/pg_shmem.h"
 #include "storage/procsignal.h"
 #include "storage/sinvaladt.h"
+#include "postmaster/subprocess.h"
 #include "utils/ascii.h"
 #include "utils/guc.h"
 #include "utils/memutils.h"
@@ -275,11 +276,7 @@ static instr_time total_func_time;
  * Local function forward declarations
  * ----------
  */
-#ifdef EXEC_BACKEND
-static pid_t pgstat_forkexec(void);
-#endif
-
-NON_EXEC_STATIC void PgstatCollectorMain(int argc, char *argv[]) pg_attribute_noreturn();
+void PgstatCollectorMain(int argc, char *argv[]) pg_attribute_noreturn();
 static void pgstat_beshutdown_hook(int code, Datum arg);
 
 static PgStat_StatDBEntry *pgstat_get_db_entry(Oid databaseid, bool create);
@@ -685,46 +682,18 @@ pgstat_reset_all(void)
 	pgstat_reset_remove_files(PGSTAT_STAT_PERMANENT_DIRECTORY);
 }
 
-#ifdef EXEC_BACKEND
 
 /*
- * pgstat_forkexec() -
- *
- * Format up the arglist for, then fork and exec, statistics collector process
- */
-static pid_t
-pgstat_forkexec(void)
-{
-	char	   *av[10];
-	int			ac = 0;
-
-	av[ac++] = "postgres";
-	av[ac++] = "--forkcol";
-	av[ac++] = NULL;			/* filled in by postmaster_forkexec */
-
-	av[ac] = NULL;
-	Assert(ac < lengthof(av));
-
-	return postmaster_forkexec(ac, av);
-}
-#endif							/* EXEC_BACKEND */
-
-
-/*
- * pgstat_start() -
+ * PgstatCollectorPrep
  *
  *	Called from postmaster at startup or after an existing collector
  *	died.  Attempt to fire up a fresh statistics collector.
  *
- *	Returns PID of child process, or 0 if fail.
- *
- *	Note: if fail, we will be called again from the postmaster main loop.
  */
 int
-pgstat_start(void)
+PgstatCollectorPrep(int argc, char *argv[])
 {
 	time_t		curtime;
-	pid_t		pgStatPid;
 
 	/*
 	 * Check that the socket is there, else pgstat_init failed and we can do
@@ -733,53 +702,18 @@ pgstat_start(void)
 	if (pgStatSock == PGINVALID_SOCKET)
 		return 0;
 
+	curtime = time(NULL);
+
 	/*
 	 * Do nothing if too soon since last collector start.  This is a safety
 	 * valve to protect against continuous respawn attempts if the collector
 	 * is dying immediately at launch.  Note that since we will be re-called
 	 * from the postmaster main loop, we will get another chance later.
 	 */
-	curtime = time(NULL);
-	if ((unsigned int) (curtime - last_pgstat_start_time) <
+	if ((unsigned int) (curtime - last_pgstat_start_time) >=
 		(unsigned int) PGSTAT_RESTART_INTERVAL)
-		return 0;
-	last_pgstat_start_time = curtime;
-
-	/*
-	 * Okay, fork off the collector.
-	 */
-#ifdef EXEC_BACKEND
-	switch ((pgStatPid = pgstat_forkexec()))
-#else
-	switch ((pgStatPid = fork_process()))
-#endif
-	{
-		case -1:
-			ereport(LOG,
-					(errmsg("could not fork statistics collector: %m")));
-			return 0;
-
-#ifndef EXEC_BACKEND
-		case 0:
-			/* in postmaster child ... */
-			InitPostmasterChild();
-
-			/* Close the postmaster's sockets */
-			ClosePostmasterPorts(false);
+		last_pgstat_start_time = curtime;
 
-			/* Drop our connection to postmaster's shared memory, as well */
-			dsm_detach_all();
-			PGSharedMemoryDetach();
-
-			PgstatCollectorMain(0, NULL);
-			break;
-#endif
-
-		default:
-			return (int) pgStatPid;
-	}
-
-	/* shouldn't get here */
 	return 0;
 }
 
@@ -2893,61 +2827,6 @@ pgstat_bestart(void)
 	 */
 	lbeentry.st_procpid = MyProcPid;
 
-	if (MyBackendId != InvalidBackendId)
-	{
-		if (IsAutoVacuumLauncherProcess())
-		{
-			/* Autovacuum Launcher */
-			lbeentry.st_backendType = B_AUTOVAC_LAUNCHER;
-		}
-		else if (IsAutoVacuumWorkerProcess())
-		{
-			/* Autovacuum Worker */
-			lbeentry.st_backendType = B_AUTOVAC_WORKER;
-		}
-		else if (am_walsender)
-		{
-			/* Wal sender */
-			lbeentry.st_backendType = B_WAL_SENDER;
-		}
-		else if (IsBackgroundWorker)
-		{
-			/* bgworker */
-			lbeentry.st_backendType = B_BG_WORKER;
-		}
-		else
-		{
-			/* client-backend */
-			lbeentry.st_backendType = B_BACKEND;
-		}
-	}
-	else
-	{
-		/* Must be an auxiliary process */
-		Assert(MyAuxProcType != NotAnAuxProcess);
-		switch (MyAuxProcType)
-		{
-			case StartupProcess:
-				lbeentry.st_backendType = B_STARTUP;
-				break;
-			case BgWriterProcess:
-				lbeentry.st_backendType = B_BG_WRITER;
-				break;
-			case CheckpointerProcess:
-				lbeentry.st_backendType = B_CHECKPOINTER;
-				break;
-			case WalWriterProcess:
-				lbeentry.st_backendType = B_WAL_WRITER;
-				break;
-			case WalReceiverProcess:
-				lbeentry.st_backendType = B_WAL_RECEIVER;
-				break;
-			default:
-				elog(FATAL, "unrecognized process type: %d",
-					 (int) MyAuxProcType);
-		}
-	}
-
 	lbeentry.st_proc_start_timestamp = MyStartTimestamp;
 	lbeentry.st_activity_start_timestamp = 0;
 	lbeentry.st_state_start_timestamp = 0;
@@ -2955,9 +2834,9 @@ pgstat_bestart(void)
 	lbeentry.st_databaseid = MyDatabaseId;
 
 	/* We have userid for client-backends, wal-sender and bgworker processes */
-	if (lbeentry.st_backendType == B_BACKEND
-		|| lbeentry.st_backendType == B_WAL_SENDER
-		|| lbeentry.st_backendType == B_BG_WORKER)
+	if (MySubprocessType == ClientBackendType
+		|| MySubprocessType == WalSenderType
+		|| MySubprocessType == BgWorkerType)
 		lbeentry.st_userid = GetSessionUserId();
 	else
 		lbeentry.st_userid = InvalidOid;
@@ -4269,48 +4148,6 @@ pgstat_get_crashed_backend_activity(int pid, char *buffer, int buflen)
 	return NULL;
 }
 
-const char *
-pgstat_get_backend_desc(BackendType backendType)
-{
-	const char *backendDesc = "unknown process type";
-
-	switch (backendType)
-	{
-		case B_AUTOVAC_LAUNCHER:
-			backendDesc = "autovacuum launcher";
-			break;
-		case B_AUTOVAC_WORKER:
-			backendDesc = "autovacuum worker";
-			break;
-		case B_BACKEND:
-			backendDesc = "client backend";
-			break;
-		case B_BG_WORKER:
-			backendDesc = "background worker";
-			break;
-		case B_BG_WRITER:
-			backendDesc = "background writer";
-			break;
-		case B_CHECKPOINTER:
-			backendDesc = "checkpointer";
-			break;
-		case B_STARTUP:
-			backendDesc = "startup";
-			break;
-		case B_WAL_RECEIVER:
-			backendDesc = "walreceiver";
-			break;
-		case B_WAL_SENDER:
-			backendDesc = "walsender";
-			break;
-		case B_WAL_WRITER:
-			backendDesc = "walwriter";
-			break;
-	}
-
-	return backendDesc;
-}
-
 /* ------------------------------------------------------------
  * Local support functions follow
  * ------------------------------------------------------------
@@ -4423,13 +4260,19 @@ pgstat_send_bgwriter(void)
  *	The argc/argv parameters are valid only in EXEC_BACKEND case.
  * ----------
  */
-NON_EXEC_STATIC void
-PgstatCollectorMain(int argc, char *argv[])
+void
+PgstatCollectorMain(pg_attribute_unused() int argc, pg_attribute_unused() char *argv[])
 {
 	int			len;
 	PgStat_Msg	msg;
 	int			wr;
 
+#ifndef EXEC_BACKEND
+	/* Drop our connection to postmaster's shared memory, as well */
+	dsm_detach_all();
+	PGSharedMemoryDetach();
+#endif
+
 	/*
 	 * Ignore all signals usually bound to some action in the postmaster,
 	 * except SIGHUP and SIGQUIT.  Note we don't need a SIGUSR1 handler to
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 55187eb910..dcfc6990c1 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -114,6 +114,7 @@
 #include "postmaster/fork_process.h"
 #include "postmaster/pgarch.h"
 #include "postmaster/postmaster.h"
+#include "postmaster/subprocess.h"
 #include "postmaster/syslogger.h"
 #include "replication/logicallauncher.h"
 #include "replication/walsender.h"
@@ -136,62 +137,19 @@
 #include "storage/spin.h"
 #endif
 
-
-/*
- * Possible types of a backend. Beyond being the possible bkend_type values in
- * struct bkend, these are OR-able request flag bits for SignalSomeChildren()
- * and CountChildren().
- */
-#define BACKEND_TYPE_NORMAL		0x0001	/* normal backend */
-#define BACKEND_TYPE_AUTOVAC	0x0002	/* autovacuum worker process */
-#define BACKEND_TYPE_WALSND		0x0004	/* walsender process */
-#define BACKEND_TYPE_BGWORKER	0x0008	/* bgworker process */
-#define BACKEND_TYPE_ALL		0x000F	/* OR of all the above */
-
-#define BACKEND_TYPE_WORKER		(BACKEND_TYPE_AUTOVAC | BACKEND_TYPE_BGWORKER)
-
-/*
- * List of active backends (or child processes anyway; we don't actually
- * know whether a given child has become a backend or is still in the
- * authorization phase).  This is used mainly to keep track of how many
- * children we have and send them appropriate signals when necessary.
- *
- * "Special" children such as the startup, bgwriter and autovacuum launcher
- * tasks are not in this list.  Autovacuum worker and walsender are in it.
- * Also, "dead_end" children are in it: these are children launched just for
- * the purpose of sending a friendly rejection message to a would-be client.
- * We must track them because they are attached to shared memory, but we know
- * they will never become live backends.  dead_end children are not assigned a
- * PMChildSlot.
- *
- * Background workers are in this list, too.
- */
-typedef struct bkend
-{
-	pid_t		pid;			/* process id of backend */
-	int32		cancel_key;		/* cancel key for cancels for this backend */
-	int			child_slot;		/* PMChildSlot for this backend, if any */
-
-	/*
-	 * Flavor of backend or auxiliary process.  Note that BACKEND_TYPE_WALSND
-	 * backends initially announce themselves as BACKEND_TYPE_NORMAL, so if
-	 * bkend_type is normal, you should check for a recent transition.
-	 */
-	int			bkend_type;
-	bool		dead_end;		/* is it going to send an error and quit? */
-	bool		bgworker_notify;	/* gets bgworker start/stop notifications */
-	dlist_node	elem;			/* list link in BackendList */
-} Backend;
-
-static dlist_head BackendList = DLIST_STATIC_INIT(BackendList);
+dlist_head BackendList = DLIST_STATIC_INIT(BackendList);
 
 #ifdef EXEC_BACKEND
 static Backend *ShmemBackendArray;
 #endif
 
 BackgroundWorker *MyBgworkerEntry = NULL;
+RegisteredBgWorker *CurrentBgWorker = NULL;
+static Backend	   *MyBackend;
+static int			child_errno;
 
-
+/* Struct containing postmaster subprocess control info */
+SubprocessType		MySubprocessType;
 
 /* The socket number we are listening for connections on */
 int			PostPortNumber;
@@ -403,25 +361,25 @@ static void BackendInitialize(Port *port);
 static void BackendRun(Port *port) pg_attribute_noreturn();
 static void ExitPostmaster(int status) pg_attribute_noreturn();
 static int	ServerLoop(void);
-static int	BackendStartup(Port *port);
 static int	ProcessStartupPacket(Port *port, bool secure_done);
 static void SendNegotiateProtocolVersion(List *unrecognized_protocol_options);
 static void processCancelRequest(Port *port, void *pkt);
 static int	initMasks(fd_set *rmask);
 static void report_fork_failure_to_client(Port *port, int errnum);
 static CAC_state canAcceptConnections(int backend_type);
-static bool RandomCancelKey(int32 *cancel_key);
 static void signal_child(pid_t pid, int signal);
 static bool SignalSomeChildren(int signal, int targets);
 static void TerminateChildren(int signal);
+#ifdef EXEC_BACKEND
+static void shmemSetup(void);
+#endif
 
 #define SignalChildren(sig)			   SignalSomeChildren(sig, BACKEND_TYPE_ALL)
 
 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 StartSubprocess(SubprocessType type);
 static void StartAutovacuumWorker(void);
 static void MaybeStartWalReceiver(void);
 static void InitPostmasterDeathWatchHandle(void);
@@ -455,8 +413,7 @@ typedef struct
 } win32_deadchild_waitinfo;
 #endif							/* WIN32 */
 
-static pid_t backend_forkexec(Port *port);
-static pid_t internal_forkexec(int argc, char *argv[], Port *port);
+static pid_t internal_forkexec(int argc, char *argv[]);
 
 /* Type for a socket that can be inherited to a client process */
 #ifdef WIN32
@@ -507,6 +464,9 @@ typedef struct
 	TimestampTz PgStartTime;
 	TimestampTz PgReloadTime;
 	pg_time_t	first_syslogger_file_time;
+
+	SubprocessType		MySubprocessType;
+
 	bool		redirection_done;
 	bool		IsBinaryUpgrade;
 	int			max_safe_fds;
@@ -534,15 +494,10 @@ static bool save_backend_variables(BackendParameters *param, Port *port,
 								   HANDLE childProcess, pid_t childPid);
 #endif
 
-static void ShmemBackendArrayAdd(Backend *bn);
 static void ShmemBackendArrayRemove(Backend *bn);
 #endif							/* EXEC_BACKEND */
 
-#define StartupDataBase()		StartChildProcess(StartupProcess)
-#define StartBackgroundWriter() StartChildProcess(BgWriterProcess)
-#define StartCheckpointer()		StartChildProcess(CheckpointerProcess)
-#define StartWalWriter()		StartChildProcess(WalWriterProcess)
-#define StartWalReceiver()		StartChildProcess(WalReceiverProcess)
+#define BGWORKER_LEN			15
 
 /* Macros to check exit status of a child process */
 #define EXIT_STATUS_0(st)  ((st) == 0)
@@ -560,6 +515,129 @@ int			postmaster_alive_fds[2] = {-1, -1};
 HANDLE		PostmasterHandle;
 #endif
 
+static Port *ConnProcPort = NULL;
+
+/*
+ *	 BackendMain
+ *
+ *	 Child code when forking a Backend.
+ */
+void BackendMain(pg_attribute_unused() int argc, pg_attribute_unused() char *argv[])
+{
+	/*
+	 * 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(ConnProcPort);
+
+	/* And run the backend */
+	BackendRun(ConnProcPort);		/* does not return */
+}
+
+/*
+ *	 BackendPostmasterMain
+ *
+ *	 Parent code when forking a Backend.
+ */
+void BackendParentMain(pg_attribute_unused() int argc, pg_attribute_unused() char *argv[])
+{
+	/* in parent, successful fork */
+	ereport(DEBUG2,
+			(errmsg_internal("forked new backend, pid=%d socket=%d",
+							 (int) MyChildProcPid, (int) MyProcPort->sock)));
+
+	/*
+	 * Everything's been successful, it's safe to add this backend to our list
+	 * of backends.
+	 */
+	MyBackend->pid = MyChildProcPid;
+	MyBackend->bkend_type = BACKEND_TYPE_NORMAL;	/* Can change later to WALSND */
+	dlist_push_head(&BackendList, &MyBackend->elem);
+
+#ifdef EXEC_BACKEND
+	if (!MyBackend->dead_end)
+		ShmemBackendArrayAdd(MyBackend);
+#endif
+}
+
+/*
+ *	 BackendCleanup
+ *
+ *	 Backend cleanup in case a failure occurs forking a new Backend.
+ */
+bool
+BackendCleanup(int argc, char *argv[])
+{
+	if (!MyBackend->dead_end)
+		(void) ReleasePostmasterChildSlot(MyBackend->child_slot);
+	free(MyBackend);
+
+	report_fork_failure_to_client(MyProcPort, child_errno);
+
+	/* Don't panic */
+	return false;
+}
+
+/*
+ * BackendPrep
+ *
+ * Prepare a ForkProcType struct for starting a Backend.
+ * This does all prep related to av parameters and error messages.
+*/
+int
+BackendPrep(int argc, char *argv[])
+{
+	/*
+	 * Create backend data structure.  Better before the fork() so we can
+	 * handle failure cleanly.
+	 */
+	MyBackend = (Backend *) malloc(sizeof(Backend));
+	if (!MyBackend)
+	{
+		ereport(LOG,
+				(errcode(ERRCODE_OUT_OF_MEMORY),
+				 errmsg("out of memory")));
+	}
+
+	/*
+	 * Compute the cancel key that will be assigned to this backend. The
+	 * backend will have its own copy in the forked-off process' value of
+	 * MyCancelKey, so that it can transmit the key to the frontend.
+	 */
+	if (!RandomCancelKey(&MyCancelKey))
+	{
+		free(MyBackend);
+		ereport(LOG,
+				(errcode(ERRCODE_INTERNAL_ERROR),
+				 errmsg("could not generate random cancel key")));
+	}
+
+	MyBackend->cancel_key = MyCancelKey;
+
+	/* Pass down canAcceptConnections state */
+	ConnProcPort->canAcceptConnections = canAcceptConnections(BACKEND_TYPE_NORMAL);
+	MyBackend->dead_end = (ConnProcPort->canAcceptConnections != CAC_OK &&
+						   ConnProcPort->canAcceptConnections != CAC_WAITBACKUP);
+
+	/*
+	 * Unless it's a dead_end child, assign it a child slot number
+	 */
+	if (!MyBackend->dead_end)
+		MyBackend->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
+	else
+		MyBackend->child_slot = 0;
+
+	/* Hasn't asked to be notified about any bgworkers yet */
+	MyBackend->bgworker_notify = false;
+
+	return 0;
+}
+
 /*
  * Postmaster main entry point
  */
@@ -1083,7 +1161,7 @@ PostmasterMain(int argc, char *argv[])
 	/*
 	 * If enabled, start up syslogger collection subprocess
 	 */
-	SysLoggerPID = SysLogger_Start();
+	SysLoggerPID = StartSubprocess(SysLoggerType);
 
 	/*
 	 * Reset whereToSendOutput from DestDebug (its starting state) to
@@ -1389,7 +1467,7 @@ PostmasterMain(int argc, char *argv[])
 	/*
 	 * We're ready to rock and roll...
 	 */
-	StartupPID = StartupDataBase();
+	StartupPID = StartSubprocess(StartupType);
 	Assert(StartupPID != 0);
 	StartupStatus = STARTUP_RUNNING;
 	pmState = PM_STARTUP;
@@ -1719,19 +1797,17 @@ ServerLoop(void)
 					break;
 				if (FD_ISSET(ListenSocket[i], &rmask))
 				{
-					Port	   *port;
-
-					port = ConnCreate(ListenSocket[i]);
-					if (port)
+					ConnProcPort = ConnCreate(ListenSocket[i]);
+					if (ConnProcPort)
 					{
-						BackendStartup(port);
+						StartSubprocess(ClientBackendType);
 
 						/*
 						 * We no longer need the open socket or port structure
 						 * in this process
 						 */
-						StreamClose(port->sock);
-						ConnFree(port);
+						StreamClose(ConnProcPort->sock);
+						ConnFree(ConnProcPort);
 					}
 				}
 			}
@@ -1739,7 +1815,7 @@ ServerLoop(void)
 
 		/* If we have lost the log collector, try to start a new one */
 		if (SysLoggerPID == 0 && Logging_collector)
-			SysLoggerPID = SysLogger_Start();
+			SysLoggerPID = StartSubprocess(SysLoggerType);
 
 		/*
 		 * If no background writer process is running, and we are not in a
@@ -1750,9 +1826,9 @@ ServerLoop(void)
 			pmState == PM_HOT_STANDBY)
 		{
 			if (CheckpointerPID == 0)
-				CheckpointerPID = StartCheckpointer();
+				CheckpointerPID = StartSubprocess(CheckpointerType);
 			if (BgWriterPID == 0)
-				BgWriterPID = StartBackgroundWriter();
+				BgWriterPID = StartSubprocess(BgWriterType);
 		}
 
 		/*
@@ -1761,7 +1837,7 @@ ServerLoop(void)
 		 * be writing any new WAL).
 		 */
 		if (WalWriterPID == 0 && pmState == PM_RUN)
-			WalWriterPID = StartWalWriter();
+			WalWriterPID = StartSubprocess(WalWriterType);
 
 		/*
 		 * If we have lost the autovacuum launcher, try to start a new one. We
@@ -1773,7 +1849,7 @@ ServerLoop(void)
 			(AutoVacuumingActive() || start_autovac_launcher) &&
 			pmState == PM_RUN)
 		{
-			AutoVacPID = StartAutoVacLauncher();
+			AutoVacPID = StartSubprocess(AutoVacLauncherType);
 			if (AutoVacPID != 0)
 				start_autovac_launcher = false; /* signal processed */
 		}
@@ -1781,11 +1857,11 @@ ServerLoop(void)
 		/* If we have lost the stats collector, try to start a new one */
 		if (PgStatPID == 0 &&
 			(pmState == PM_RUN || pmState == PM_HOT_STANDBY))
-			PgStatPID = pgstat_start();
+			PgStatPID = StartSubprocess(PgstatCollectorType);
 
 		/* If we have lost the archiver, try to start a new one. */
 		if (PgArchPID == 0 && PgArchStartupAllowed())
-			PgArchPID = pgarch_start();
+			PgArchPID = StartSubprocess(PgArchiverType);
 
 		/* If we need to signal the autovacuum launcher, do so now */
 		if (avlauncher_needs_signal)
@@ -2533,12 +2609,9 @@ ConnFree(Port *conn)
  * This is called during child process startup to release file descriptors
  * that are not needed by that child process.  The postmaster still has
  * them open, of course.
- *
- * Note: we pass am_syslogger as a boolean because we don't want to set
- * the global variable yet when this is called.
  */
 void
-ClosePostmasterPorts(bool am_syslogger)
+ClosePostmasterPorts(void)
 {
 	int			i;
 
@@ -2575,7 +2648,8 @@ ClosePostmasterPorts(bool am_syslogger)
 	 * If using syslogger, close the read side of the pipe.  We don't bother
 	 * tracking this in fd.c, either.
 	 */
-	if (!am_syslogger)
+	if (MySubprocessType == SysLoggerType ||
+		MySubprocessType == ClientBackendType)
 	{
 #ifndef WIN32
 		if (syslogPipe[0] >= 0)
@@ -3037,22 +3111,22 @@ reaper(SIGNAL_ARGS)
 			 * if this fails, we'll just try again later.
 			 */
 			if (CheckpointerPID == 0)
-				CheckpointerPID = StartCheckpointer();
+				CheckpointerPID = StartSubprocess(CheckpointerType);
 			if (BgWriterPID == 0)
-				BgWriterPID = StartBackgroundWriter();
+				BgWriterPID = StartSubprocess(BgWriterType);
 			if (WalWriterPID == 0)
-				WalWriterPID = StartWalWriter();
+				WalWriterPID = StartSubprocess(WalWriterType);
 
 			/*
 			 * Likewise, start other special children as needed.  In a restart
 			 * situation, some of them may be alive already.
 			 */
 			if (!IsBinaryUpgrade && AutoVacuumingActive() && AutoVacPID == 0)
-				AutoVacPID = StartAutoVacLauncher();
+				AutoVacPID = StartSubprocess(AutoVacLauncherType);
 			if (PgArchStartupAllowed() && PgArchPID == 0)
-				PgArchPID = pgarch_start();
+				PgArchPID = StartSubprocess(PgArchiverType);
 			if (PgStatPID == 0)
-				PgStatPID = pgstat_start();
+				PgStatPID = StartSubprocess(PgstatCollectorType);
 
 			/* workers may be scheduled to start now */
 			maybe_start_bgworkers();
@@ -3198,7 +3272,7 @@ reaper(SIGNAL_ARGS)
 				LogChildExit(LOG, _("archiver process"),
 							 pid, exitstatus);
 			if (PgArchStartupAllowed())
-				PgArchPID = pgarch_start();
+				PgArchPID = StartSubprocess(PgArchiverType);
 			continue;
 		}
 
@@ -3214,7 +3288,7 @@ reaper(SIGNAL_ARGS)
 				LogChildExit(LOG, _("statistics collector process"),
 							 pid, exitstatus);
 			if (pmState == PM_RUN || pmState == PM_HOT_STANDBY)
-				PgStatPID = pgstat_start();
+				PgStatPID = StartSubprocess(PgstatCollectorType);
 			continue;
 		}
 
@@ -3223,7 +3297,7 @@ reaper(SIGNAL_ARGS)
 		{
 			SysLoggerPID = 0;
 			/* for safety's sake, launch new logger *first* */
-			SysLoggerPID = SysLogger_Start();
+			SysLoggerPID = StartSubprocess(SysLoggerType);
 			if (!EXIT_STATUS_0(exitstatus))
 				LogChildExit(LOG, _("system logger process"),
 							 pid, exitstatus);
@@ -3855,7 +3929,7 @@ PostmasterStateMachine(void)
 				Assert(Shutdown > NoShutdown);
 				/* Start the checkpointer if not running */
 				if (CheckpointerPID == 0)
-					CheckpointerPID = StartCheckpointer();
+					CheckpointerPID = StartSubprocess(CheckpointerType);
 				/* And tell it to shut down */
 				if (CheckpointerPID != 0)
 				{
@@ -3996,7 +4070,7 @@ PostmasterStateMachine(void)
 
 		reset_shared();
 
-		StartupPID = StartupDataBase();
+		StartupPID = StartSubprocess(StartupType);
 		Assert(StartupPID != 0);
 		StartupStatus = STARTUP_RUNNING;
 		pmState = PM_STARTUP;
@@ -4118,122 +4192,6 @@ TerminateChildren(int signal)
 		signal_child(PgStatPID, signal);
 }
 
-/*
- * BackendStartup -- start backend process
- *
- * returns: STATUS_ERROR if the fork failed, STATUS_OK otherwise.
- *
- * Note: if you change this code, also consider StartAutovacuumWorker.
- */
-static int
-BackendStartup(Port *port)
-{
-	Backend    *bn;				/* for backend cleanup */
-	pid_t		pid;
-
-	/*
-	 * Create backend data structure.  Better before the fork() so we can
-	 * handle failure cleanly.
-	 */
-	bn = (Backend *) malloc(sizeof(Backend));
-	if (!bn)
-	{
-		ereport(LOG,
-				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("out of memory")));
-		return STATUS_ERROR;
-	}
-
-	/*
-	 * Compute the cancel key that will be assigned to this backend. The
-	 * backend will have its own copy in the forked-off process' value of
-	 * MyCancelKey, so that it can transmit the key to the frontend.
-	 */
-	if (!RandomCancelKey(&MyCancelKey))
-	{
-		free(bn);
-		ereport(LOG,
-				(errcode(ERRCODE_INTERNAL_ERROR),
-				 errmsg("could not generate random cancel key")));
-		return STATUS_ERROR;
-	}
-
-	bn->cancel_key = MyCancelKey;
-
-	/* Pass down canAcceptConnections state */
-	port->canAcceptConnections = canAcceptConnections(BACKEND_TYPE_NORMAL);
-	bn->dead_end = (port->canAcceptConnections != CAC_OK &&
-					port->canAcceptConnections != CAC_WAITBACKUP);
-
-	/*
-	 * Unless it's a dead_end child, assign it a child slot number
-	 */
-	if (!bn->dead_end)
-		bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
-	else
-		bn->child_slot = 0;
-
-	/* Hasn't asked to be notified about any bgworkers yet */
-	bn->bgworker_notify = false;
-
-#ifdef EXEC_BACKEND
-	pid = backend_forkexec(port);
-#else							/* !EXEC_BACKEND */
-	pid = fork_process();
-	if (pid == 0)				/* child */
-	{
-		free(bn);
-
-		/* Detangle from postmaster */
-		InitPostmasterChild();
-
-		/* Close the postmaster's sockets */
-		ClosePostmasterPorts(false);
-
-		/* Perform additional initialization and collect startup packet */
-		BackendInitialize(port);
-
-		/* And run the backend */
-		BackendRun(port);
-	}
-#endif							/* EXEC_BACKEND */
-
-	if (pid < 0)
-	{
-		/* in parent, fork failed */
-		int			save_errno = errno;
-
-		if (!bn->dead_end)
-			(void) ReleasePostmasterChildSlot(bn->child_slot);
-		free(bn);
-		errno = save_errno;
-		ereport(LOG,
-				(errmsg("could not fork new process for connection: %m")));
-		report_fork_failure_to_client(port, save_errno);
-		return STATUS_ERROR;
-	}
-
-	/* in parent, successful fork */
-	ereport(DEBUG2,
-			(errmsg_internal("forked new backend, pid=%d socket=%d",
-							 (int) pid, (int) port->sock)));
-
-	/*
-	 * Everything's been successful, it's safe to add this backend to our list
-	 * of backends.
-	 */
-	bn->pid = pid;
-	bn->bkend_type = BACKEND_TYPE_NORMAL;	/* Can change later to WALSND */
-	dlist_push_head(&BackendList, &bn->elem);
-
-#ifdef EXEC_BACKEND
-	if (!bn->dead_end)
-		ShmemBackendArrayAdd(bn);
-#endif
-
-	return STATUS_OK;
-}
-
 /*
  * Try to report backend fork() failure to client before we close the
  * connection.  Since we do not care to risk blocking the postmaster on
@@ -4433,7 +4391,7 @@ BackendInitialize(Port *port)
 	 * init_ps_display() to avoid abusing the parameters like this.
 	 */
 	if (am_walsender)
-		init_ps_display(pgstat_get_backend_desc(B_WAL_SENDER), port->user_name, remote_ps_data,
+		init_ps_display(MySubprocess->desc, port->user_name, remote_ps_data,
 						update_process_title ? "authentication" : "");
 	else
 		init_ps_display(port->user_name, port->database_name, remote_ps_data,
@@ -4476,7 +4434,7 @@ BackendRun(Port *port)
 									  maxac * sizeof(char *));
 	ac = 0;
 
-	av[ac++] = "postgres";
+	av[ac++] = pstrdup("postgres");
 
 	/*
 	 * Pass any backend switches specified with -o on the postmaster's own
@@ -4527,36 +4485,11 @@ BackendRun(Port *port)
 pid_t
 postmaster_forkexec(int argc, char *argv[])
 {
-	Port		port;
-
 	/* This entry point passes dummy values for the Port variables */
-	memset(&port, 0, sizeof(port));
-	return internal_forkexec(argc, argv, &port);
-}
+	if (!ConnProcPort)
+		ConnProcPort = palloc0(sizeof(*ConnProcPort));
 
-/*
- * 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(Port *port)
-{
-	char	   *av[4];
-	int			ac = 0;
-
-	av[ac++] = "postgres";
-	av[ac++] = "--forkbackend";
-	av[ac++] = NULL;			/* filled in by internal_forkexec */
-
-	av[ac] = NULL;
-	Assert(ac < lengthof(av));
-
-	return internal_forkexec(ac, av, port);
+	return internal_forkexec(argc, argv);
 }
 
 #ifndef WIN32
@@ -4568,7 +4501,7 @@ backend_forkexec(Port *port)
  * - fork():s, and then exec():s the child process
  */
 static pid_t
-internal_forkexec(int argc, char *argv[], Port *port)
+internal_forkexec(int argc, char *argv[])
 {
 	static unsigned long tmpBackendFileNum = 0;
 	pid_t		pid;
@@ -4576,7 +4509,7 @@ internal_forkexec(int argc, char *argv[], Port *port)
 	BackendParameters param;
 	FILE	   *fp;
 
-	if (!save_backend_variables(&param, port))
+	if (!save_backend_variables(&param, ConnProcPort))
 		return -1;				/* log made by save_backend_variables */
 
 	/* Calculate name for temp file */
@@ -4861,6 +4794,28 @@ retry:
 }
 #endif							/* WIN32 */
 
+/*
+ * shmemSetup
+ *
+ * Helper function for a child to set up shmem before
+ * executing.
+ *
+ * aux_process - set to true if an auxiliary process.
+ */
+static void
+shmemSetup(void)
+{
+	/* Restore basic shared memory pointers */
+	InitShmemAccess(UsedShmemSegAddr);
+
+	if (MySubprocess->needs_aux_proc)
+		InitAuxiliaryProcess();
+	else
+		InitProcess();
+
+	/* Attach process to shared data structures */
+	CreateSharedMemoryAndSemaphores();
+}
 
 /*
  * SubPostmasterMain -- Get the fork/exec'd process into a state equivalent
@@ -4877,6 +4832,7 @@ void
 SubPostmasterMain(int argc, char *argv[])
 {
 	Port		port;
+	int			shmem_slot;
 
 	/* In EXEC_BACKEND case we will not have inherited these settings */
 	IsPostmasterEnvironment = true;
@@ -4892,12 +4848,15 @@ SubPostmasterMain(int argc, char *argv[])
 	if (argc < 3)
 		elog(FATAL, "invalid subpostmaster invocation");
 
+	Assert(!ConnProcPort);
+
 	/* Read in the variables file */
 	memset(&port, 0, sizeof(Port));
 	read_backend_variables(argv[2], &port);
+	ConnProcPort = &port;
 
 	/* Close the postmaster's sockets (as soon as we know them) */
-	ClosePostmasterPorts(strcmp(argv[1], "--forklog") == 0);
+	ClosePostmasterPorts();
 
 	/*
 	 * Set reference point for stack-depth checking
@@ -4916,6 +4875,9 @@ SubPostmasterMain(int argc, char *argv[])
 				 errmsg("out of memory")));
 #endif
 
+	if (!MySubprocess)
+		InitializeMySubprocess(MySubprocessType);
+
 	/*
 	 * 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
@@ -4932,20 +4894,16 @@ SubPostmasterMain(int argc, char *argv[])
 	 * 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], "--forkboot") == 0 ||
-		strncmp(argv[1], "--forkbgworker=", 15) == 0)
+	if (MySubprocess->needs_shmem)
 		PGSharedMemoryReAttach();
 	else
 		PGSharedMemoryNoReAttach();
 
 	/* autovacuum needs this set before calling InitProcess */
 	if (strcmp(argv[1], "--forkavlauncher") == 0)
-		AutovacuumLauncherIAm();
+		am_autovacuum_launcher = true;
 	if (strcmp(argv[1], "--forkavworker") == 0)
-		AutovacuumWorkerIAm();
+		am_autovacuum_worker = true;
 
 	/*
 	 * Start our win32 signal implementation. This has to be done after we
@@ -4985,8 +4943,7 @@ SubPostmasterMain(int argc, char *argv[])
 	 */
 	process_shared_preload_libraries();
 
-	/* Run backend or appropriate child */
-	if (strcmp(argv[1], "--forkbackend") == 0)
+	if (MySubprocessType == ClientBackendType)
 	{
 		Assert(argc == 3);		/* shouldn't be any more args */
 
@@ -5024,96 +4981,43 @@ SubPostmasterMain(int argc, char *argv[])
 		 */
 		BackendInitialize(&port);
 
-		/* Restore basic shared memory pointers */
-		InitShmemAccess(UsedShmemSegAddr);
-
-		/* Need a PGPROC to run CreateSharedMemoryAndSemaphores */
-		InitProcess();
-
-		/* Attach process to shared data structures */
-		CreateSharedMemoryAndSemaphores();
+		shmemSetup();
 
 		/* And run the backend */
 		BackendRun(&port);		/* does not return */
 	}
-	if (strcmp(argv[1], "--forkboot") == 0)
-	{
-		/* Restore basic shared memory pointers */
-		InitShmemAccess(UsedShmemSegAddr);
-
-		/* Need a PGPROC to run CreateSharedMemoryAndSemaphores */
-		InitAuxiliaryProcess();
-
-		/* Attach process to shared data structures */
-		CreateSharedMemoryAndSemaphores();
-
-		AuxiliaryProcessMain(argc - 2, argv + 2);	/* does not return */
-	}
-	if (strcmp(argv[1], "--forkavlauncher") == 0)
-	{
-		/* Restore basic shared memory pointers */
-		InitShmemAccess(UsedShmemSegAddr);
-
-		/* Need a PGPROC to run CreateSharedMemoryAndSemaphores */
-		InitProcess();
-
-		/* Attach process to shared data structures */
-		CreateSharedMemoryAndSemaphores();
-
-		AutoVacLauncherMain(argc - 2, argv + 2);	/* does not return */
-	}
-	if (strcmp(argv[1], "--forkavworker") == 0)
-	{
-		/* Restore basic shared memory pointers */
-		InitShmemAccess(UsedShmemSegAddr);
-
-		/* Need a PGPROC to run CreateSharedMemoryAndSemaphores */
-		InitProcess();
-
-		/* Attach process to shared data structures */
-		CreateSharedMemoryAndSemaphores();
-
-		AutoVacWorkerMain(argc - 2, argv + 2);	/* does not return */
-	}
-	if (strncmp(argv[1], "--forkbgworker=", 15) == 0)
-	{
-		int			shmem_slot;
 
-		/* do this as early as possible; in particular, before InitProcess() */
+	/* do this as early as possible; in particular, before InitProcess() */
+	if (MySubprocessType == BgWorkerType)
 		IsBackgroundWorker = true;
 
-		/* Restore basic shared memory pointers */
-		InitShmemAccess(UsedShmemSegAddr);
-
-		/* Need a PGPROC to run CreateSharedMemoryAndSemaphores */
-		InitProcess();
-
-		/* Attach process to shared data structures */
-		CreateSharedMemoryAndSemaphores();
+	shmemSetup();
 
-		/* Fetch MyBgworkerEntry from shared memory */
-		shmem_slot = atoi(argv[1] + 15);
-		MyBgworkerEntry = BackgroundWorkerEntry(shmem_slot);
-
-		StartBackgroundWorker();
-	}
-	if (strcmp(argv[1], "--forkarch") == 0)
+	if (MySubprocess->needs_aux_proc)
 	{
-		/* Do not want to attach to shared memory */
-
-		PgArchiverMain(argc, argv); /* does not return */
+		AuxiliaryProcessMain(argc-2, argv+2);
 	}
-	if (strcmp(argv[1], "--forkcol") == 0)
-	{
-		/* Do not want to attach to shared memory */
 
-		PgstatCollectorMain(argc, argv);	/* does not return */
-	}
-	if (strcmp(argv[1], "--forklog") == 0)
+	switch(MySubprocessType)
 	{
-		/* Do not want to attach to shared memory */
-
-		SysLoggerMain(argc, argv);	/* does not return */
+		case BgWorkerType:
+			/* Fetch MyBgworkerEntry from shared memory */
+			shmem_slot = atoi(argv[1] + 15);
+			MyBgworkerEntry = BackgroundWorkerEntry(shmem_slot);
+			/* fallthrough */
+		case PgArchiverType:
+		case PgstatCollectorType:
+		case SysLoggerType:
+			MySubprocess->entrypoint(argc, argv); /* no return */
+			break;
+		case AutoVacLauncherType:
+		case AutoVacWorkerType:
+			MySubprocess->entrypoint(argc-2, argv+2);
+			break;
+		default:
+			ereport(LOG,
+					(errmsg("could not start unknown process type (%d) under postmaster",
+							MySubprocessType)));
 	}
 
 	abort();					/* shouldn't get here */
@@ -5197,9 +5101,9 @@ sigusr1_handler(SIGNAL_ARGS)
 		 * we'll just try again later.
 		 */
 		Assert(CheckpointerPID == 0);
-		CheckpointerPID = StartCheckpointer();
+		CheckpointerPID = StartSubprocess(CheckpointerType);
 		Assert(BgWriterPID == 0);
-		BgWriterPID = StartBackgroundWriter();
+		BgWriterPID = StartSubprocess(BgWriterType);
 
 		/*
 		 * Start the archiver if we're responsible for (re-)archiving received
@@ -5207,7 +5111,7 @@ sigusr1_handler(SIGNAL_ARGS)
 		 */
 		Assert(PgArchPID == 0);
 		if (XLogArchivingAlways())
-			PgArchPID = pgarch_start();
+			PgArchPID = StartSubprocess(PgArchiverType);
 
 		/*
 		 * If we aren't planning to enter hot standby mode later, treat
@@ -5231,7 +5135,7 @@ sigusr1_handler(SIGNAL_ARGS)
 		 * Likewise, start other special children as needed.
 		 */
 		Assert(PgStatPID == 0);
-		PgStatPID = pgstat_start();
+		PgStatPID = StartSubprocess(PgstatCollectorType);
 
 		ereport(LOG,
 				(errmsg("database system is ready to accept read only connections")));
@@ -5378,7 +5282,7 @@ StartupPacketTimeoutHandler(void)
 /*
  * Generate a random cancel key.
  */
-static bool
+bool
 RandomCancelKey(int32 *cancel_key)
 {
 	return pg_strong_random(cancel_key, sizeof(int32));
@@ -5424,7 +5328,6 @@ CountChildren(int target)
 	return cnt;
 }
 
-
 /*
  * StartChildProcess -- start an auxiliary process for the postmaster
  *
@@ -5435,93 +5338,110 @@ CountChildren(int target)
  * to start subprocess.
  */
 static pid_t
-StartChildProcess(AuxProcType type)
+StartSubprocess(SubprocessType type)
 {
 	pid_t		pid;
-	char	   *av[10];
-	int			ac = 0;
+	char	   *argv[10];
+	int			argc = 0;
 	char		typebuf[32];
+#ifdef EXEC_BACKEND
+	char		forkname[32];
+#endif
 
-	/*
-	 * Set up command-line arguments for subprocess
-	 */
-	av[ac++] = "postgres";
+	/* This should be our first time in this function */
+	InitializeMySubprocess(type);
+
+	argv[argc++] = "postgres";
 
 #ifdef EXEC_BACKEND
-	av[ac++] = "--forkboot";
-	av[ac++] = NULL;			/* filled in by postmaster_forkexec */
+	snprintf(forkname, sizeof(forkname), "--%s", MySubprocess->progname);
+	argv[argc++] = psprintf("--%s", MySubprocess->progname);
+	argv[argc++] = NULL;
 #endif
 
 	snprintf(typebuf, sizeof(typebuf), "-x%d", type);
-	av[ac++] = typebuf;
+	argv[argc++] = typebuf;
 
-	av[ac] = NULL;
-	Assert(ac < lengthof(av));
+	argv[argc] = NULL;
+	Assert(argc < lengthof(argv));
+
+	/* Prep the subprocesses for a fork */
+	if (MySubprocess->init)
+	{
+		int rc = 0;
+		rc = MySubprocess->init(argc, argv);
+
+		if (rc != 0)
+			return 0;
+	}
 
 #ifdef EXEC_BACKEND
-	pid = postmaster_forkexec(ac, av);
-#else							/* !EXEC_BACKEND */
+	pid = postmaster_forkexec(argc, argv);
+#else
 	pid = fork_process();
+#endif
 
+	/* some processes like backends and bgworkers need the pid */
+	MyChildProcPid = pid;
+
+#ifndef EXEC_BACKEND
 	if (pid == 0)				/* child */
 	{
 		InitPostmasterChild();
+		ClosePostmasterPorts();
 
-		/* Close the postmaster's sockets */
-		ClosePostmasterPorts(false);
+		/*
+		 * Release postmaster's working memory context unless we're still using
+		 * it.
+		 */
+		if (!MySubprocess->keep_postmaster_memcontext)
+		{
+			MemoryContextSwitchTo(TopMemoryContext);
+			MemoryContextDelete(PostmasterContext);
+
+			PostmasterContext = NULL;
+		}
 
-		/* Release postmaster's working memory context */
-		MemoryContextSwitchTo(TopMemoryContext);
-		MemoryContextDelete(PostmasterContext);
-		PostmasterContext = NULL;
+		/* Call the process's main subroutine */
+		if (MySubprocess->needs_aux_proc)
+			AuxiliaryProcessMain(argc, argv);
+		else
+			MySubprocess->entrypoint(argc, argv);
 
-		AuxiliaryProcessMain(ac, av);
 		ExitPostmaster(0);
 	}
-#endif							/* EXEC_BACKEND */
-
+#endif
 	if (pid < 0)
 	{
 		/* in parent, fork failed */
-		int			save_errno = errno;
+		child_errno = errno;
 
-		errno = save_errno;
-		switch (type)
-		{
-			case StartupProcess:
-				ereport(LOG,
-						(errmsg("could not fork startup 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;
-		}
+		ereport(LOG,
+				(errmsg("could not fork %s: %m", MySubprocess->desc)));
 
 		/*
 		 * fork failure is fatal during startup, but there's no need to choke
 		 * immediately if starting other child types fails.
 		 */
-		if (type == StartupProcess)
-			ExitPostmaster(1);
+		if (MySubprocess->cleanup)
+		{
+			/*
+			 * Panic if the cleanup function tells us to. Otherwise, just bail
+			 * with -1.
+			 */
+			if (MySubprocess->cleanup(argc, argv))
+				ExitPostmaster(1);
+			else
+				return -1;
+		}
+
 		return 0;
 	}
+	else
+	{
+		if (MySubprocess->parent_main)
+			MySubprocess->parent_main(argc, argv);
+	}
 
 	/*
 	 * in parent, successful fork
@@ -5576,7 +5496,7 @@ StartAutovacuumWorker(void)
 			bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
 			bn->bgworker_notify = false;
 
-			bn->pid = StartAutoVacWorker();
+			bn->pid = StartSubprocess(AutoVacWorkerType);
 			if (bn->pid > 0)
 			{
 				bn->bkend_type = BACKEND_TYPE_AUTOVAC;
@@ -5637,7 +5557,7 @@ MaybeStartWalReceiver(void)
 		 pmState == PM_HOT_STANDBY || pmState == PM_WAIT_READONLY) &&
 		Shutdown == NoShutdown)
 	{
-		WalReceiverPID = StartWalReceiver();
+		WalReceiverPID = StartSubprocess(WalReceiverType);
 		if (WalReceiverPID != 0)
 			WalReceiverRequested = false;
 		/* else leave the flag set, so we'll try again later */
@@ -5756,124 +5676,6 @@ BackgroundWorkerUnblockSignals(void)
 	PG_SETMASK(&UnBlockSig);
 }
 
-#ifdef EXEC_BACKEND
-static pid_t
-bgworker_forkexec(int shmem_slot)
-{
-	char	   *av[10];
-	int			ac = 0;
-	char		forkav[MAXPGPATH];
-
-	snprintf(forkav, MAXPGPATH, "--forkbgworker=%d", shmem_slot);
-
-	av[ac++] = "postgres";
-	av[ac++] = forkav;
-	av[ac++] = NULL;			/* filled in by postmaster_forkexec */
-	av[ac] = NULL;
-
-	Assert(ac < lengthof(av));
-
-	return postmaster_forkexec(ac, av);
-}
-#endif
-
-/*
- * Start a new bgworker.
- * Starting time conditions must have been checked already.
- *
- * Returns true on success, false on failure.
- * In either case, update the RegisteredBgWorker's state appropriately.
- *
- * This code is heavily based on autovacuum.c, q.v.
- */
-static bool
-do_start_bgworker(RegisteredBgWorker *rw)
-{
-	pid_t		worker_pid;
-
-	Assert(rw->rw_pid == 0);
-
-	/*
-	 * Allocate and assign the Backend element.  Note we must do this before
-	 * forking, so that we can handle failures (out of memory or child-process
-	 * slots) cleanly.
-	 *
-	 * Treat failure as though the worker had crashed.  That way, the
-	 * postmaster will wait a bit before attempting to start it again; if we
-	 * tried again right away, most likely we'd find ourselves hitting the
-	 * same resource-exhaustion condition.
-	 */
-	if (!assign_backendlist_entry(rw))
-	{
-		rw->rw_crashed_at = GetCurrentTimestamp();
-		return false;
-	}
-
-	ereport(DEBUG1,
-			(errmsg("starting background worker process \"%s\"",
-					rw->rw_worker.bgw_name)));
-
-#ifdef EXEC_BACKEND
-	switch ((worker_pid = bgworker_forkexec(rw->rw_shmem_slot)))
-#else
-	switch ((worker_pid = fork_process()))
-#endif
-	{
-		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;
-			free(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;
-
-			StartBackgroundWorker();
-
-			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);
-#ifdef EXEC_BACKEND
-			ShmemBackendArrayAdd(rw->rw_backend);
-#endif
-			return true;
-	}
-
-	return false;
-}
-
 /*
  * Does the current postmaster state require starting a worker with the
  * specified start_time?
@@ -5914,67 +5716,6 @@ bgworker_should_start_now(BgWorkerStartTime start_time)
 	return false;
 }
 
-/*
- * Allocate the Backend struct for a connected background worker, but don't
- * add it to the list of backends just yet.
- *
- * On failure, return false without changing any worker state.
- *
- * Some info from the Backend is copied into the passed rw.
- */
-static bool
-assign_backendlist_entry(RegisteredBgWorker *rw)
-{
-	Backend    *bn;
-
-	/*
-	 * Check that database state allows another connection.  Currently the
-	 * only possible failure is CAC_TOOMANY, so we just log an error message
-	 * based on that rather than checking the error code precisely.
-	 */
-	if (canAcceptConnections(BACKEND_TYPE_BGWORKER) != CAC_OK)
-	{
-		ereport(LOG,
-				(errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
-				 errmsg("no slot available for new worker process")));
-		return false;
-	}
-
-	/*
-	 * Compute the cancel key that will be assigned to this session. We
-	 * probably don't need cancel keys for background workers, but we'd better
-	 * have something random in the field to prevent unfriendly people from
-	 * sending cancels to them.
-	 */
-	if (!RandomCancelKey(&MyCancelKey))
-	{
-		ereport(LOG,
-				(errcode(ERRCODE_INTERNAL_ERROR),
-				 errmsg("could not generate random cancel key")));
-		return false;
-	}
-
-	bn = malloc(sizeof(Backend));
-	if (bn == NULL)
-	{
-		ereport(LOG,
-				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("out of memory")));
-		return false;
-	}
-
-	bn->cancel_key = MyCancelKey;
-	bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
-	bn->bkend_type = BACKEND_TYPE_BGWORKER;
-	bn->dead_end = false;
-	bn->bgworker_notify = false;
-
-	rw->rw_backend = bn;
-	rw->rw_child_slot = bn->child_slot;
-
-	return true;
-}
-
 /*
  * If the time is right, start background worker(s).
  *
@@ -6079,7 +5820,8 @@ maybe_start_bgworkers(void)
 			 * crashed, but there's no need because the next run of this
 			 * function will do that.
 			 */
-			if (!do_start_bgworker(rw))
+			CurrentBgWorker = rw;
+			if (StartSubprocess(BgWorkerType) <= 0)
 			{
 				StartWorkerNeeded = true;
 				return;
@@ -6197,6 +5939,8 @@ save_backend_variables(BackendParameters *param, Port *port,
 	param->PgReloadTime = PgReloadTime;
 	param->first_syslogger_file_time = first_syslogger_file_time;
 
+	param->MySubprocessType = MySubprocessType;
+
 	param->redirection_done = redirection_done;
 	param->IsBinaryUpgrade = IsBinaryUpgrade;
 	param->max_safe_fds = max_safe_fds;
@@ -6432,6 +6176,8 @@ restore_backend_variables(BackendParameters *param, Port *port)
 	PgReloadTime = param->PgReloadTime;
 	first_syslogger_file_time = param->first_syslogger_file_time;
 
+	MySubprocessType = param->MySubprocessType;
+
 	redirection_done = param->redirection_done;
 	IsBinaryUpgrade = param->IsBinaryUpgrade;
 	max_safe_fds = param->max_safe_fds;
@@ -6486,7 +6232,7 @@ ShmemBackendArrayAllocation(void)
 	memset(ShmemBackendArray, 0, size);
 }
 
-static void
+void
 ShmemBackendArrayAdd(Backend *bn)
 {
 	/* The array slot corresponding to my PMChildSlot should be free */
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index c2250d7d4e..8957b341b9 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -127,13 +127,19 @@ HandleStartupProcInterrupts(void)
 		ProcessProcSignalBarrier();
 }
 
+bool
+StartupCleanup(int argc, char *argv[])
+{
+	/* Panic! */
+	return true;
+}
 
 /* ----------------------------------
  *	Startup Process main entry point
  * ----------------------------------
  */
 void
-StartupProcessMain(void)
+StartupProcessMain(int argc, char *argv[])
 {
 	/*
 	 * Properly accept or ignore signals the postmaster might send us.
diff --git a/src/backend/postmaster/subprocess.c b/src/backend/postmaster/subprocess.c
new file mode 100644
index 0000000000..1c91c5f0c3
--- /dev/null
+++ b/src/backend/postmaster/subprocess.c
@@ -0,0 +1,208 @@
+/*-------------------------------------------------------------------------
+ *
+ * subprocess.c
+ *
+ * Copyright (c) 2004-2020, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/postmaster/syslogger.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "postmaster/subprocess.h"
+#include "bootstrap/bootstrap.h"
+#include "postmaster/bgwriter.h"
+#include "postmaster/walwriter.h"
+#include "postmaster/syslogger.h"
+#include "postmaster/autovacuum.h"
+#include "postmaster/bgworker_internals.h"
+#include "postmaster/bgworker.h"
+#include "postmaster/postmaster.h"
+#include "postmaster/startup.h"
+#include "pgstat.h"
+#include "replication/walreceiver.h"
+
+PgSubprocess	*MySubprocess;
+
+PgSubprocess process_types[] = {
+	{
+		.progname = "boot",
+		.name = "checker",
+		.desc = "checker process",
+		.needs_shmem = true,
+		.needs_aux_proc = true,
+		.keep_postmaster_memcontext = false,
+		.init = NULL,
+		.entrypoint = CheckerModeMain,
+		.cleanup = NULL,
+		.parent_main = NULL
+	},
+	{
+		.progname = "boot",
+		.name = "bootstrap",
+		.desc = "bootstrap process",
+		.needs_shmem = true,
+		.needs_aux_proc = true,
+		.keep_postmaster_memcontext = false,
+		.init = NULL,
+		.entrypoint = BootstrapModeMain,
+		.cleanup = NULL,
+		.parent_main = NULL
+	},
+	{
+		.progname = "forkboot",
+		.name = "startup",
+		.desc = "startup process",
+		.needs_shmem = true,
+		.needs_aux_proc = true,
+		.keep_postmaster_memcontext = false,
+		.init = NULL,
+		.entrypoint = StartupProcessMain,
+		.cleanup = StartupCleanup,
+		.parent_main = NULL
+	},
+	{
+		.progname = "forkboot",
+		.name = "bgwriter",
+		.desc = "background writer process",
+		.needs_shmem = true,
+		.needs_aux_proc = true,
+		.keep_postmaster_memcontext = false,
+		.init = NULL,
+		.entrypoint = BackgroundWriterMain,
+		.cleanup = NULL,
+		.parent_main = NULL
+	},
+	{
+		.progname = "forkboot",
+		.name = "checkpointer",
+		.desc = "checkpointer process",
+		.needs_shmem = true,
+		.needs_aux_proc = true,
+		.keep_postmaster_memcontext = false,
+		.init = NULL,
+		.entrypoint = CheckpointerMain,
+		.cleanup = NULL,
+		.parent_main = NULL
+	},
+	{
+		.progname = "forkboot",
+		.name = "walwriter",
+		.desc = "WAL writer process",
+		.needs_shmem = true,
+		.needs_aux_proc = true,
+		.keep_postmaster_memcontext = false,
+		.init = NULL,
+		.entrypoint = WalWriterMain,
+		.cleanup = NULL,
+		.parent_main = NULL
+	},
+	{
+		.progname = "forkboot",
+		.name = "walreceiver",
+		.desc = "WAL receiver process",
+		.needs_shmem = true,
+		.needs_aux_proc = true,
+		.keep_postmaster_memcontext = false,
+		.init = NULL,
+		.entrypoint = WalReceiverMain,
+		.cleanup = NULL,
+		.parent_main = NULL
+	},
+	{
+		.progname = "forkavlauncher",
+		.name = "autovacuum launcher",
+		.desc = "autovacuum launcher process",
+		.needs_shmem = true,
+		.needs_aux_proc = false,
+		.keep_postmaster_memcontext = false,
+		.init = NULL,
+		.entrypoint = AutoVacLauncherMain,
+		.cleanup = NULL,
+		.parent_main = NULL
+	},
+	{
+		.progname = "forkavworker",
+		.name = "autovacuum worker",
+		.desc = "autovacuum worker process",
+		.needs_shmem = true,
+		.needs_aux_proc = false,
+		.keep_postmaster_memcontext = false,
+		.init = NULL,
+		.entrypoint = AutoVacWorkerMain,
+		.cleanup = NULL,
+		.parent_main = NULL
+	},
+	{
+		.progname = "forkcol",
+		.name = "pgstat collector",
+		.desc = "statistics collector process",
+		.needs_shmem = true,
+		.needs_aux_proc = false,
+		.keep_postmaster_memcontext = false,
+		.init = PgstatCollectorPrep,
+		.entrypoint = PgstatCollectorMain,
+		.cleanup = NULL,
+		.parent_main = NULL
+	},
+	{
+		.progname = "forkarch",
+		.name = "archiver",
+		.desc = "archiver process",
+		.needs_shmem = false,
+		.needs_aux_proc = false,
+		.keep_postmaster_memcontext = false,
+		.init = PgArchiverPrep,
+		.entrypoint = PgArchiverMain,
+		.cleanup = NULL,
+		.parent_main = NULL
+	},
+	{
+		.progname = "forklog",
+		.name = "logger",
+		.desc = "system logger",
+		.needs_shmem = false,
+		.needs_aux_proc = false,
+		.keep_postmaster_memcontext = true,
+		.init = SysLoggerPrep,
+		.entrypoint = SysLoggerMain,
+		.cleanup = NULL,
+		.parent_main = SysLoggerParentMain
+	},
+	{
+		.progname = "forkbgworker",
+		.name = "bgworker",
+		.desc = "background worker",
+		.needs_shmem = true,
+		.needs_aux_proc = false,
+		.keep_postmaster_memcontext = true,
+		.init = BgWorkerPrep,
+		.entrypoint = BackgroundWorkerMain,
+		.cleanup = BackgroundWorkerCleanup,
+		.parent_main = BackgroundWorkerParentMain
+	},
+	{
+		.progname = "forkbackend",
+		.name = "backend",
+		.desc = "client backend",
+		.needs_shmem = true,
+		.needs_aux_proc = false,
+		.keep_postmaster_memcontext = true,
+		.init = BackendPrep,
+		.entrypoint = BackendMain,
+		.cleanup = BackendCleanup,
+		.parent_main = BackendParentMain
+	}
+};
+
+void
+InitializeMySubprocess(SubprocessType type)
+{
+	/* Globally set subprocess information */
+	MySubprocessType = type;
+	MySubprocess = (PgSubprocess *)malloc(sizeof(PgSubprocess));
+
+	memcpy(MySubprocess, &process_types[type], sizeof(PgSubprocess));
+}
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index cf7b535e4e..59497e829b 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -48,6 +48,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"
 
@@ -93,6 +94,7 @@ NON_EXEC_STATIC pg_time_t first_syslogger_file_time = 0;
 static char *last_file_name = NULL;
 static char *last_csv_file_name = NULL;
 
+
 /*
  * Buffers for saving partial messages from different backends.
  *
@@ -131,13 +133,11 @@ static CRITICAL_SECTION sysloggerSection;
 static volatile sig_atomic_t got_SIGHUP = false;
 static volatile sig_atomic_t rotation_requested = false;
 
-
 /* Local subroutines */
 #ifdef EXEC_BACKEND
-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,
@@ -158,7 +158,7 @@ static void update_metainfo_datafile(void);
  * Main entry point for syslogger process
  * argc/argv parameters are valid only in EXEC_BACKEND case.
  */
-NON_EXEC_STATIC void
+void
 SysLoggerMain(int argc, char *argv[])
 {
 #ifndef WIN32
@@ -171,12 +171,16 @@ SysLoggerMain(int argc, char *argv[])
 	pg_time_t	now;
 	WaitEventSet *wes;
 
-	now = MyStartTime;
-
 #ifdef EXEC_BACKEND
 	syslogger_parseArgs(argc, argv);
+#else
+	/* Drop our connection to postmaster's shared memory, as well */
+	dsm_detach_all();
+	PGSharedMemoryDetach();
 #endif							/* EXEC_BACKEND */
 
+	now = MyStartTime;
+
 	am_syslogger = true;
 
 	init_ps_display("logger", "", "", "");
@@ -540,16 +544,40 @@ SysLoggerMain(int argc, char *argv[])
 }
 
 /*
- * Postmaster subroutine to start a syslogger subprocess.
+ * Helper function for setting up a file number buffer
+ */
+static void
+write_filenobuf(char *filenobuf, FILE *file)
+{
+	/* static variables (those not passed by write_backend_variables) */
+#ifndef WIN32
+	if (file != NULL)
+		snprintf(filenobuf, sizeof(filenobuf), "%d", fileno(file));
+	else
+		strcpy(filenobuf, "-1");
+#else						   /* WIN32 */
+	if (file != NULL)
+		snprintf(filenobuf, sizeof(filenobuf), "%ld", (long) _get_osfhandle(_fileno(file)));
+	else
+		strcpy(filenobuf, "-1");
+#endif						  /* WIN32 */
+}
+
+/*
+ * SysLoggerPrep
+ *
+ * Postmaster subroutine to prepare a syslogger subprocess.
  */
 int
-SysLogger_Start(void)
+SysLoggerPrep(int argc, char *argv[])
 {
-	pid_t		sysloggerPid;
 	char	   *filename;
+	char		filenobuf[32];
+	char		csvfilenobuf[32];
 
+	/* Tell StartupSubprocess not to continue forking */
 	if (!Logging_collector)
-		return 0;
+		return -1;
 
 	/*
 	 * If first time through, create the pipe which will receive stderr
@@ -610,11 +638,9 @@ SysLogger_Start(void)
 	 * a time-based rotation.
 	 */
 	first_syslogger_file_time = time(NULL);
-
 	filename = logfile_getname(first_syslogger_file_time, NULL);
-
 	syslogFile = logfile_open(filename, "a", false);
-
+	write_filenobuf(filenobuf, syslogFile);
 	pfree(filename);
 
 	/*
@@ -625,173 +651,97 @@ SysLogger_Start(void)
 	if (Log_destination & LOG_DESTINATION_CSVLOG)
 	{
 		filename = logfile_getname(first_syslogger_file_time, ".csv");
-
 		csvlogFile = logfile_open(filename, "a", false);
-
+		write_filenobuf(csvfilenobuf, csvlogFile);
 		pfree(filename);
 	}
 
-#ifdef EXEC_BACKEND
-	switch ((sysloggerPid = syslogger_forkexec()))
-#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();
-
-			/* do the work */
-			SysLoggerMain(0, NULL);
-			break;
-#endif
-
-		default:
-			/* success, in postmaster */
-
-			/* now we redirect stderr, if not done already */
-			if (!redirection_done)
-			{
-#ifdef WIN32
-				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)));
-
-#ifndef WIN32
-				fflush(stdout);
-				if (dup2(syslogPipe[1], fileno(stdout)) < 0)
-					ereport(FATAL,
-							(errcode_for_file_access(),
-							 errmsg("could not redirect stdout: %m")));
-				fflush(stderr);
-				if (dup2(syslogPipe[1], fileno(stderr)) < 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, _fileno(stderr)) < 0)
-					ereport(FATAL,
-							(errcode_for_file_access(),
-							 errmsg("could not redirect stderr: %m")));
-				close(fd);
-				_setmode(_fileno(stderr), _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;
-#endif
-				redirection_done = true;
-			}
+	argv[argc++] = filenobuf;
+	argv[argc++] = csvfilenobuf;
 
-			/* postmaster will never write the file(s); close 'em */
-			fclose(syslogFile);
-			syslogFile = NULL;
-			if (csvlogFile != NULL)
-			{
-				fclose(csvlogFile);
-				csvlogFile = NULL;
-			}
-			return (int) sysloggerPid;
-	}
+	argv[argc] = NULL;
+	Assert(argc < lengthof(argv));
 
-	/* we should never reach here */
 	return 0;
 }
 
-
-#ifdef EXEC_BACKEND
-
 /*
- * syslogger_forkexec() -
+ * SysLoggerParentMain
  *
- * Format up the arglist for, then fork and exec, a syslogger process
+ * Fallthrough subroutine for parent running syslogger.
  */
-static pid_t
-syslogger_forkexec(void)
+void
+SysLoggerParentMain(int argc, char *argv[])
 {
-	char	   *av[10];
-	int			ac = 0;
-	char		filenobuf[32];
-	char		csvfilenobuf[32];
+	/* now we redirect stderr, if not done already */
+	if (!redirection_done)
+	{
+#ifdef WIN32
+		int			fd;
+#endif
 
-	av[ac++] = "postgres";
-	av[ac++] = "--forklog";
-	av[ac++] = NULL;			/* filled in by postmaster_forkexec */
+		/*
+		 * 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)));
 
-	/* static variables (those not passed by write_backend_variables) */
 #ifndef WIN32
-	if (syslogFile != NULL)
-		snprintf(filenobuf, sizeof(filenobuf), "%d",
-				 fileno(syslogFile));
-	else
-		strcpy(filenobuf, "-1");
-#else							/* WIN32 */
-	if (syslogFile != NULL)
-		snprintf(filenobuf, sizeof(filenobuf), "%ld",
-				 (long) _get_osfhandle(_fileno(syslogFile)));
-	else
-		strcpy(filenobuf, "0");
-#endif							/* WIN32 */
-	av[ac++] = filenobuf;
+		fflush(stdout);
+		if (dup2(syslogPipe[1], fileno(stdout)) < 0)
+			ereport(FATAL,
+					(errcode_for_file_access(),
+					 errmsg("could not redirect stdout: %m")));
+		fflush(stderr);
+		if (dup2(syslogPipe[1], fileno(stderr)) < 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
 
-#ifndef WIN32
-	if (csvlogFile != NULL)
-		snprintf(csvfilenobuf, sizeof(csvfilenobuf), "%d",
-				 fileno(csvlogFile));
-	else
-		strcpy(csvfilenobuf, "-1");
-#else							/* WIN32 */
-	if (csvlogFile != NULL)
-		snprintf(csvfilenobuf, sizeof(csvfilenobuf), "%ld",
-				 (long) _get_osfhandle(_fileno(csvlogFile)));
-	else
-		strcpy(csvfilenobuf, "0");
-#endif							/* WIN32 */
-	av[ac++] = csvfilenobuf;
+		/*
+		 * 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, _fileno(stderr)) < 0)
+			ereport(FATAL,
+					(errcode_for_file_access(),
+					 errmsg("could not redirect stderr: %m")));
+		close(fd);
+		_setmode(_fileno(stderr), _O_BINARY);
 
-	av[ac] = NULL;
-	Assert(ac < lengthof(av));
+		/*
+		 * 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;
+	}
 
-	return postmaster_forkexec(ac, av);
+	/* postmaster will never write the file(s); close 'em */
+	fclose(syslogFile);
+	syslogFile = NULL;
+	if (csvlogFile != NULL)
+	{
+		fclose(csvlogFile);
+		csvlogFile = NULL;
+	}
 }
 
+#ifdef EXEC_BACKEND
 /*
  * syslogger_parseArgs() -
  *
@@ -800,7 +750,7 @@ syslogger_forkexec(void)
 static void
 syslogger_parseArgs(int argc, char *argv[])
 {
-	int			fd;
+	int		 fd;
 
 	Assert(argc == 5);
 	argv += 3;
@@ -825,7 +775,7 @@ syslogger_parseArgs(int argc, char *argv[])
 		csvlogFile = fdopen(fd, "a");
 		setvbuf(csvlogFile, NULL, PG_IOLBF, 0);
 	}
-#else							/* WIN32 */
+#else						   /* WIN32 */
 	fd = atoi(*argv++);
 	if (fd != 0)
 	{
@@ -846,10 +796,9 @@ syslogger_parseArgs(int argc, char *argv[])
 			setvbuf(csvlogFile, NULL, PG_IOLBF, 0);
 		}
 	}
-#endif							/* WIN32 */
+#endif						  /* WIN32 */
 }
-#endif							/* EXEC_BACKEND */
-
+#endif
 
 /* --------------------------------
  *		pipe protocol handling
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 45a2757969..3a0cec3371 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -85,7 +85,7 @@ int			WalWriterFlushAfter = 128;
  * basic execution environment, but not enabled signals yet.
  */
 void
-WalWriterMain(void)
+WalWriterMain(int argc, char *argv[])
 {
 	sigjmp_buf	local_sigjmp_buf;
 	MemoryContext walwriter_context;
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 2ab15c3cbb..725023f0f6 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -164,7 +164,7 @@ ProcessWalRcvInterrupts(void)
 
 /* Main entry point for walreceiver process */
 void
-WalReceiverMain(void)
+WalReceiverMain(int argc, char *argv[])
 {
 	char		conninfo[MAXCONNINFO];
 	char	   *tmp_conninfo;
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 7e6a3c1774..ec0f34d582 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -24,6 +24,7 @@
 #include "pgstat.h"
 #include "postmaster/bgworker_internals.h"
 #include "postmaster/postmaster.h"
+#include "postmaster/subprocess.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
 #include "utils/acl.h"
@@ -691,7 +692,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 
 			proc = BackendPidGetProc(beentry->st_procpid);
 
-			if (proc == NULL && (beentry->st_backendType != B_BACKEND))
+			if (proc == NULL && (MySubprocessType != ClientBackendType))
 			{
 				/*
 				 * For an auxiliary process, retrieve process info from
@@ -739,7 +740,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 			 * date.
 			 */
 			if (beentry->st_xact_start_timestamp != 0 &&
-				beentry->st_backendType != B_WAL_SENDER)
+				MySubprocessType != WalSenderType)
 				values[8] = TimestampTzGetDatum(beentry->st_xact_start_timestamp);
 			else
 				nulls[8] = true;
@@ -827,7 +828,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 				}
 			}
 			/* Add backend type */
-			if (beentry->st_backendType == B_BG_WORKER)
+			if (MySubprocessType == BgWorkerType)
 			{
 				const char *bgw_type;
 
@@ -839,7 +840,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 			}
 			else
 				values[17] =
-					CStringGetTextDatum(pgstat_get_backend_desc(beentry->st_backendType));
+					CStringGetTextDatum(MySubprocess->desc);
 
 			/* SSL information */
 			if (beentry->st_ssl)
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index eb19644419..3e7be0bfcd 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -38,9 +38,10 @@ volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
 
 int			MyProcPid;
+int			MyChildProcPid;
 pg_time_t	MyStartTime;
 TimestampTz MyStartTimestamp;
-struct Port *MyProcPort;
+struct Port *MyProcPort = NULL;
 int32		MyCancelKey;
 int			MyPMChildSlot;
 
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 8a47dcdcb1..91e43382c0 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -1112,7 +1112,7 @@ process_startup_options(Port *port, bool am_superuser)
 		av = (char **) palloc(maxac * sizeof(char *));
 		ac = 0;
 
-		av[ac++] = "postgres";
+		av[ac++] = pstrdup("postgres");
 
 		pg_split_opts(av, &ac, port->cmdline_options);
 
diff --git a/src/include/bootstrap/bootstrap.h b/src/include/bootstrap/bootstrap.h
index b67fa9acc1..5d851e0081 100644
--- a/src/include/bootstrap/bootstrap.h
+++ b/src/include/bootstrap/bootstrap.h
@@ -15,7 +15,7 @@
 #define BOOTSTRAP_H
 
 #include "nodes/execnodes.h"
-
+#include "postmaster/fork_process.h"
 
 /*
  * MAXATTR is the maximum number of attributes in a relation supported
@@ -31,6 +31,8 @@ extern Relation boot_reldesc;
 extern Form_pg_attribute attrtypes[MAXATTR];
 extern int	numattr;
 
+extern void CheckerModeMain(int argc, char *argv[]);
+extern void BootstrapModeMain(int argc, char *argv[]);
 
 extern void AuxiliaryProcessMain(int argc, char *argv[]) pg_attribute_noreturn();
 
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index f985453ec3..823f5258d8 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -163,6 +163,7 @@ extern PGDLLIMPORT int max_worker_processes;
 extern PGDLLIMPORT int max_parallel_workers;
 
 extern PGDLLIMPORT int MyProcPid;
+extern PGDLLIMPORT int MyChildProcPid;
 extern PGDLLIMPORT pg_time_t MyStartTime;
 extern PGDLLIMPORT TimestampTz MyStartTimestamp;
 extern PGDLLIMPORT struct Port *MyProcPort;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 3a65a51696..4e77a86943 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -1048,9 +1048,6 @@ typedef struct PgBackendStatus
 	/* The entry is valid iff st_procpid > 0, unused if st_procpid == 0 */
 	int			st_procpid;
 
-	/* Type of backends */
-	BackendType st_backendType;
-
 	/* Times when current backend, transaction, and activity started */
 	TimestampTz st_proc_start_timestamp;
 	TimestampTz st_xact_start_timestamp;
@@ -1237,13 +1234,12 @@ extern Size BackendStatusShmemSize(void);
 extern void CreateSharedBackendStatus(void);
 
 extern void pgstat_init(void);
-extern int	pgstat_start(void);
 extern void pgstat_reset_all(void);
 extern void allow_immediate_pgstat_restart(void);
 
-#ifdef EXEC_BACKEND
+/* Stat collector subprocess startup functions */
+extern int PgstatCollectorPrep(int argc, char *argv[]);
 extern void PgstatCollectorMain(int argc, char *argv[]) pg_attribute_noreturn();
-#endif
 
 
 /* ----------
diff --git a/src/include/postmaster/autovacuum.h b/src/include/postmaster/autovacuum.h
index d40ed55531..dc02de745e 100644
--- a/src/include/postmaster/autovacuum.h
+++ b/src/include/postmaster/autovacuum.h
@@ -45,6 +45,10 @@ extern int	AutovacuumLauncherPid;
 
 extern int	Log_autovacuum_min_duration;
 
+/* autovacuum identification */
+extern bool am_autovacuum_launcher;
+extern bool am_autovacuum_worker;
+
 /* Status inquiry functions */
 extern bool AutoVacuumingActive(void);
 extern bool IsAutoVacuumLauncherProcess(void);
@@ -58,18 +62,15 @@ extern void autovac_init(void);
 extern int	StartAutoVacLauncher(void);
 extern int	StartAutoVacWorker(void);
 
+extern void AutoVacWorkerMain(int argc, char *argv[]);
+extern void AutoVacLauncherMain(int argc, char *argv[]);
+
 /* called from postmaster when a worker could not be forked */
 extern void AutoVacWorkerFailed(void);
 
 /* autovacuum cost-delay balancer */
 extern void AutoVacuumUpdateDelay(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 bool AutoVacuumRequestWork(AutoVacuumWorkItemType type,
 								  Oid relationId, BlockNumber blkno);
diff --git a/src/include/postmaster/bgworker_internals.h b/src/include/postmaster/bgworker_internals.h
index f7e24664d5..366bbcf1e3 100644
--- a/src/include/postmaster/bgworker_internals.h
+++ b/src/include/postmaster/bgworker_internals.h
@@ -43,6 +43,7 @@ typedef struct RegisteredBgWorker
 } RegisteredBgWorker;
 
 extern slist_head BackgroundWorkerList;
+extern RegisteredBgWorker *CurrentBgWorker;
 
 extern Size BackgroundWorkerShmemSize(void);
 extern void BackgroundWorkerShmemInit(void);
@@ -54,7 +55,11 @@ extern void BackgroundWorkerStopNotifications(pid_t pid);
 extern void ResetBackgroundWorkerCrashTimes(void);
 
 /* Function to start a background worker, called from postmaster.c */
-extern void StartBackgroundWorker(void) pg_attribute_noreturn();
+extern int BgWorkerPrep(int argc, char *argv[]);
+extern void BackgroundWorkerMain(int argc, char *argv[]);
+extern void BackgroundWorkerParentMain(int argc, char *argv[]);
+extern bool BackgroundWorkerCleanup(int argc, char *argv[]);
+
 
 #ifdef EXEC_BACKEND
 extern BackgroundWorker *BackgroundWorkerEntry(int slotno);
diff --git a/src/include/postmaster/bgwriter.h b/src/include/postmaster/bgwriter.h
index 0a5708b32e..6fb1ee1d6d 100644
--- a/src/include/postmaster/bgwriter.h
+++ b/src/include/postmaster/bgwriter.h
@@ -27,8 +27,8 @@ extern int	CheckPointTimeout;
 extern int	CheckPointWarning;
 extern double CheckPointCompletionTarget;
 
-extern void BackgroundWriterMain(void) pg_attribute_noreturn();
-extern void CheckpointerMain(void) pg_attribute_noreturn();
+extern void BackgroundWriterMain(int argc, char *argv[]) pg_attribute_noreturn();
+extern void CheckpointerMain(int argc, char *argv[]) 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
index 8cb568fc63..a42f859a08 100644
--- a/src/include/postmaster/fork_process.h
+++ b/src/include/postmaster/fork_process.h
@@ -12,6 +12,12 @@
 #ifndef FORK_PROCESS_H
 #define FORK_PROCESS_H
 
+#include "postmaster.h"
+
 extern pid_t fork_process(void);
 
+#ifdef EXEC_BACKEND
+extern pid_t postmaster_forkexec(int argc, char *argvp[]);
+#endif
+
 #endif							/* FORK_PROCESS_H */
diff --git a/src/include/postmaster/pgarch.h b/src/include/postmaster/pgarch.h
index b3200874ca..ba5b012839 100644
--- a/src/include/postmaster/pgarch.h
+++ b/src/include/postmaster/pgarch.h
@@ -26,14 +26,9 @@
 #define MAX_XFN_CHARS	40
 #define VALID_XFN_CHARS "0123456789ABCDEF.history.backup.partial"
 
-/* ----------
- * Functions called from postmaster
- * ----------
- */
-extern int	pgarch_start(void);
 
-#ifdef EXEC_BACKEND
+/* Archiver subprocess startup functions */
+extern int PgArchiverPrep(int argc, char *argv[]);
 extern void PgArchiverMain(int argc, char *argv[]) pg_attribute_noreturn();
-#endif
 
 #endif							/* _PGARCH_H */
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index babc87dfc9..d4c0f46220 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -13,6 +13,41 @@
 #ifndef _POSTMASTER_H
 #define _POSTMASTER_H
 
+#include "lib/ilist.h"
+
+/*
+ * List of active backends (or child processes anyway; we don't actually
+ * know whether a given child has become a backend or is still in the
+ * authorization phase).  This is used mainly to keep track of how many
+ * children we have and send them appropriate signals when necessary.
+ *
+ * "Special" children such as the startup, bgwriter and autovacuum launcher
+ * tasks are not in this list.  Autovacuum worker and walsender are in it.
+ * Also, "dead_end" children are in it: these are children launched just for
+ * the purpose of sending a friendly rejection message to a would-be client.
+ * We must track them because they are attached to shared memory, but we know
+ * they will never become live backends.  dead_end children are not assigned a
+ * PMChildSlot.
+ *
+ * Background workers are in this list, too.
+ */
+typedef struct bkend
+{
+	pid_t		pid;			/* process id of backend */
+	int32		cancel_key;		/* cancel key for cancels for this backend */
+	int			child_slot;		/* PMChildSlot for this backend, if any */
+
+	/*
+	 * Flavor of backend or auxiliary process.  Note that BACKEND_TYPE_WALSND
+	 * backends initially announce themselves as BACKEND_TYPE_NORMAL, so if
+	 * bkend_type is normal, you should check for a recent transition.
+	 */
+	int			bkend_type;
+	bool		dead_end;		/* is it going to send an error and quit? */
+	bool		bgworker_notify;	/* gets bgworker start/stop notifications */
+	dlist_node	elem;			/* list link in BackendList */
+} Backend;
+
 /* GUC options */
 extern bool EnableSSL;
 extern int	ReservedBackends;
@@ -47,21 +82,31 @@ extern int	postmaster_alive_fds[2];
 extern PGDLLIMPORT const char *progname;
 
 extern void PostmasterMain(int argc, char *argv[]) pg_attribute_noreturn();
-extern void ClosePostmasterPorts(bool am_syslogger);
+extern void ClosePostmasterPorts(void);
 extern void InitProcessGlobals(void);
 
 extern int	MaxLivePostmasterChildren(void);
 
 extern bool PostmasterMarkPIDForWorkerNotify(int);
 
+extern bool RandomCancelKey(int32 *cancel_key);
+
+extern int BackendPrep(int argc, char *argv[]);
+extern void BackendMain(int argc, char *argv[]);
+extern void BackendParentMain(int argc, char *argv[]);
+extern bool BackendCleanup(int argc, char *argv[]);
+extern void BackgroundWorkerMain(int argc, char *argv[]);
+
 #ifdef EXEC_BACKEND
-extern pid_t postmaster_forkexec(int argc, char *argv[]);
 extern void SubPostmasterMain(int argc, char *argv[]) pg_attribute_noreturn();
 
 extern Size ShmemBackendArraySize(void);
 extern void ShmemBackendArrayAllocation(void);
+extern void ShmemBackendArrayAdd(Backend *bn);
 #endif
 
+extern PGDLLIMPORT dlist_head BackendList;
+
 /*
  * 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
@@ -74,4 +119,17 @@ extern void ShmemBackendArrayAllocation(void);
  */
 #define MAX_BACKENDS	0x3FFFF
 
+/*
+ * Possible types of a backend. Beyond being the possible bkend_type values in
+ * struct bkend, these are OR-able request flag bits for SignalSomeChildren()
+ * and CountChildren().
+ */
+#define BACKEND_TYPE_NORMAL		0x0001	/* normal backend */
+#define BACKEND_TYPE_AUTOVAC	0x0002	/* autovacuum worker process */
+#define BACKEND_TYPE_WALSND		0x0004	/* walsender process */
+#define BACKEND_TYPE_BGWORKER	0x0008	/* bgworker process */
+#define BACKEND_TYPE_ALL		0x000F	/* OR of all the above */
+
+#define BACKEND_TYPE_WORKER		(BACKEND_TYPE_AUTOVAC | BACKEND_TYPE_BGWORKER)
+
 #endif							/* _POSTMASTER_H */
diff --git a/src/include/postmaster/startup.h b/src/include/postmaster/startup.h
index 9f59c1ffa3..14a646136f 100644
--- a/src/include/postmaster/startup.h
+++ b/src/include/postmaster/startup.h
@@ -13,10 +13,13 @@
 #define _STARTUP_H
 
 extern void HandleStartupProcInterrupts(void);
-extern void StartupProcessMain(void) pg_attribute_noreturn();
 extern void PreRestoreCommand(void);
 extern void PostRestoreCommand(void);
 extern bool IsPromoteTriggered(void);
 extern void ResetPromoteTriggered(void);
 
+/* Startup subprocess functions */
+extern void StartupProcessMain(int argc, char *argv[]) pg_attribute_noreturn();
+extern bool StartupCleanup(int argc, char *argv[]);
+
 #endif							/* _STARTUP_H */
diff --git a/src/include/postmaster/subprocess.h b/src/include/postmaster/subprocess.h
new file mode 100644
index 0000000000..b65fc75475
--- /dev/null
+++ b/src/include/postmaster/subprocess.h
@@ -0,0 +1,66 @@
+/*-------------------------------------------------------------------------
+ *
+ * subprocess.h
+ *
+ * Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ *
+ * src/include/postmaster/subprocess.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SUBPROCESS_H
+#define SUBPROCESS_H
+
+#include "postmaster.h"
+
+typedef enum
+{
+	NoProcessType = -1,
+	CheckerType = 0,
+	BootstrapType,
+	StartupType,
+	BgWriterType,
+	CheckpointerType,
+	WalWriterType,
+	WalReceiverType,	/* end of Auxiliary Process Forks */
+	AutoVacLauncherType,
+	AutoVacWorkerType,
+	PgstatCollectorType,
+	PgArchiverType,
+	SysLoggerType,
+	BgWorkerType,
+	ClientBackendType,
+
+	WalSenderType,	/* Dummy type for wal sender. No process struct needed as
+					 * this process just takes over an existing bgworker */
+
+	NUMSUBPROCESSTYPES			/* Must be last! */
+} SubprocessType;
+
+typedef int (*SubprocessInit) (int argc, char *argv[]);
+typedef void (*SubprocessEntryPoint) (int argc, char *argv[]);
+typedef bool (*SubprocessCleanup) (int argc, char *argv[]);
+typedef void (*SubprocessParent) (int argc, char *argv[]);
+
+/* Current subprocess initializer */
+extern void InitializeMySubprocess(SubprocessType type);
+
+typedef struct PgSubprocess
+{
+	const char			   *progname;
+	const char			   *name;
+	const char			   *desc;
+	bool					needs_shmem;
+	bool					needs_aux_proc;
+	bool					needs_full_proc;
+	bool					keep_postmaster_memcontext;
+	SubprocessInit			init;
+	SubprocessEntryPoint	entrypoint;
+	SubprocessCleanup		cleanup;
+	SubprocessParent		parent_main;
+} PgSubprocess;
+
+extern SubprocessType				MySubprocessType;
+extern PGDLLIMPORT PgSubprocess	   *MySubprocess;
+
+#endif							/* SUBPROCESS_H */
diff --git a/src/include/postmaster/syslogger.h b/src/include/postmaster/syslogger.h
index 9b7a386dbd..ededfd7c23 100644
--- a/src/include/postmaster/syslogger.h
+++ b/src/include/postmaster/syslogger.h
@@ -14,7 +14,6 @@
 
 #include <limits.h>				/* for PIPE_BUF */
 
-
 /*
  * Primitive protocol structure for writing to syslogger pipe(s).  The idea
  * here is to divide long messages into chunks that are not more than
@@ -83,9 +82,10 @@ extern int	SysLogger_Start(void);
 
 extern void write_syslogger_file(const char *buffer, int count, int dest);
 
-#ifdef EXEC_BACKEND
-extern void SysLoggerMain(int argc, char *argv[]) pg_attribute_noreturn();
-#endif
+/* Subprocess startup functions */
+extern int SysLoggerPrep(int argc, char *argv[]);
+extern void SysLoggerParentMain(int argc, char *argv[]);
+extern void SysLoggerMain(int argc, char *argv[]);
 
 extern bool CheckLogrotateSignal(void);
 extern void RemoveLogrotateSignalFiles(void);
diff --git a/src/include/postmaster/walwriter.h b/src/include/postmaster/walwriter.h
index 011dc26a6e..596c437c54 100644
--- a/src/include/postmaster/walwriter.h
+++ b/src/include/postmaster/walwriter.h
@@ -16,6 +16,6 @@
 extern int	WalWriterDelay;
 extern int	WalWriterFlushAfter;
 
-extern void WalWriterMain(void) pg_attribute_noreturn();
+extern void WalWriterMain(int argc, char *argv[]) pg_attribute_noreturn();
 
 #endif							/* _WALWRITER_H */
diff --git a/src/include/replication/walreceiver.h b/src/include/replication/walreceiver.h
index e08afc6548..2025f19ab3 100644
--- a/src/include/replication/walreceiver.h
+++ b/src/include/replication/walreceiver.h
@@ -311,7 +311,7 @@ walrcv_clear_result(WalRcvExecResult *walres)
 }
 
 /* prototypes for functions in walreceiver.c */
-extern void WalReceiverMain(void) pg_attribute_noreturn();
+extern void WalReceiverMain(int argc, char *argv[]);
 extern void ProcessWalRcvInterrupts(void);
 
 /* prototypes for functions in walreceiverfuncs.c */
-- 
2.21.0

Reply via email to