游戏建设网站海宁市建设局官方网站6
postgres源码学习之登录
- 进程概览
- 接受新的连接
- 新增进程
- 代码示例
- 子进程处理逻辑
- 权限认证
- 等待sql输入
进程概览
当执行完pg_ctl -l logfile start后,postgres将运行以下进程
- /usr/local/pgsql/bin/postgres
- postgres: checkpointer
- postgres: background writer
- postgres: walwriter
- postgres: autovacuum launcher
- postgres: logical replication launcher
查询进程状态如下:
其中第一个进程/usr/local/pgsql/bin/postgres为master,后文中会用master指代该进程,其会监听5432端口及本地socket接收新的连接;
参考postgres文档
接受新的连接
新增进程
使用psql test连接后,将会有一个新的进程产生,进程状态如下:
- /usr/local/pgsql/bin/postgres
- postgres: checkpointer
- postgres: background writer
- postgres: walwriter
- postgres: autovacuum launcher
- postgres: logical replication launcher
- postgres: postgres test [local] idle
代码示例
通过gdb -p 54382 master进程的调用栈如下
从调用栈中不难看出,master的会在ServerLoop函数中进行循环接收新的连接,并创建新的进程处理该连接。
代码如下:
for (;;){time_t now;nevents = WaitEventSetWait(pm_wait_set,DetermineSleepTime(),events,lengthof(events),0 /* postmaster posts no wait_events */ );/** Latch set by signal handler, or new connection pending on any of* our sockets? If the latter, fork a child process to deal with it.*/for (int i = 0; i < nevents; i++){// ...if (events[i].events & WL_SOCKET_ACCEPT){ClientSocket s;if (AcceptConnection(events[i].fd, &s) == STATUS_OK)BackendStartup(&s); // 此函数会创建新的进程并处理}}
BackenStartup函数内容如下:
static int BackendStartup(ClientSocket *client_sock)
{// ...pid = postmaster_child_launch(B_BACKEND,(char *) &startup_data, sizeof(startup_data),client_sock);// ..
忽略其中的细节,不难看出实际上调用了postmaster_child_launch函数,其主要内容如下
pid_t postmaster_child_launch(BackendType child_type,char *startup_data, size_t startup_data_len,ClientSocket *client_sock)
{pid_t pid;pid = fork_process();if (pid == 0) /* child */{child_process_kinds[child_type].main_fn(startup_data, startup_data_len);pg_unreachable(); /* main_fn never returns */}
#endif /* EXEC_BACKEND */return pid;
}
其中child_proccess_kinds定义如下:
child_process_kind child_process_kinds[] = {[B_INVALID] = {"invalid", NULL, false},[B_BACKEND] = {"backend", BackendMain, true},[B_AUTOVAC_LAUNCHER] = {"autovacuum launcher", AutoVacLauncherMain, true},[B_AUTOVAC_WORKER] = {"autovacuum worker", AutoVacWorkerMain, true},[B_BG_WORKER] = {"bgworker", BackgroundWorkerMain, true},/** WAL senders start their life as regular backend processes, and change* their type after authenticating the client for replication. We list it* here for PostmasterChildName() but cannot launch them directly.*/[B_WAL_SENDER] = {"wal sender", NULL, true},[B_SLOTSYNC_WORKER] = {"slot sync worker", ReplSlotSyncWorkerMain, true},[B_STANDALONE_BACKEND] = {"standalone backend", NULL, false},[B_ARCHIVER] = {"archiver", PgArchiverMain, true},[B_BG_WRITER] = {"bgwriter", BackgroundWriterMain, true},[B_CHECKPOINTER] = {"checkpointer", CheckpointerMain, true},[B_STARTUP] = {"startup", StartupProcessMain, true},[B_WAL_RECEIVER] = {"wal_receiver", WalReceiverMain, true},[B_WAL_SUMMARIZER] = {"wal_summarizer", WalSummarizerMain, true},[B_WAL_WRITER] = {"wal_writer", WalWriterMain, true},[B_LOGGER] = {"syslogger", SysLoggerMain, false},
};
postmaster_child_launch函数调用传入的第一个参数为B_BACKEND,所以新进程将进入BackendMain函数进行处理。从函数调用fork_process,可以看出新的进程为master的子进程。
子进程处理逻辑
新的子进程启动后,对该连接进行权限认证,如果认证通过后,将等待客户端输入sql语句。
权限认证
代码调用的逻辑为
BackendMain->PostgresMain->InitPostgres->PerformAuthentication
代码片段如下:
void BackendMain(char *startup_data, size_t startup_data_len){// ...PostgresMain(MyProcPort->database_name, MyProcPort->user_name);
}
void PostgresMain(const char *dbname, const char *username){sigjmp_buf local_sigjmp_buf;// .../* Early initialization */BaseInit();InitPostgres(dbname, InvalidOid, /* database to connect to */username, InvalidOid, /* role to connect as */(!am_walsender) ? INIT_PG_LOAD_SESSION_LIBS : 0,NULL); /* no out_dbname */// ...}void InitPostgres(const char *in_dbname, Oid dboid,const char *username, Oid useroid,bits32 flags,char *out_dbname) {bool bootstrap = IsBootstrapProcessingMode();bool am_superuser;char *fullpath;char dbname[NAMEDATALEN];int nfree = 0;elog(DEBUG3, "InitPostgres");InitProcessPhase2();SharedInvalBackendInit(false);ProcSignalInit();/** Perform client authentication if necessary, then figure out our* postgres user ID, and see if we are a superuser.** In standalone mode, autovacuum worker processes and slot sync worker* process, we use a fixed ID, otherwise we figure it out from the* authenticated user name.*/if (bootstrap || AmAutoVacuumWorkerProcess() || AmLogicalSlotSyncWorkerProcess()){...}else if (!IsUnderPostmaster){...}else if (AmBackgroundWorkerProcess()){...}else{/* normal multiuser case */Assert(MyProcPort != NULL);PerformAuthentication(MyProcPort);}...
其中PerformAuthentication为远端客户端登录进行权限认证的函数体。
等待sql输入
完成权限认证后,子进程将进入idle状态,等待sql输入。其调用栈如下:
接收sql后及其执行过程,在下一节我们在进行说明。