SequoiaDB 系列之五 :源码分析之main函数
好久好久沒有寫博客了,因為一直要做各種事,工作上的,生活上的,這一下就是半年。
時光如梭。
這兩天回頭看了看寫的博客,感覺都是貽笑大方。
但是還是想堅持把SequoiaDB系列寫完。
?
初步的打算已經確定好,已經更新的?前言?中。
從本篇開始,進入源碼分析篇。
為了能讓自己堅持下去,也讓看我的博客學習的同學由淺入深逐步學習,我們先從簡單的開始。
如果你覺得本系列的博文讓你覺得有用,請收藏我的博客地址 :)
?
分析SequoiaDB的進程模型,免不了要從進程的Main函數開涮。
SequoiaDB源碼編譯出來之后,主要的進程就是一個,在bin目錄下的sequoiadb。
這個執行程序,能根據配置文件,化身成不用的角色,比如coord節點進程,data節點進程,以及catalog節點進程。
sequoiadb的主函數入口,代碼位于 SequoiaDB/engine/pmd/pmdMain.cpp 中,非常簡單,堪比“Hello world”:
INT32 main ( INT32 argc, CHAR** argv ) {INT32 rc = SDB_OK ;PD_TRACE_ENTRY ( SDB_PMDMAIN );rc = engine::pmdMasterThreadMain ( argc, argv ) ;PD_TRACE_EXITRC ( SDB_PMDMAIN, rc );return rc ; }main函數的函數體,其實就是執行engine::omdMasterThreadMain函數,在函數退出的時候,取得執行后的錯誤碼結束進程。
?
SequoiaDB的源碼中,C和C++混合存在。源碼中定義了不少宏,像 PD_TRACE_ENTRY,PD_TRACE_EXITRC等等,這類函數只要是檢測用的,我就不表述了,有興趣的自己去研究一下 : )
接下來我們來看pmdMasterThreadMain函數:
1 INT32 pmdMasterThreadMain ( INT32 argc, CHAR** argv ) 2 { 3 INT32 rc = SDB_OK ; 4 PD_TRACE_ENTRY ( SDB_PMDMSTTHRDMAIN ); 5 pmdKRCB *krcb = pmdGetKRCB () ; 6 UINT32 startTimerCount = 0 ; 7 8 rc = pmdResolveArguments ( argc, argv ) ; 9 if ( rc ) 10 { 11 ossPrintf( "Failed resolving arguments(error=%d), exit"OSS_NEWLINE, 12 rc ) ; 13 goto error ; 14 } 15 if ( PMD_IS_DB_DOWN ) 16 { 17 return rc ; 18 } 19 20 sdbEnablePD( pmdGetOptionCB()->getDiagLogPath(), 21 pmdGetOptionCB()->diagFileNum() ) ; 22 setPDLevel( (PDLEVEL)( pmdGetOptionCB()->getDiagLevel() ) ) ; 23 // 設置log日志級別,以免輸出不關心的日志 24 PD_LOG ( ( getPDLevel() > PDEVENT ? PDEVENT : getPDLevel() ) , 25 "Start sequoiadb(%s) [Ver: %d.%d, Release: %d, Build: %s]...", 26 pmdGetOptionCB()->krcbRole(), SDB_ENGINE_VERISON_CURRENT, 27 SDB_ENGINE_SUBVERSION_CURRENT, SDB_ENGINE_RELEASE_CURRENT, 28 SDB_ENGINE_BUILD_TIME ) ; 29 30 { 31 BSONObj confObj ; 32 krcb->getOptionCB()->toBSON( confObj ) ; 33 PD_LOG( PDEVENT, "All configs: %s", confObj.toString().c_str() ) ; 34 } 35 // 捕捉操作系統信號 36 rc = pmdEnableSignalEvent( pmdGetOptionCB()->getDiagLogPath(), 37 (PMD_ON_QUIT_FUNC)pmdOnQuit ) ; 38 PD_RC_CHECK ( rc, PDERROR, "Failed to enable trap, rc: %d", rc ) ; 39 // 根據role類型,注冊不同的功能模塊 40 sdbGetPMDController()->registerCB( pmdGetDBRole() ) ; 41 // 啟動分析 42 rc = _pmdSystemInit() ; 43 if ( rc ) 44 { 45 goto error ; 46 } 47 // 初始化各個功能模塊 48 rc = krcb->init() ; 49 if ( rc ) 50 { 51 PD_LOG( PDERROR, "Failed to init krcb, rc: %d", rc ) ; 52 goto error ; 53 } 54 55 rc = _pmdPostInit() ; 56 if ( rc ) 57 { 58 goto error ; 59 } 60 // 進入while循環,等待收到功能都完成初始化,可以提供服務的通知 61 while ( PMD_IS_DB_UP && startTimerCount < PMD_START_WAIT_TIME && 62 !krcb->isBusinessOK() ) 63 { 64 ossSleepmillis( 100 ) ; 65 startTimerCount += 100 ; 66 } 67 68 if ( PMD_IS_DB_DOWN ) 69 { 70 rc = krcb->getExitCode() ; 71 PD_LOG( PDERROR, "Start failed, rc: %d", rc ) ; 72 goto error ; 73 } 74 else if ( startTimerCount >= PMD_START_WAIT_TIME ) 75 { 76 PD_LOG( PDWARNING, "Start warning (timeout)" ) ; 77 } 78 79 #if defined (_LINUX) 80 { 81 CHAR pmdProcessName [ OSS_RENAME_PROCESS_BUFFER_LEN + 1 ] = {0} ; 82 ossSnprintf ( pmdProcessName, OSS_RENAME_PROCESS_BUFFER_LEN, 83 "%s(%s) %s", utilDBTypeStr( pmdGetDBType() ), 84 pmdGetOptionCB()->getServiceAddr(), 85 utilDBRoleShortStr( pmdGetDBRole() ) ) ; 86 ossEnableNameChanges ( argc, argv ) ; 87 ossRenameProcess ( pmdProcessName ) ; 88 } 89 #endif // _LINUX 90 { 91 EDUID agentEDU = PMD_INVALID_EDUID ; 92 pmdEDUMgr *eduMgr = pmdGetKRCB()->getEDUMgr() ; 93 eduMgr->startEDU ( EDU_TYPE_PIPESLISTENER, 94 (void*)pmdGetOptionCB()->getServiceAddr(), 95 &agentEDU ) ; 96 eduMgr->regSystemEDU ( EDU_TYPE_PIPESLISTENER, agentEDU ) ; 97 } 98 // 大while循環,如果程序沒有收到退出信號,就一直在while中;收到退出信號,PMD_IS_DB_UP所代表的變量就會變成 FALSE 99 while ( PMD_IS_DB_UP ) 100 { 101 ossSleepsecs ( 1 ) ; 102 sdbGetPMDController()->onTimer( OSS_ONE_SEC ) ; 103 } 104 rc = krcb->getExitCode() ; 105 106 done : 107 PMD_SHUTDOWN_DB( rc ) ; 108 pmdSetQuit() ; 109 krcb->destroy () ; 110 pmdGetStartup().final() ; 111 PD_LOG ( PDEVENT, "Stop sequoiadb, exit code: %d", 112 krcb->getExitCode() ) ; 113 PD_TRACE_EXITRC ( SDB_PMDMSTTHRDMAIN, rc ); 114 return utilRC2ShellRC( rc ) ; 115 error : 116 goto done ; 117 }?
看起來有點大,慢慢來。
首先,函數通過 pmdGetKRCB() 得到了一個krcb的對象指針。所謂krcb,其實全面大概就是 kernel control block了。如果你跟進它的產生里面,你會發現它是 static的,全局的靜態變量。基本上可以確定,這個是數據庫的一個核心模塊。這里我們先不管。
?
接下來,對入參進行解析,通過pmdResolveArguments函數,main函數很簡單,只是簡單地把程序的附加參數,傳給了pmdMasterThreadMain,然后在這個地方解析。
再就是sdbEnablePD,setPDLevel等,這些是和打印程序運行中的一些關鍵信息相關的,比如日志等級啊,日志文件路徑等等。
這里,我們不關心這些。
?
再下來就是用pmdEnableSignalEvent函數處理操作系統信號:當收到操作系統發給進程信號的時候,catch到信號事件,然后對針對信號做一些處理的。一個好的服務端程序,是應該能catch到信號事件,然后對信號做一些處理的。例如,程序跑著跑著,收到一個SIGPIPE信號,如果沒有捕捉到信號,程序就退出了。沒有留下任何幫助信息。這個時候,如果能捕捉到這個信號,抓取進程的棧數據,放倒一個文件里面,就可以根據這些信息,去定位程序出問題的地方。很多程序的dump收集,就是基于這個原理的。感興趣的可以跟進?pmdEnableSignalEvent函數,看看它怎么捕捉信號事件,怎么處理的。
?
接下來就到了重點了:
sdbGetPMDController()->registerCB( pmdGetDBRole() ) ;
_pmdSystemInit() ;
krcb->init() ;
這幾個函數,主要是根據當前進程的角色,用來初始化已經注冊的引擎模塊。
?
先看注冊:
1 void _pmdController::registerCB( SDB_ROLE dbrole ) 2 { 3 // 根據不同的數據節點角色,注冊對應的模塊 4 // DPS 數據保護服務模塊,這塊的核心是記錄寫操作的日志,方便同步,如果你對mongodb熟悉的話,有個oplog,功能和它類似,它是一致性的保證之一 5 // TRANS 事務功能模塊 6 // CLS 集群管理服務模塊,管理集群中的節點 7 // BPS 8 // FMP 外部消息協議模塊 9 // CATALOGUE 編目信息服務模塊 10 // AUTH 鑒權模塊 11 // DMS 數據管理服務模塊,這是數據庫存儲的核心 12 // SQL sql語言支持模塊 13 // RTN 平臺運行庫,主要是跨平臺的api封裝 14 // OMSVC OM服務,支持rest等協議支持模塊 15 // AGGR 數據聚集服務模塊 16 if ( SDB_ROLE_DATA == dbrole ) 17 { 18 PMD_REGISTER_CB( sdbGetDPSCB() ) ; // DPS 19 PMD_REGISTER_CB( sdbGetTransCB() ) ; // TRANS 20 PMD_REGISTER_CB( sdbGetClsCB() ) ; // CLS 21 PMD_REGISTER_CB( sdbGetBPSCB() ) ; // BPS 22 } 23 else if ( SDB_ROLE_COORD == dbrole ) 24 { 25 PMD_REGISTER_CB( sdbGetTransCB() ) ; // TRANS 26 PMD_REGISTER_CB( sdbGetCoordCB() ) ; // COORD 27 PMD_REGISTER_CB( sdbGetFMPCB () ) ; // FMP 28 } 29 else if ( SDB_ROLE_CATALOG == dbrole ) 30 { 31 PMD_REGISTER_CB( sdbGetDPSCB() ) ; // DPS 32 PMD_REGISTER_CB( sdbGetTransCB() ) ; // TRANS 33 PMD_REGISTER_CB( sdbGetClsCB() ) ; // CLS 34 PMD_REGISTER_CB( sdbGetCatalogueCB() ) ; // CATALOGUE 35 PMD_REGISTER_CB( sdbGetBPSCB() ) ; // BPS 36 PMD_REGISTER_CB( sdbGetAuthCB() ) ; // AUTH 37 } 38 else if ( SDB_ROLE_STANDALONE == dbrole ) 39 { 40 PMD_REGISTER_CB( sdbGetDPSCB() ) ; // DPS 41 PMD_REGISTER_CB( sdbGetTransCB() ) ; // TRANS 42 PMD_REGISTER_CB( sdbGetBPSCB() ) ; // BPS 43 } 44 else if ( SDB_ROLE_OM == dbrole ) 45 { 46 PMD_REGISTER_CB( sdbGetDPSCB() ) ; // DPS 47 PMD_REGISTER_CB( sdbGetTransCB() ) ; // TRANS 48 PMD_REGISTER_CB( sdbGetBPSCB() ) ; // BPS 49 PMD_REGISTER_CB( sdbGetAuthCB() ) ; // AUTH 50 PMD_REGISTER_CB( sdbGetOMManager() ) ; // OMSVC 51 } 52 PMD_REGISTER_CB( sdbGetDMSCB() ) ; // DMS 53 PMD_REGISTER_CB( sdbGetRTNCB() ) ; // RTN 54 PMD_REGISTER_CB( sdbGetSQLCB() ) ; // SQL 55 PMD_REGISTER_CB( sdbGetAggrCB() ) ; // AGGR 56 PMD_REGISTER_CB( sdbGetPMDController() ) ; // CONTROLLER 57 }以上表明,數據庫節點的角色,分為data,calalog,coord,om,和standalone等。不同的角色,會注冊(加載)不同的功能模塊。
?
再看_pmdSystemInit函數,這個函數會初始化系統模塊:
1 static INT32 _pmdSystemInit() 2 { 3 INT32 rc = SDB_OK ; 4 5 rc = pmdGetStartup().init( pmdGetOptionCB()->getDbPath() ) ; 6 PD_RC_CHECK( rc, PDERROR, "Start up check failed[rc:%d]", rc ) ; 7 8 rc = getQgmStrategyTable()->init() ; 9 PD_RC_CHECK( rc, PDERROR, "Init qgm strategy table failed, rc: %d", 10 rc ) ; 11 12 done: 13 return rc ; 14 error: 15 goto done ; 16 }這是函數從指定的路徑中讀取啟動文件并初始化,然后初始化SQL相關的策略。啟動文件是一個隱藏文件,當數據庫正常或者異常退出時候,會記錄數據庫的狀態。如果是從異常退出,則在再次啟動的時候,會重新找主節點做全量同步,是自身數據和主節點一致。至于SQL相關的策略,我沒有細看,應該是SQL語法樹相關。
?
在完成啟動分析之后,接下來就開始初始化前面注冊的功能模塊了,krcb->init()?
1 INT32 _SDB_KRCB::init () 2 { 3 INT32 rc = SDB_OK ; 4 INT32 index = 0 ; 5 IControlBlock *pCB = NULL ; 6 7 _mainEDU.setName( "Main" ) ; 8 if ( NULL == pmdGetThreadEDUCB() ) 9 { 10 pmdDeclareEDUCB( &_mainEDU ) ; 11 } 12 13 rc = ossGetHostName( _hostName, OSS_MAX_HOSTNAME ) ; 14 PD_RC_CHECK( rc, PDERROR, "Failed to get host name, rc: %d", rc ) ; 15 16 _init = TRUE ; 17 // 一次初始化已經注冊的功能模塊 18 for ( index = 0 ; index < SDB_CB_MAX ; ++index ) 19 { 20 pCB = _arrayCBs[ index ] ; 21 if ( !pCB ) 22 { 23 continue ; 24 } 25 if ( SDB_OK != ( rc = pCB->init() ) ) 26 { 27 PD_LOG( PDERROR, "Init cb[Type: %d, Name: %s] failed, rc: %d", 28 pCB->cbType(), pCB->cbName(), rc ) ; 29 goto error ; 30 } 31 } 32 // 依次激活已經初始化的功能模塊 33 for ( index = 0 ; index < SDB_CB_MAX ; ++index ) 34 { 35 pCB = _arrayCBs[ index ] ; 36 if ( !pCB ) 37 { 38 continue ; 39 } 40 if ( SDB_OK != ( rc = pCB->active() ) ) 41 { 42 PD_LOG( PDERROR, "Active cb[Type: %d, Name: %s] failed, rc: %d", 43 pCB->cbType(), pCB->cbName(), rc ) ; 44 goto error ; 45 } 46 } 47 48 _isActive = TRUE ; 49 // 時間采樣,不表 50 _curTime.sample() ; 51 52 done: 53 return rc ; 54 error: 55 goto done ; 56 }前面有提到 krcb是一個全局的變量,是整個數據庫的核心。 SequoiaDB中的各個模塊,都繼承自同一個控制接口 IControlBlock,由虛函數來實現多態,并且交給KRCB模塊集中管理,體現了軟件開發中“誰產生,誰管理”的思想。
1 class _IControlBlock : public SDBObject, public _ISDBRoot 2 { 3 public: 4 _IControlBlock () {} 5 virtual ~_IControlBlock () {} 6 7 virtual SDB_CB_TYPE cbType() const = 0 ; 8 virtual const CHAR* cbName() const = 0 ; 9 10 virtual INT32 init () = 0 ; 11 virtual INT32 active () = 0 ; 12 virtual INT32 deactive () = 0 ; 13 virtual INT32 fini () = 0 ; 14 virtual void onConfigChange() {} 15 16 } ; 17 typedef _IControlBlock IControlBlock ;函數中通過for循環,初始化各個模塊,然后并激活各個模塊。如此,數據庫就開始真正的提供服務了,_isActive = TRUE很坦白地說明了這一點。
?
至于最后的_pmdPostInit() 函數,就是給初始化工作做一些掃尾工作,具體是把異常啟動的standalone模式的節點或提供om服務節點的節點狀態標記為正常。具體內容就不再貼代碼了。
?
然后,程序就開始做一些啟動后的掃尾工作:重命名進程,以便ps命令看到整齊的進程角色和服務端口;創建PIPE監聽的服務,只是用于檢測數據庫狀態,(載體是EDU)。
然后主線程進入了while循環,直到收到退出信號:
1 while ( PMD_IS_DB_UP ) 2 { 3 ossSleepsecs ( 1 ) ; 4 sdbGetPMDController()->onTimer( OSS_ONE_SEC ) ; 5 }?
至此,我們的main函數分析到一段落。
?
從整個main函數的分析,可以看出SequoiaDB中的功能劃分很清楚。一個KRCB控制塊,管理其他功能模塊(也是控制塊);其他模塊,管理自身下的功能。以后會分析到幾個主要模塊的功能,如DPS,DMS等。
這和我認識的軟件開發思想大致相同:一個軟件產品,從粗粒度來看,無非就是功能模塊的組裝,但是要合理地去協調各個模塊的關系,使之井然有序穩定地工作,這就關系到技術細節上了。
?
感謝您能耐心看到這里。
下一篇開始分析SequoiaDB的插入以及相關源碼。
?
=====>THE END<=====?
轉載于:https://www.cnblogs.com/tynia/p/mainEntry.html
總結
以上是生活随笔為你收集整理的SequoiaDB 系列之五 :源码分析之main函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 循环链表合并
- 下一篇: 【技术分享】CSS 实现渐变色背景