PostgreSQL数据库采用C/S架构,系统会为每个客户端分配一个后台服务进程,也就是postgres进程。当客户端想要访问数据库的时候,就调用接口(libpq、ODBC、JDBC)把连接请求发送给守护进程Postmaster,Postmaster就会fork出一个新的服务进程postgres为用户服务,此后客户端和服务端就不再通过Postmaster而是直接与postgres进程通信。
Postmaster总是监听用户连接请求并为用户分配服务进程postgres,而postgres则负责为客户执行各种操作。
下面聊一下Postmaster监听请求并将请求转交给postgres的整个流程(读源码参考的《PostgreSQL数据库内核分析》这本书)。
Postmaster进程:

postgres进程:

Postgres数据库启动后,Postmaster会调用ServerLoop函数来循环等待客户端的连接请求,该函数主体是个死循环,栈调用关系如下:
#0 0x00007fe62b20af53 in __select_nocancel () from lib64/libc.so.6#1 0x00000000007070a7 in ServerLoop () at postmaster.c:1670#2 0x00000000007083d9 in PostmasterMain (argc=argc@entry=3, argv=argv@entry=0x126e950) at postmaster.c:1379#3 0x00000000006740f4 in main (argc=3, argv=0x126e950) at main.c:228
循环中会首先调用select系统函数等待客户端提出的连接请求:
selres = select(nSockets, &rmask, NULL, NULL, &timeout);
当select函数正常返回后,扫描ListenSocket数组中的每个监听套接字描述符,如果发现该套接字上有用户连接请求,将调用ConnCreate函数创建一个Port结构体,之后调用accept系统函数接受由该监听套接字所维护的用户连接请求。
成功创建Port结构体后,调用BackendStartup函数,该函数会先创建出一个Backend结构体,之后通过调用fork_process生成postgres进程(fork_process会调用系统函数fork,fork被调用一次,返回两次,两次返回的区别是子进程的返回值是0,父进程的返回值则是新建子进程的进程ID,父进程会将子进程pid等postgres进程信息填充到Backend结构体,并将此结构添加到BackendList,这样Postmaster就可以通过BackendList来监控其子进程;子进程则会调用BackendInitializ来获取客户端的信息(客户端主机名,端口,用户名,数据库等)并填入Port中,之后调用BackendRun设置postgres运行时参数并进行初始化的工作,最后调用PostgresMain函数进入postgres的执行入口),自此与客户端的交互权由Postmaster转给postgres,然后关闭客户端与Postmaster的连接,释放Port结构体,检查辅助进程BgWriter、SysLogger等是否正常运行,完成后进行下一次循环。
for (i = 0; i < MAXLISTEN; i++){if (ListenSocket[i] == PGINVALID_SOCKET)break;if (FD_ISSET(ListenSocket[i], &rmask)){Port *port;port = ConnCreate(ListenSocket[i]);if (port){BackendStartup(port);/** We no longer need the open socket or port structure* in this process*/StreamClose(port->sock);ConnFree(port);}}}
父进程在fork之后的执行流程:
/** 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);
子进程在fork之后的执行流程:
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);}
服务进程postgres启动后的栈调用关系:
Thread 1 (Thread 0x7fe62c024740 (LWP 152)):#0 0x00007fe62b214463 in __epoll_wait_nocancel () from /lib64/libc.so.6#1 0x0000000000759ee9 in WaitEventSetWaitBlock (nevents=1, occurred_events=0x7ffe7f3ae770, cur_timeout=-1,set=0x129c728) at latch.c:1048#2 WaitEventSetWait (set=0x129c728, timeout=timeout@entry=-1,occurred_events=occurred_events@entry=0x7ffe7f3ae770, nevents=nevents@entry=1,wait_event_info=wait_event_info@entry=100663296) at latch.c:1000#3 0x00000000006659a8 in secure_read (port=0x12947c0, ptr=0xdb40e0 <PqRecvBuffer>, len=8192)at be-secure.c:173#4 0x000000000066f970 in pq_recvbuf () at pqcomm.c:963#5 0x00000000006703c0 in pq_getbyte () at pqcomm.c:1006#6 0x000000000077b122 in SocketBackend (inBuf=inBuf@entry=0x7ffe7f3ae910) at postgres.c:341#7 0x000000000077ca17 in ReadCommand (inBuf=inBuf@entry=0x7ffe7f3ae910) at postgres.c:514#8 0x000000000077f8cc in PostgresMain (argc=<optimized out>, argv=argv@entry=0x129c9f0,dbname=0x129c868 "postgres", username=<optimized out>) at postgres.c:4124#9 0x00000000007043ed in BackendRun (port=port@entry=0x12947c0) at postmaster.c:4361#10 0x0000000000706ec9 in BackendStartup (port=port@entry=0x12947c0) at postmaster.c:4033#11 0x000000000070715e in ServerLoop () at postmaster.c:1706#12 0x00000000007083d9 in PostmasterMain (argc=argc@entry=3, argv=argv@entry=0x126e950) at postmaster.c:1379#13 0x00000000006740f4 in main (argc=3, argv=0x126e950) at main.c:228




