设为首页 收藏本站
查看: 926|回复: 0

[经验分享] PostgreSQL启动过程中的那些事十六:启动进程一

[复制链接]

尚未签到

发表于 2016-11-20 12:39:05 | 显示全部楼层 |阅读模式
话说pg中有个昙花一现的进程“启动进程”(“startup progress”),做了启动XLOG、验证数据库一致性、根据需要做数据库恢复和创建检查点等事情(参见《pg启动过程中的那些事十五StartDataBase梗概》),现在来讨论这个进程。
  
1
 
DSC0000.bmp

StartupDataBase调用流程略图

  
 
  
话说Main()->PostmasterMain()->StartupDatabase(),启动数据库,StartupDatabase方法其实就是StartChildProcess方法。还有启动后台写进程的方法StartBackgroundWriter、WAL日志写进程的方法StartWalWriter、WAL日志接受进程的方法StartWalreceiver都是StartChildProcess方法,只是入参不同,可能的入参见枚举类型AuxProcType。相关定义见下面。
  
#define StartupDataBase()       StartChildProcess(StartupProcess)

  
#define StartBackgroundWriter()StartChildProcess(BgWriterProcess)

  
#define StartWalWriter()    StartChildProcess(WalWriterProcess)

  
#define StartWalReceiver()      StartChildProcess(WalReceiverProcess)

  
 
  
typedef enum

  
{

  
    CheckerProcess,

  
    BootstrapProcess,

  
    StartupProcess,

  
    BgWriterProcess,

  
    WalWriterProcess,

  
    WalReceiverProcess,

  
 

  
    NUM_AUXPROCTYPES         /* Must be last! */

  
} AuxProcType;
  
       接着StartChildProcess()->postmaster_forkexec()->internal_forexec(),在StartChildProcess方法中,组织了参数“postgres –forkbootNULL [v_AuxProcType]”,[v_AuxProcType]就是枚举类型AuxProcType的变量。再调用internal_forexec(),把要传递给即将产生的新进程的参数组织到了BackendParameters结构里并写入到文件pgsql_tmp/pgsql_tmp.backend_var.[pid].[tmpFileNum]里,[pid]是当前进程ID,[tmpFileNum]是一个静态递增的无符号整型变量。然后用这个文件名字代替参数串中的NULL,现在参数成了这样“postgres –forkboot pgsql_tmp/pgsql_tmp.backend_var.[pid].[tmpFileNum][v_AuxProcType]”,然后fork一个进程——“启动进程”。BackendParameters结构的定义见下面
  
 

  
typedef struct

  
{

  
    Port       port;

  
    InheritableSocket portsocket;

  
    char       DataDir[MAXPGPATH];

  
    pgsocket   ListenSocket[MAXLISTEN];

  
    long       MyCancelKey;

  
    int        MyPMChildSlot;

  
#ifndef WIN32

  
    unsigned long UsedShmemSegID;

  
#else

  
    HANDLE     UsedShmemSegID;

  
#endif

  
    void      *UsedShmemSegAddr;

  
    slock_t    *ShmemLock;

  
    VariableCache ShmemVariableCache;

  
    Backend   *ShmemBackendArray;

  
    LWLock    *LWLockArray;

  
    slock_t    *ProcStructLock;

  
    PROC_HDR   *ProcGlobal;

  
    PGPROC    *AuxiliaryProcs;

  
    PMSignalData *PMSignalState;

  
    InheritableSocket pgStatSock;

  
    pid_t      PostmasterPid;

  
    TimestampTz PgStartTime;

  
    TimestampTz PgReloadTime;

  
    bool       redirection_done;

  
#ifdef WIN32

  
    HANDLE     PostmasterHandle;

  
    HANDLE     initial_signal_pipe;

  
    HANDLE     syslogPipe[2];

  
#else

  
    int        syslogPipe[2];

  
#endif

  
    char       my_exec_path[MAXPGPATH];

  
    char       pkglib_path[MAXPGPATH];

  
    char       ExtraOptions[MAXPGPATH];

  
} BackendParameters;
  
 
  
该结构由postmaster用于它和前端进程交互。它包含在后台进程运行前所需要的所有状态信息。这个结构由malloc分配内存并且后台进程运行时它仍然是可用的,或者由pallocTopMemoryContext中分配,因此它使PostgresMain执行。
  
 

  
typedef struct Port

  
{

  
    pgsocket   sock;         /* File descriptor */

  
    bool       noblock;      /* is the socket in non-blocking mode? */

  
    ProtocolVersion proto;      /* FE/BE protocol version */

  
    SockAddr   laddr;        /* local addr(postmaster) */

  
    SockAddr   raddr;        /* remote addr(client) */

  
    char      *remote_host/* name (or ipaddr) of remote host */

  
    char      *remote_hostname;/* name (not ipaddr) of remote host, if

  
                             *available */

  
    int        remote_hostname_resolv; /* +1 = remote_hostname is known to

  
                                    * resolve to client's IP address; -1

  
                                    * = remote_hostname is known NOT to

  
                                    * resolve to client's IP address; 0 =

  
                                    * we have not done the forward DNS

  
                                    * lookup yet */

  
    char      *remote_port/* text repof remote port */

  
    CAC_state  canAcceptConnections;    /*postmaster connection status */

  
 

  
    /* 需要启动包保存的 并传递进后台进程使用的信息。指向一个可替换的名字值对列表 */

  
    char      *database_name;

  
    char      *user_name;

  
    char      *cmdline_options;

  
    List     *guc_options;

  
 

  
    /*在认证周期期间需要持有的信息 */

  
    HbaLine    *hba;

  
    char       md5Salt[4];       /*Password salt */

  
 

  
    /*没有业务的放在该结构里的信息,但是因为elog.c要用到,database_name和其它成员也是同样的情况。 */

  
    TimestampTz SessionStartTime;      /* backendstart time */

  
 

  
    /*TCP活跃设置

  
如果AF_UNIX或者还不知道,默认值是0;如果AF_UNIX或者使用默认值,当前值是0-1作为默认值意味着本进程不能发现默认的。 */

  
    int        default_keepalives_idle;

  
    int        default_keepalives_interval;

  
    int        default_keepalives_count;

  
    int        keepalives_idle;

  
    int        keepalives_interval;

  
    int        keepalives_count;

  
 

  
#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)

  
 

  
/*如果支持GSSAPI,存储GSSAPI信息。否则,物理保证在该结构里相同的偏移量而置NULL*/

  
    pg_gssinfo *gss;

  
#else

  
    void      *gss;

  
#endif

  
 

  
    /* SSL 结构(放在最后以使其不影响其他字段位置) */

  
#ifdef USE_SSL

  
    SSL       *ssl;

  
    X509      *peer;

  
    char       peer_dn[128 + 1];

  
    char       peer_cn[SM_USER + 1];

  
    unsigned long count;

  
#endif

  
} Port;

  
 
  
2
 
 
DSC0001.png

启动进程的调用流程略图

 

  
 

  
在启动进程里Main()->SubPostmasterMain(),调用了如下方法,启动XLOG后就结束了生命。
  
1)MemoryContextInt方法,参见《PostgresQL启动过程中的那些事一》;
  
2)InitializeGUCOptions方法,参见《PostgresQL启动过程中的那些事三》;
  
3)Read_backend_variablases方法,为重组BackendParameters结构读取前面存储的文件pgsql_tmp/pgsql_tmp.backend_var.[pid].[tmpFileNum];
  
4)PGSharedMemoryReAttach方法,attach进程postmaster里的共享内存;
  
5)read_nondefault_variables方法,读非默认GUC参数,参见《PostgresQL中的那些事十一:保存非默认GUC参数到文件》;
  
6)ClosePostmasterPorts方法,关闭“启动进程”不用的文件句柄,当然,在postmaster进程里这些文件还是打开的;
  
7)InitShmemAccess方法,在初始化本进程共享内存全局变量:这些shmem头的ShmemSegHdr、shmem起始地址ShmemBase和shmem结束地址+1的ShmemBase。定义见下面。
  
static PGShmemHeader *ShmemSegHdr;           /* shared memsegment header */

  
static void *ShmemBase;                      /* start address of shared memory */

  
static void *ShmemEnd;                       /* end+1 address of shared memory */
  
8)InitAuxiliaryProcess方法,初始化一个PGPROC结构;
  
9)CreateSharedMemoryAndSemaphores方法,参见《PostgresQL中的那些事七》;
  
10)AuxiliaryProcessMain方法,辅助进程入口函数,干一堆活;
  
 
  
SubPostmasterMain的流程图见下面。根据启动进程的传入参数“postgres –forkboot NULL [v_AuxProcType]”走了"--forkboot"这个分支。还有bgwriter进程、WalWriter进程、WalReciver进程都走了这个分支,以后要讨论到相关进程,就直接从这个分支里开始了。还有AutoVacuumLauncher进程、AutoVacuumWorker进程、归档进程、统计进程以及为前端提供服务的postgres进程等在进程初始阶段,几乎没有区别的都走了1-6步,然后根据不同入参走了不同的分支,因此以后要讨论到这些进程,就直接从这些分支开始。

DSC0002.bmp
 
SubPostmasterMain的流程图

  
 
  
第8步InitAuxiliaryProcess初始化了一个每个辅助进程都有一个的PGPROC结构。每一个后台进程都在共享内存里有一个PGPROC结构,共享内存里有一个未使用的PGPROC结构链表,从其中给新的后台进程分配。参见《PostgresQL启动过程中的那些事七初始化ProcGlobal
  
当等待锁时,这个PGPROC结构被链入锁的等待进程队列。回收后的PGPROC链入ProcGlobal(见《PostgresQL启动过程中的那些事七初始化ProcGlobal》)的空闲进程列表。
  
注意,两阶段提交会为每一个当前已准备事务设置一个假的PGPROC。这些PGPROC出现在ProcArray数据结构里以使已准备事务显示其还在运行并且能正确显示其持有锁。已准备事务的PGPROC和真实进程的PGPROC的区别是已准备事务的PGPROC的pid等于0。在已准备事务的PGPROC里不使用信号和锁行为,但是它的myProcLocks[]列表是有效的。
  
struct PGPROC

  
{

  
    /* proc->links必须是结构的第一个成员*/

  
    SHM_QUEUE  links;        /* list link if process is in a list */

  
 

  
    PGSemaphoreDatasem;     /* ONE semaphore to sleep on */

  
    int        waitStatus;       /*STATUS_WAITING, STATUS_OK or STATUS_ERROR */

  
 

  
    LocalTransactionId lxid; /* local id of top-level transaction currently

  
                             *being executed by this proc, if running;

  
                             *else InvalidLocalTransactionId */

  
 

  
    TransactionId xid;          /* id of top-level transaction currently being

  
                             *executed by this proc, if running and XID

  
                              * is assigned; else InvalidTransactionId */

  
 

  
    TransactionId xmin;         /* minimal running XID as it was when we were

  
                             *starting our xact, excluding LAZY VACUUM:

  
                             *vacuum must not remove tuples deleted by

  
                             * xid>= xmin! */

  
 

  
    int        pid;          /* Backend's process ID; 0 if prepared xact*/

  
 

  
    /* 当后台进程仍在启动时这些字段是0: */

  
    BackendId  backendId;    /* This backend's backendID (if assigned) */

  
    Oid        databaseId;       /* OID of database this backendis using */

  
    Oid        roleId;           /* OID of role using this backend*/

  
 

  
    bool       inCommit;     /* true if within commit critical section */

  
 

  
    uint8      vacuumFlags/* vacuum-related flags, see above */

  
 

  
    /*当是热备模式时,显示已为当前事务发出冲突信号。尽管没有要求,当持有ProcArrayLock锁事设置/消除。如果需要,没有锁可以访问。

  
    bool       recoveryConflictPending;

  
 

  
    /* 如果有,是进程当前正在等待的轻量锁的信息 */

  
    bool       lwWaiting;    /* true if waiting for an LW lock */

  
    bool       lwExclusive/* true if waiting for exclusive access */

  
    struct PGPROC*lwWaitLink/* next waiter for same LW lock */

  
 

  
    /* 如果有,进程当前正在等待的锁的信息*/

  
    /* 如果当前没有等待的锁,waitLockwaitProcLockNULL */

  
    LOCK     *waitLock;     /* Lock object we're sleeping on ... */

  
    PROCLOCK   *waitProcLock; /* Per-holder info for awaited lock */

  
    LOCKMODE   waitLockMode; /* type of lock we're waiting for */

  
    LOCKMASK   heldLocks;    /* bitmaskfor lock types already held on this

  
                             *lock object by this backend*/

  
 

  
    Latch      procLatch;    /* generic latch for process */

  
 

  
    /*如果需要,是允许本进程等待同步复制的信息。如果没有等待的话waitLSN的值是InvalidXLogRecPtr;仅由用户后台进程设置。除非属主进程或WALSender进程可以touch syncRepState。仅当持有SyncRepLock锁时才可以用syncRepLinks

  
     */

  
    XLogRecPtr waitLSN;      /* waiting for this LSN or higher */

  
    int        syncRepState; /* wait state forsync rep*/

  
    SHM_QUEUE  syncRepLinks; /* list link if process is in syncrepqueue */

  
 

  
    /*为锁持有的所有PROCLOCK对象或者由该后台进程等待的PROCLOCK被链入这些链表中的一个,根据他们锁的分区号。

  
    所有为持有锁或者有后台进程等待的PROCLOCK对象被链到这个列表,股他们锁的发布号。

  
     */

  
    SHM_QUEUE  myProcLocks[NUM_LOCK_PARTITIONS];

  
 

  
    struct XidCache subxids; /* cache for subtransactionXIDs */

  
};

  
 

  
typedef struct SHM_QUEUE

  
{

  
    struct SHM_QUEUE *prev;

  
    struct SHM_QUEUE *next;

  
} SHM_QUEUE;
  
 
  
第10步行信息

DSC0003.bmp

AuxiliaryProcessMain的流程图

  
在AuxiliaryProcessMain方法中,设置本进程运行模式为引导模式,调用BaseInit方法初始化了一个虚拟文件描述符结构Vfd的头指针VfdCache并注册了进程退出是清理临时文件的函数,接着初始化了存储管理器,这个另行讨论,最后初始化了本地记录每个缓存信息的数组。然后根据情况ProcSignalInit为辅助进程分配ProcSignalSlot,调用InitBufferPoolBackbend方法,在其中调用on_shmem_exit注册共享内存退出清理缓存相关资源要调用的函数AtProcExit_Buffers。然后把进程设回通常模式。根据传入参数调用StartupProcessMain,设置合适的信号处理句柄,然后调用StartupXLOG方法,这个方法有约1000行,搞完退出,产生子进程退出信号,激活postmaster进程响应该信号句柄reaper,启动其它进程。
  
先到这儿把,下一篇接着讨论StartupXLOG方法。
 
------------
转载请著名出处,来自博客:
blog.csdn.net/beiigang
beigang.iyunv.com
  
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-302954-1-1.html 上篇帖子: postgreSql 关键字和自定义的字段重名 下篇帖子: postgresql在windows(包括win7)下的安装配置
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表