Postgres中postmaster代码解析(中)

今天我们对postmaster的以下细节进行讨论:

backend的启动和client的连接请求的认证 客户端取消查询时的处理 接受pg_ctl的shutdown请求进行shutdown处理 2.与前端的交互 2.1backend的启动和client的连接请求的认证

关于backend的启动,其函数调用栈如下:

PostmasterMain()   |->ServerLoop()   |->initMasks()   |->for(;;)   |->select() <--监听端口   |->ConnCreate() <--创建connection相关的数据结构   |->BackendStartup() <--建立后端进程backend process   |->PostmasterRandom()   |->canAcceptConnections()   |->fork_process()   |->InitPostmasterChild()   |->ClosePostmasterPorts()   |->BackendInitialize()   |->ProcessStartupPacket()   |->BackendRun()   |->PostgresMain() |->ConnFree() <--释放connection相关的数据结构

简单来说,在系统调用select()中我们监听客户端的连接请求,当读到一个客户端请求时我们将为其创建相关数据结构,做一下初始化。注意此时只是监听接受了请求,这个请求是否合法(例如password是否正确)在此时是不做判断的。判断是放在BackendStartup()中的。

你可能会疑惑:BackendStartup()用来创建backend,但是创建了backend之后才做验证是不是有点晚?

我们来看BackendStartup()的处理(具体的大家看上面的调用栈和source)。

typedef struct bkend { pid_t pid; /* process id of backend */ long 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;

(backend的数据结构比较重要,我先引用在这里)

首先,程序会调用PostmasterRandom()函数产生一个cancelkey。这个cancelkey是做什么的呢?它是用来标记前端发来的cancel指令的:即前端发来一个中止当前SQL文的操作的指令时(比如你按Crtl+C), postmaster会先通过backendPID先在后端的backendList中找到对应的backend,再利用这个cancelkey来和这个backend做验证(这个在下一节会提到)。

然后调用canAcceptConnections来判断当前postmaster的状态是否可以接受连接?读者又疑惑了:前面不是已经接受连接了么?前面的select只是调用select()系统调用获取了这个连接请求(甚至连连接请求也算不上,只是收到了一个Client发来的packet,可能是startup_packet,也可能是cancel_request_packet),至于是不是能接受连接,我们有两个判断:

当前数据库是不是处于可以接受连接的状态?(startup/shutdown/inconsistent recovery状态不可接受连接);

当前数据连接数是不是已经满了(超过MaxConnections)

如果不满足条件。我们把将要启动的backend标记为dead_end。就是说这个后端只是用来向前端报错用的,报错之后立即退出。所以我们就不给它分配Slot了。判断可以连接了之后,我们就给它分配好slot。

继续往下走。调用fork_process()启动一个进程(当然就是用来作为backend的了)。backend启动起来了之后,我们就可以脱离postmaster,把后面的一切交给backend自己处理了。随之而来的InitPostmasterChild()就是用来初始化backend进程,将环境句柄从postmaster切换到backend。然后调用ClosePostmasterPorts()关闭此时不需要的文件描述符。

然后调用BackendInitialize做进一步的初始化。这里我们比较感兴趣的可能就是它调用了ProcessStartupPacket()获取前端发送的StartupPacket并为之分配内存,做一些简单的判断处理。验证部分放在了后面。

最后,我们调用BackendRun()真正的运行这个backend。
从代码我们可知,BackendRun()函数也只是一个壳子,他只是切换了内存上下文到TopMemoryContext并且获取postmaster的命令行上 -o参数指定的一些参数,然后将这些参数传给了PostgresMain()函数到这里,我们看出PostgresMain()函PostmasterMain()很像。都是命令的入口。而且后面我们看到PostgresMain()函数里面也有一个Loop。就是循读取客户端发来的SQL文。

InitPostgres()函数是PostgresMain()调用的一个非常重要的初始化函数,自然它的作用是做初始化。做哪些初始化呢?

我列举一些:

InitProcessPhase2() Add my PGPROC struct to the ProcArray SharedInvalBackendInit() shared cache invalidation communication(inval) ProcSignalInit() Register the current process in the procsignal array RegisterTimeout() Register timeout RelationCacheInitialize() Initialize relationcache InitCatalogCache() Initialize catalog cache InitPlanCache() Initialize callbacks for inval EnablePortalManager() Portals are objects representing the execution state of a query, This module provides memory management services for portals InitializeClientEncoding() initialize client encoding

关于PostgresMain()其它的我不多说,和PostmasterMain()很像,只不过处理的所有对象都是针对banckend的,具体看代码吧。

到这里我们可以回答为啥要创建了backend之后才做验证:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwydsw.html